GLK: FROTZ: Change Quetzal saving to use new base Quetzal writer
This commit is contained in:
parent
ec8409c115
commit
c405203cde
4 changed files with 121 additions and 148 deletions
|
@ -39,178 +39,139 @@ enum ParseState {
|
||||||
GOT_ERROR = 0x80
|
GOT_ERROR = 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define WRITE_RUN(RUN) ws.writeByte(0); ws.writeByte((byte)(RUN))
|
||||||
bool Quetzal::read_word(Common::ReadStream *f, zword *result) {
|
|
||||||
*result = f->readUint16BE();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Quetzal::read_long(Common::ReadStream *f, uint *result) {
|
|
||||||
*result = f->readUint32BE();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::String &desc) {
|
bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::String &desc) {
|
||||||
Processor &p = *proc;
|
Processor &p = *proc;
|
||||||
uint ifzslen = 0, cmemlen = 0, stkslen = 0, descLen = 0;
|
|
||||||
offset_t pc;
|
offset_t pc;
|
||||||
zword i, j, n;
|
zword i, j, n;
|
||||||
zword nvars, nargs, nstk;
|
zword nvars, nargs, nstk;
|
||||||
zbyte var;
|
zbyte var;
|
||||||
long cmempos, stkspos;
|
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
// Set a temporary memory stream for writing out the data. This is needed, since we need to
|
// Reset Quetzal writer
|
||||||
// do some seeking within it at the end to fill out totals before properly writing it all out
|
_writer.clear();
|
||||||
Common::MemoryWriteStreamDynamic saveData(DisposeAfterUse::YES);
|
|
||||||
_out = &saveData;
|
|
||||||
|
|
||||||
// Write `IFZS' header.
|
|
||||||
write_chnk(ID_FORM, 0);
|
|
||||||
write_long(ID_IFZS);
|
|
||||||
|
|
||||||
// Write `IFhd' chunk
|
// Write `IFhd' chunk
|
||||||
pc = p.getPC();
|
{
|
||||||
write_chnk(ID_IFhd, 13);
|
Common::WriteStream &ws = _writer.add(ID_IFhd);
|
||||||
write_word(p.h_release);
|
pc = p.getPC();
|
||||||
for (i = H_SERIAL; i<H_SERIAL + 6; ++i)
|
ws.writeUint16BE(p.h_release);
|
||||||
write_byte(p[i]);
|
ws.write(&p[H_SERIAL], 6);
|
||||||
|
ws.writeUint16BE(p.h_checksum);
|
||||||
|
|
||||||
write_word(p.h_checksum);
|
ws.writeByte((pc >> 16) & 0xff);
|
||||||
write_long(pc << 8); // Includes pad
|
ws.writeByte((pc >> 8) & 0xff);
|
||||||
|
ws.writeByte(pc & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
// Write 'ANNO' chunk
|
// Write 'ANNO' chunk
|
||||||
descLen = desc.size() + 1;
|
{
|
||||||
write_chnk(ID_ANNO, descLen);
|
Common::WriteStream &ws = _writer.add(ID_ANNO);
|
||||||
saveData.write(desc.c_str(), desc.size());
|
ws.write(desc.c_str(), desc.size());
|
||||||
write_byte(0);
|
ws.writeByte(0);
|
||||||
if ((desc.size() % 2) == 0) {
|
|
||||||
write_byte(0);
|
|
||||||
++descLen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write `CMem' chunk.
|
// Write `CMem' chunk.
|
||||||
cmempos = saveData.pos();
|
{
|
||||||
write_chnk(ID_CMem, 0);
|
Common::WriteStream &ws = _writer.add(ID_CMem);
|
||||||
_storyFile->seek(0);
|
_storyFile->seek(0);
|
||||||
|
|
||||||
// j holds current run length.
|
// j holds current run length.
|
||||||
for (i = 0, j = 0, cmemlen = 0; i < p.h_dynamic_size; ++i) {
|
for (i = 0, j = 0; i < p.h_dynamic_size; ++i) {
|
||||||
c = _storyFile->readByte();
|
c = _storyFile->readByte();
|
||||||
c ^= p[i];
|
c ^= p[i];
|
||||||
|
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
// It's a run of equal bytes
|
// It's a run of equal bytes
|
||||||
++j;
|
++j;
|
||||||
} else {
|
|
||||||
// Write out any run there may be.
|
|
||||||
if (j > 0) {
|
|
||||||
for (; j > 0x100; j -= 0x100) {
|
|
||||||
write_run(0xFF);
|
|
||||||
cmemlen += 2;
|
|
||||||
}
|
|
||||||
write_run(j - 1);
|
|
||||||
cmemlen += 2;
|
|
||||||
j = 0;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Write out any run there may be.
|
||||||
|
if (j > 0) {
|
||||||
|
for (; j > 0x100; j -= 0x100) {
|
||||||
|
WRITE_RUN(0xFF);
|
||||||
|
}
|
||||||
|
WRITE_RUN(j - 1);
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Any runs are now written. Write this (nonzero) byte
|
// Any runs are now written. Write this (nonzero) byte
|
||||||
write_byte((zbyte)c);
|
ws.writeByte(c);
|
||||||
++cmemlen;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reached end of dynamic memory. We ignore any unwritten run there may be at this point.
|
|
||||||
if (cmemlen & 1)
|
|
||||||
// Chunk length must be even.
|
|
||||||
write_byte(0);
|
|
||||||
|
|
||||||
// Write `Stks' chunk. You are not expected to understand this. ;)
|
// Write `Stks' chunk. You are not expected to understand this. ;)
|
||||||
stkspos = saveData.pos();
|
{
|
||||||
write_chnk(ID_Stks, 0);
|
Common::WriteStream &ws = _writer.add(ID_Stks);
|
||||||
|
|
||||||
// We construct a list of frame indices, most recent first, in `frames'.
|
// We construct a list of frame indices, most recent first, in `frames'.
|
||||||
// These indices are the offsets into the `stack' array of the word before
|
// These indices are the offsets into the `stack' array of the word before
|
||||||
// the first word pushed in each frame.
|
// the first word pushed in each frame.
|
||||||
frames[0] = p._sp - p._stack; // The frame we'd get by doing a call now.
|
frames[0] = p._sp - p._stack; // The frame we'd get by doing a call now.
|
||||||
for (i = p._fp - p._stack + 4, n = 0; i < STACK_SIZE + 4; i = p._stack[i - 3] + 5)
|
for (i = p._fp - p._stack + 4, n = 0; i < STACK_SIZE + 4; i = p._stack[i - 3] + 5)
|
||||||
frames[++n] = i;
|
frames[++n] = i;
|
||||||
|
|
||||||
// All versions other than V6 can use evaluation stack outside a function
|
// All versions other than V6 can use evaluation stack outside a function
|
||||||
// context. We write a faked stack frame (most fields zero) to cater for this.
|
// context. We write a faked stack frame (most fields zero) to cater for this.
|
||||||
if (p.h_version != V6) {
|
if (p.h_version != V6) {
|
||||||
for (i = 0; i < 6; ++i)
|
for (i = 0; i < 6; ++i)
|
||||||
write_byte(0);
|
ws.writeByte(0);
|
||||||
nstk = STACK_SIZE - frames[n];
|
nstk = STACK_SIZE - frames[n];
|
||||||
write_word(nstk);
|
ws.writeUint16BE(nstk);
|
||||||
for (j = STACK_SIZE - 1; j >= frames[n]; --j)
|
for (j = STACK_SIZE - 1; j >= frames[n]; --j)
|
||||||
write_word(p._stack[j]);
|
ws.writeUint16BE(p._stack[j]);
|
||||||
stkslen = 8 + 2 * nstk;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out the rest of the stack frames.
|
|
||||||
for (i = n; i > 0; --i) {
|
|
||||||
zword *pf = p._stack + frames[i] - 4; // Points to call frame
|
|
||||||
nvars = (pf[0] & 0x0F00) >> 8;
|
|
||||||
nargs = pf[0] & 0x00FF;
|
|
||||||
nstk = frames[i] - frames[i - 1] - nvars - 4;
|
|
||||||
pc = ((uint)pf[3] << 9) | pf[2];
|
|
||||||
|
|
||||||
// Check type of call
|
|
||||||
switch (pf[0] & 0xF000) {
|
|
||||||
case 0x0000:
|
|
||||||
// Function
|
|
||||||
var = p[pc];
|
|
||||||
pc = ((pc + 1) << 8) | nvars;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1000:
|
|
||||||
// Procedure
|
|
||||||
var = 0;
|
|
||||||
pc = (pc << 8) | 0x10 | nvars; // Set procedure flag
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
p.runtimeError(ERR_SAVE_IN_INTER);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if (nargs != 0)
|
|
||||||
nargs = (1 << nargs) - 1; // Make args into bitmap
|
|
||||||
|
|
||||||
// Write the main part of the frame...
|
// Write out the rest of the stack frames.
|
||||||
write_long(pc);
|
for (i = n; i > 0; --i) {
|
||||||
write_byte(var);
|
zword *pf = p._stack + frames[i] - 4; // Points to call frame
|
||||||
write_byte(nargs);
|
nvars = (pf[0] & 0x0F00) >> 8;
|
||||||
write_word(nstk);
|
nargs = pf[0] & 0x00FF;
|
||||||
|
nstk = frames[i] - frames[i - 1] - nvars - 4;
|
||||||
|
pc = ((uint)pf[3] << 9) | pf[2];
|
||||||
|
|
||||||
// Write the variables and eval stack
|
// Check type of call
|
||||||
for (j = 0, --pf; j<nvars + nstk; ++j, --pf)
|
switch (pf[0] & 0xF000) {
|
||||||
write_word(*pf);
|
case 0x0000:
|
||||||
|
// Function
|
||||||
|
var = p[pc];
|
||||||
|
pc = ((pc + 1) << 8) | nvars;
|
||||||
|
break;
|
||||||
|
|
||||||
// Calculate length written thus far
|
case 0x1000:
|
||||||
stkslen += 8 + 2 * (nvars + nstk);
|
// Procedure
|
||||||
|
var = 0;
|
||||||
|
pc = (pc << 8) | 0x10 | nvars; // Set procedure flag
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
p.runtimeError(ERR_SAVE_IN_INTER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nargs != 0)
|
||||||
|
nargs = (1 << nargs) - 1; // Make args into bitmap
|
||||||
|
|
||||||
|
// Write the main part of the frame...
|
||||||
|
ws.writeUint32BE(pc);
|
||||||
|
ws.writeByte(var);
|
||||||
|
ws.writeByte(nargs);
|
||||||
|
ws.writeUint16BE(nstk);
|
||||||
|
|
||||||
|
// Write the variables and eval stack
|
||||||
|
for (j = 0, --pf; j<nvars + nstk; ++j, --pf)
|
||||||
|
ws.writeUint16BE(*pf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in variable chunk lengths
|
|
||||||
ifzslen = 4 * 8 + 4 + 14 + cmemlen + stkslen + descLen;
|
|
||||||
if (cmemlen & 1)
|
|
||||||
++ifzslen;
|
|
||||||
|
|
||||||
saveData.seek(4);
|
|
||||||
saveData.writeUint32BE(ifzslen);
|
|
||||||
saveData.seek(cmempos + 4);
|
|
||||||
saveData.writeUint32BE(cmemlen);
|
|
||||||
saveData.seek(stkspos + 4);
|
|
||||||
saveData.writeUint32BE(stkslen);
|
|
||||||
|
|
||||||
// Write the save data out
|
// Write the save data out
|
||||||
svf->write(saveData.getData(), saveData.size());
|
_writer.save(svf, ID_IFZS);
|
||||||
|
|
||||||
// After all that, still nothing went wrong!
|
// After all that, still nothing went wrong!
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
|
int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
|
||||||
Processor &p = *proc;
|
Processor &p = *proc;
|
||||||
uint ifzslen, currlen, tmpl;
|
uint ifzslen, currlen, tmpl;
|
||||||
|
|
|
@ -35,19 +35,11 @@ class Processor;
|
||||||
class Quetzal {
|
class Quetzal {
|
||||||
private:
|
private:
|
||||||
Common::SeekableReadStream *_storyFile;
|
Common::SeekableReadStream *_storyFile;
|
||||||
Common::WriteStream *_out;
|
QuetzalReader _reader;
|
||||||
|
QuetzalWriter _writer;
|
||||||
zword frames[STACK_SIZE / 4 + 1];
|
zword frames[STACK_SIZE / 4 + 1];
|
||||||
|
/*
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* Read a 16-bit value from the file
|
|
||||||
*/
|
|
||||||
bool read_word(Common::ReadStream *f, zword *result);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read 32-bit value from the file
|
|
||||||
*/
|
|
||||||
bool read_long(Common::ReadStream *f, uint *result);
|
|
||||||
|
|
||||||
void write_byte(zbyte b) { _out->writeByte(b); }
|
void write_byte(zbyte b) { _out->writeByte(b); }
|
||||||
void write_bytx(zword b) { _out->writeByte(b & 0xFF); }
|
void write_bytx(zword b) { _out->writeByte(b & 0xFF); }
|
||||||
void write_word(zword w) { _out->writeUint16BE(w); }
|
void write_word(zword w) { _out->writeUint16BE(w); }
|
||||||
|
@ -57,6 +49,7 @@ private:
|
||||||
_out->writeUint32BE(id);
|
_out->writeUint32BE(id);
|
||||||
_out->writeUint32BE(len);
|
_out->writeUint32BE(len);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
|
|
@ -25,6 +25,11 @@
|
||||||
|
|
||||||
namespace Glk {
|
namespace Glk {
|
||||||
|
|
||||||
|
void QuetzalReader::clear() {
|
||||||
|
_chunks.clear();
|
||||||
|
_stream = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) {
|
bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) {
|
||||||
_chunks.clear();
|
_chunks.clear();
|
||||||
stream->seek(0);
|
stream->seek(0);
|
||||||
|
@ -74,7 +79,7 @@ Common::WriteStream &QuetzalWriter::add(uint32 chunkId) {
|
||||||
error("Duplicate chunk added");
|
error("Duplicate chunk added");
|
||||||
}
|
}
|
||||||
|
|
||||||
_chunks.push_back(Chunk());
|
_chunks.push_back(Chunk(chunkId));
|
||||||
return _chunks.back()._stream;
|
return _chunks.back()._stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +87,7 @@ void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) {
|
||||||
// Calculate the size of the chunks
|
// Calculate the size of the chunks
|
||||||
uint size = 4;
|
uint size = 4;
|
||||||
for (uint idx = 0; idx < _chunks.size(); ++idx)
|
for (uint idx = 0; idx < _chunks.size(); ++idx)
|
||||||
size += _chunks[idx]._stream.size();
|
size += 8 + _chunks[idx]._stream.size() + (_chunks[idx]._stream.size() & 1);
|
||||||
|
|
||||||
// Write out the header
|
// Write out the header
|
||||||
out->writeUint32BE(ID_FORM);
|
out->writeUint32BE(ID_FORM);
|
||||||
|
@ -92,10 +97,9 @@ void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) {
|
||||||
// Loop through writing the chunks
|
// Loop through writing the chunks
|
||||||
for (uint idx = 0; idx < _chunks.size(); ++idx) {
|
for (uint idx = 0; idx < _chunks.size(); ++idx) {
|
||||||
Common::MemoryWriteStreamDynamic &s = _chunks[idx]._stream;
|
Common::MemoryWriteStreamDynamic &s = _chunks[idx]._stream;
|
||||||
uint chunkSize = s.size() + (s.size() & 1);
|
|
||||||
|
|
||||||
out->writeUint32BE(_chunks[idx]._id);
|
out->writeUint32BE(_chunks[idx]._id);
|
||||||
out->writeUint32BE(chunkSize);
|
out->writeUint32BE(s.size());
|
||||||
out->write(s.getData(), s.size());
|
out->write(s.getData(), s.size());
|
||||||
if (s.size() & 1)
|
if (s.size() & 1)
|
||||||
out->writeByte(0);
|
out->writeByte(0);
|
||||||
|
|
|
@ -93,6 +93,11 @@ public:
|
||||||
*/
|
*/
|
||||||
QuetzalReader() : _stream(nullptr) {}
|
QuetzalReader() : _stream(nullptr) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a Quetzal file for access
|
* Opens a Quetzal file for access
|
||||||
*/
|
*/
|
||||||
|
@ -124,10 +129,20 @@ class QuetzalWriter {
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
Chunk() : _id(0), _stream(DisposeAfterUse::YES) {}
|
Chunk() : _id(0), _stream(DisposeAfterUse::YES) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Chunk(uint32 id) : _id(id), _stream(DisposeAfterUse::YES) {}
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
Common::Array<Chunk> _chunks;
|
Common::Array<Chunk> _chunks;
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Clear
|
||||||
|
*/
|
||||||
|
void clear() { _chunks.clear(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a chunk
|
* Add a chunk
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue