aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/glk/frotz/quetzal.h14
-rw-r--r--engines/glk/module.mk1
-rw-r--r--engines/glk/quetzal.cpp105
-rw-r--r--engines/glk/quetzal.h144
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