GLK: FROTZ: Change Quetzal saving to use new base Quetzal writer

This commit is contained in:
Paul Gilbert 2019-06-15 21:50:01 -07:00
parent ec8409c115
commit c405203cde
4 changed files with 121 additions and 148 deletions

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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
*/ */