diff options
-rw-r--r-- | engines/glk/frotz/quetzal.h | 14 | ||||
-rw-r--r-- | engines/glk/module.mk | 1 | ||||
-rw-r--r-- | engines/glk/quetzal.cpp | 105 | ||||
-rw-r--r-- | engines/glk/quetzal.h | 144 |
4 files changed, 252 insertions, 12 deletions
diff --git a/engines/glk/frotz/quetzal.h b/engines/glk/frotz/quetzal.h index 98765b8319..10effff25c 100644 --- a/engines/glk/frotz/quetzal.h +++ b/engines/glk/frotz/quetzal.h @@ -24,22 +24,12 @@ #define GLK_FROTZ_QUETZAL #include "glk/glk_types.h" +#include "glk/quetzal.h" #include "glk/frotz/frotz_types.h" namespace Glk { namespace Frotz { -enum QueztalTag { - ID_FORM = MKTAG('F', 'O', 'R', 'M'), - ID_IFZS = MKTAG('I', 'F', 'Z', 'S'), - ID_IFhd = MKTAG('I', 'F', 'h', 'd'), - ID_UMem = MKTAG('U', 'M', 'e', 'm'), - ID_CMem = MKTAG('C', 'M', 'e', 'm'), - ID_Stks = MKTAG('S', 't', 'k', 's'), - ID_ANNO = MKTAG('A', 'N', 'N', 'O'), - ID_SCVM = MKTAG('S', 'C', 'V', 'M') -}; - class Processor; class Quetzal { @@ -63,7 +53,7 @@ private: void write_word(zword w) { _out->writeUint16BE(w); } void write_long(uint l) { _out->writeUint32BE(l); } void write_run(zword run) { write_byte(0); write_byte(run); } - void write_chnk(QueztalTag id, zword len) { + void write_chnk(uint32 id, zword len) { _out->writeUint32BE(id); _out->writeUint32BE(len); } diff --git a/engines/glk/module.mk b/engines/glk/module.mk index b599f02fb9..19dc6c9d97 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -11,6 +11,7 @@ MODULE_OBJS := \ glk_dispa.o \ pc_speaker.o \ picture.o \ + quetzal.o \ raw_decoder.o \ screen.o \ selection.o \ diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp new file mode 100644 index 0000000000..7a53e598ee --- /dev/null +++ b/engines/glk/quetzal.cpp @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "glk/quetzal.h" +#include "common/memstream.h" + +namespace Glk { + +bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) { + _chunks.clear(); + stream->seek(0); + if (stream->readUint32BE() != ID_FORM) + return false; + + uint32 size = stream->readUint32BE(); + uint32 fileFormType = stream->readUint32BE(); + + if (formType != ID_IFSF && fileFormType != formType) + return false; + if ((int)size > stream->size() || (size & 1) || (size < 4)) + return false; + size -= 4; + + // Iterate through reading chunk headers + while (size > 0) { + if (size < 8) + // Couldn't contain a chunk + return false; + + // Get in the chunk header + Chunk c; + c._id = stream->readUint32BE(); + c._size = stream->readUint32BE() - 8; + c._offset = stream->pos(); + _chunks.push_back(c); + + int chunkRemainder = c._size + (c._size & 1); + if ((stream->pos() + chunkRemainder) > stream->size()) + // Chunk goes beyond the file size + return false; + + size -= 8 + chunkRemainder; + stream->skip(chunkRemainder); + } + + return true; +} + +/*--------------------------------------------------------------------------*/ + +Common::WriteStream &QuetzalWriter::add(uint32 chunkId) { + // Sanity check to prevent adding the same chunk multiple times + for (uint idx = 0; idx < _chunks.size(); ++idx) { + if (_chunks[idx]._id == chunkId) + error("Duplicate chunk added"); + } + + _chunks.push_back(Chunk()); + return _chunks.back()._stream; +} + +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(); + + // Write out the header + out->writeUint32BE(ID_FORM); + out->writeUint32BE(size); + out->writeUint32BE(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->write(s.getData(), s.size()); + if (s.size() & 1) + out->writeByte(0); + } +} + +} // End of namespace Glk diff --git a/engines/glk/quetzal.h b/engines/glk/quetzal.h new file mode 100644 index 0000000000..2badf35bca --- /dev/null +++ b/engines/glk/quetzal.h @@ -0,0 +1,144 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GLK_QUETZAL +#define GLK_QUETZAL + +#include "common/array.h" +#include "common/endian.h" +#include "common/memstream.h" +#include "common/stream.h" +#include "glk/blorb.h" + +namespace Glk { + +enum QueztalTag { + ID_IFSF = MKTAG('I', 'F', 'S', 'F'), + ID_IFZS = MKTAG('I', 'F', 'Z', 'S'), + ID_IFhd = MKTAG('I', 'F', 'h', 'd'), + ID_UMem = MKTAG('U', 'M', 'e', 'm'), + ID_CMem = MKTAG('C', 'M', 'e', 'm'), + ID_Stks = MKTAG('S', 't', 'k', 's'), + ID_SCVM = MKTAG('S', 'C', 'V', 'M') +}; + +/** + * Quetzal save file reader + */ +class QuetzalReader { + struct Chunk { + uint32 _id; + size_t _offset, _size; + }; +public: + /** + * Iterator for the chunks list + */ + struct Iterator : public Chunk { + private: + Common::SeekableReadStream *_stream; + Common::Array<Chunk> &_chunks; + int _index; + int _size; + public: + /** + * Constructor + */ + Iterator(Common::SeekableReadStream *stream, Common::Array<Chunk> &chunks, int index, int size) : + _stream(stream), _chunks(chunks), _index(index), _size(size) {} + + /** + * Incrementer + */ + Iterator &operator++() { ++_index; } + + /** + * Decrementer + */ + Iterator &operator--() { --_index; } + + /** + * Get a read stream for the contents of a chunk + */ + Common::SeekableReadStream *getStream() { + _stream->seek(_offset); + return _stream->readStream(_size); + } + }; +private: + Common::SeekableReadStream *_stream; + Common::Array<Chunk> _chunks; +public: + /** + * Constructor + */ + QuetzalReader() : _stream(nullptr) {} + + /** + * Opens a Quetzal file for access + */ + bool open(Common::SeekableReadStream *stream, uint32 formType = ID_IFSF); + + /** + * Return an iterator for the beginning of the chunks list + */ + Iterator begin() { return Iterator(_stream, _chunks, 0, _chunks.size()); } + + /** + * Return an iterator for the beginning of the chunks list + */ + Iterator end() { return Iterator(_stream, _chunks, 0, _chunks.size()); } +}; + +/** + * Quetzal save file writer + */ +class QuetzalWriter { + /** + * Chunk entry + */ + struct Chunk { + uint32 _id; + Common::MemoryWriteStreamDynamic _stream; + + /** + * Constructor + */ + Chunk() : _id(0), _stream(DisposeAfterUse::YES) {} + }; +private: + Common::Array<Chunk> _chunks; +public: + /** + * Add a chunk + */ + Common::WriteStream &add(uint32 chunkId); + + /** + * Save the added chunks to file + */ + void save(Common::WriteStream *out, uint32 formType = ID_IFSF); +}; + +} // End of namespace Glk + +#endif |