diff options
-rw-r--r-- | engines/glk/frotz/quetzal.cpp | 237 | ||||
-rw-r--r-- | engines/glk/frotz/quetzal.h | 15 | ||||
-rw-r--r-- | engines/glk/quetzal.cpp | 12 | ||||
-rw-r--r-- | engines/glk/quetzal.h | 15 |
4 files changed, 126 insertions, 153 deletions
diff --git a/engines/glk/frotz/quetzal.cpp b/engines/glk/frotz/quetzal.cpp index e025b2bcc7..0ae6d2f239 100644 --- a/engines/glk/frotz/quetzal.cpp +++ b/engines/glk/frotz/quetzal.cpp @@ -39,178 +39,139 @@ enum ParseState { GOT_ERROR = 0x80 }; - -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; -} +#define WRITE_RUN(RUN) ws.writeByte(0); ws.writeByte((byte)(RUN)) bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::String &desc) { Processor &p = *proc; - uint ifzslen = 0, cmemlen = 0, stkslen = 0, descLen = 0; offset_t pc; zword i, j, n; zword nvars, nargs, nstk; zbyte var; - long cmempos, stkspos; int c; - // Set a temporary memory stream for writing out the data. This is needed, since we need to - // do some seeking within it at the end to fill out totals before properly writing it all out - Common::MemoryWriteStreamDynamic saveData(DisposeAfterUse::YES); - _out = &saveData; - - // Write `IFZS' header. - write_chnk(ID_FORM, 0); - write_long(ID_IFZS); + // Reset Quetzal writer + _writer.clear(); // Write `IFhd' chunk - pc = p.getPC(); - write_chnk(ID_IFhd, 13); - write_word(p.h_release); - for (i = H_SERIAL; i<H_SERIAL + 6; ++i) - write_byte(p[i]); - - write_word(p.h_checksum); - write_long(pc << 8); // Includes pad + { + Common::WriteStream &ws = _writer.add(ID_IFhd); + pc = p.getPC(); + ws.writeUint16BE(p.h_release); + ws.write(&p[H_SERIAL], 6); + ws.writeUint16BE(p.h_checksum); + + ws.writeByte((pc >> 16) & 0xff); + ws.writeByte((pc >> 8) & 0xff); + ws.writeByte(pc & 0xff); + } // Write 'ANNO' chunk - descLen = desc.size() + 1; - write_chnk(ID_ANNO, descLen); - saveData.write(desc.c_str(), desc.size()); - write_byte(0); - if ((desc.size() % 2) == 0) { - write_byte(0); - ++descLen; + { + Common::WriteStream &ws = _writer.add(ID_ANNO); + ws.write(desc.c_str(), desc.size()); + ws.writeByte(0); } // Write `CMem' chunk. - cmempos = saveData.pos(); - write_chnk(ID_CMem, 0); - _storyFile->seek(0); - - // j holds current run length. - for (i = 0, j = 0, cmemlen = 0; i < p.h_dynamic_size; ++i) { - c = _storyFile->readByte(); - c ^= p[i]; - - if (c == 0) { - // It's a run of equal bytes - ++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; + { + Common::WriteStream &ws = _writer.add(ID_CMem); + _storyFile->seek(0); + + // j holds current run length. + for (i = 0, j = 0; i < p.h_dynamic_size; ++i) { + c = _storyFile->readByte(); + c ^= p[i]; + + if (c == 0) { + // It's a run of equal bytes + ++j; } + 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 - write_byte((zbyte)c); - ++cmemlen; + // Any runs are now written. Write this (nonzero) byte + ws.writeByte(c); + } } } - // 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. ;) - stkspos = saveData.pos(); - write_chnk(ID_Stks, 0); - - // 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 - // the first word pushed in each frame. - 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) - frames[++n] = i; - - // 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. - if (p.h_version != V6) { - for (i = 0; i < 6; ++i) - write_byte(0); - nstk = STACK_SIZE - frames[n]; - write_word(nstk); - for (j = STACK_SIZE - 1; j >= frames[n]; --j) - write_word(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; + { + Common::WriteStream &ws = _writer.add(ID_Stks); + + // 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 + // the first word pushed in each frame. + 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) + frames[++n] = i; + + // 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. + if (p.h_version != V6) { + for (i = 0; i < 6; ++i) + ws.writeByte(0); + nstk = STACK_SIZE - frames[n]; + ws.writeUint16BE(nstk); + for (j = STACK_SIZE - 1; j >= frames[n]; --j) + ws.writeUint16BE(p._stack[j]); } - if (nargs != 0) - nargs = (1 << nargs) - 1; // Make args into bitmap - // Write the main part of the frame... - write_long(pc); - write_byte(var); - write_byte(nargs); - write_word(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; - // Write the variables and eval stack - for (j = 0, --pf; j<nvars + nstk; ++j, --pf) - write_word(*pf); + case 0x1000: + // Procedure + var = 0; + pc = (pc << 8) | 0x10 | nvars; // Set procedure flag + break; - // Calculate length written thus far - stkslen += 8 + 2 * (nvars + nstk); + 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 - svf->write(saveData.getData(), saveData.size()); + _writer.save(svf, ID_IFZS); // After all that, still nothing went wrong! return true; } - int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) { Processor &p = *proc; uint ifzslen, currlen, tmpl; diff --git a/engines/glk/frotz/quetzal.h b/engines/glk/frotz/quetzal.h index 10effff25c..ce6b1bdcb2 100644 --- a/engines/glk/frotz/quetzal.h +++ b/engines/glk/frotz/quetzal.h @@ -35,19 +35,11 @@ class Processor; class Quetzal { private: Common::SeekableReadStream *_storyFile; - Common::WriteStream *_out; + QuetzalReader _reader; + QuetzalWriter _writer; zword frames[STACK_SIZE / 4 + 1]; +/* 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_bytx(zword b) { _out->writeByte(b & 0xFF); } void write_word(zword w) { _out->writeUint16BE(w); } @@ -57,6 +49,7 @@ private: _out->writeUint32BE(id); _out->writeUint32BE(len); } + */ public: /** * Constructor diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp index 7a53e598ee..4a3ea8b88e 100644 --- a/engines/glk/quetzal.cpp +++ b/engines/glk/quetzal.cpp @@ -25,6 +25,11 @@ namespace Glk { +void QuetzalReader::clear() { + _chunks.clear(); + _stream = nullptr; +} + bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) { _chunks.clear(); stream->seek(0); @@ -74,7 +79,7 @@ Common::WriteStream &QuetzalWriter::add(uint32 chunkId) { error("Duplicate chunk added"); } - _chunks.push_back(Chunk()); + _chunks.push_back(Chunk(chunkId)); return _chunks.back()._stream; } @@ -82,7 +87,7 @@ void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) { // Calculate the size of the chunks uint size = 4; 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 out->writeUint32BE(ID_FORM); @@ -92,10 +97,9 @@ void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) { // Loop through writing the chunks for (uint idx = 0; idx < _chunks.size(); ++idx) { Common::MemoryWriteStreamDynamic &s = _chunks[idx]._stream; - uint chunkSize = s.size() + (s.size() & 1); out->writeUint32BE(_chunks[idx]._id); - out->writeUint32BE(chunkSize); + out->writeUint32BE(s.size()); out->write(s.getData(), s.size()); if (s.size() & 1) out->writeByte(0); diff --git a/engines/glk/quetzal.h b/engines/glk/quetzal.h index 2badf35bca..706fb9fe43 100644 --- a/engines/glk/quetzal.h +++ b/engines/glk/quetzal.h @@ -94,6 +94,11 @@ public: QuetzalReader() : _stream(nullptr) {} /** + * Clear + */ + void clear(); + + /** * Opens a Quetzal file for access */ bool open(Common::SeekableReadStream *stream, uint32 formType = ID_IFSF); @@ -124,11 +129,21 @@ class QuetzalWriter { * Constructor */ Chunk() : _id(0), _stream(DisposeAfterUse::YES) {} + + /** + * Constructor + */ + Chunk(uint32 id) : _id(id), _stream(DisposeAfterUse::YES) {} }; private: Common::Array<Chunk> _chunks; public: /** + * Clear + */ + void clear() { _chunks.clear(); } + + /** * Add a chunk */ Common::WriteStream &add(uint32 chunkId); |