diff options
Diffstat (limited to 'engines/glk')
-rw-r--r-- | engines/glk/POTFILES | 1 | ||||
-rw-r--r-- | engines/glk/detection.cpp | 28 | ||||
-rw-r--r-- | engines/glk/frotz/detection.cpp | 35 | ||||
-rw-r--r-- | engines/glk/frotz/detection.h | 5 | ||||
-rw-r--r-- | engines/glk/quetzal.cpp | 89 | ||||
-rw-r--r-- | engines/glk/quetzal.h | 13 | ||||
-rw-r--r-- | engines/glk/streams.cpp | 88 | ||||
-rw-r--r-- | engines/glk/streams.h | 27 |
8 files changed, 106 insertions, 180 deletions
diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES index 895fabdf51..2a9e70d807 100644 --- a/engines/glk/POTFILES +++ b/engines/glk/POTFILES @@ -1,4 +1,5 @@ engines/glk/glk_api.cpp +engines/glk/quetzal.cpp engines/glk/streams.cpp engines/glk/advsys/advsys.cpp engines/glk/advsys/vm.cpp diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp index 22b2d1036d..5de262ddd9 100644 --- a/engines/glk/detection.cpp +++ b/engines/glk/detection.cpp @@ -22,6 +22,7 @@ #include "glk/glk.h" #include "glk/detection.h" +#include "glk/quetzal.h" #include "glk/advsys/detection.h" #include "glk/advsys/advsys.h" #include "glk/alan2/detection.h" @@ -242,7 +243,6 @@ SaveStateList GlkMetaEngine::listSaves(const char *target) const { Common::StringArray filenames; Common::String saveDesc; Common::String pattern = Common::String::format("%s.0##", target); - Glk::SavegameHeader header; filenames = saveFileMan->listSavefiles(pattern); @@ -255,10 +255,9 @@ SaveStateList GlkMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - if (Glk::FileStream::readSavegameHeader(in, header)) - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); - else if (Glk::Frotz::FrotzMetaEngine::readSavegameHeader(in, header)) - saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + Common::String saveName; + if (Glk::QuetzalReader::getSavegameDescription(in, saveName)) + saveList.push_back(SaveStateDescriptor(slot, saveName)); delete in; } @@ -282,21 +281,18 @@ void GlkMetaEngine::removeSaveState(const char *target, int slot) const { SaveStateDescriptor GlkMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::String filename = Common::String::format("%s.%03d", target, slot); Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename); + SaveStateDescriptor ssd; + bool result = false; if (in) { - Glk::SavegameHeader header; - if (Glk::FileStream::readSavegameHeader(in, header)) { - // Create the return descriptor - SaveStateDescriptor desc(slot, header._saveName); - desc.setSaveDate(header._year, header._month, header._day); - desc.setSaveTime(header._hour, header._minute); - desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME); - - delete in; - return desc; - } + result = Glk::QuetzalReader::getSavegameMetaInfo(in, ssd); + ssd.setSaveSlot(slot); + delete in; } + if (result) + return ssd; + return SaveStateDescriptor(); } diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp index b9d5e1062b..0c8a92b9ab 100644 --- a/engines/glk/frotz/detection.cpp +++ b/engines/glk/frotz/detection.cpp @@ -159,40 +159,5 @@ void FrotzMetaEngine::detectClashes(Common::StringMap &map) { } } -bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk::SavegameHeader &header) { - stream->seek(0); - if (stream->readUint32BE() != ID_FORM) - return false; - stream->readUint32BE(); - if (stream->readUint32BE() != ID_IFZS) - return false; - - header._interpType = INTERPRETER_FROTZ; - header._saveName = _("Unnamed savegame"); - - while (stream->pos() < stream->size()) { - uint type = stream->readUint32BE(); - size_t len = stream->readUint32BE(); - - if (type == ID_ANNO) { - // Read savegame name from the annotation chunk - char *buffer = new char[len + 1]; - stream->read(buffer, len); - buffer[len] = '\0'; - header._saveName = Common::String(buffer); - break; - - } else { - if (len & 1) - // Length must be even - ++len; - stream->skip(len); - } - } - - stream->seek(0); - return true; -} - } // End of namespace Frotz } // End of namespace Glk diff --git a/engines/glk/frotz/detection.h b/engines/glk/frotz/detection.h index 7943385aa9..6e1643302c 100644 --- a/engines/glk/frotz/detection.h +++ b/engines/glk/frotz/detection.h @@ -60,11 +60,6 @@ public: * Check for game Id clashes with other sub-engines */ static void detectClashes(Common::StringMap &map); - - /** - * Check a passed stream for a Quetzal save, and if so, get header information - */ - static bool readSavegameHeader(Common::SeekableReadStream *stream, Glk::SavegameHeader &header); }; } // End of namespace Frotz diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp index 1425f419c4..1dd93a892d 100644 --- a/engines/glk/quetzal.cpp +++ b/engines/glk/quetzal.cpp @@ -25,9 +25,19 @@ #include "glk/events.h" #include "common/memstream.h" #include "common/system.h" +#include "common/translation.h" namespace Glk { +static Common::String readString(Common::ReadStream *src) { + char c; + Common::String result; + while ((c = src->readByte()) != 0) + result += c; + + return result; +} + void QuetzalReader::clear() { _chunks.clear(); _stream = nullptr; @@ -44,8 +54,12 @@ bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) { uint32 size = stream->readUint32BE(); uint32 fileFormType = stream->readUint32BE(); - if (formType != ID_IFSF && fileFormType != formType) + if (formType != ID_IFSF) return false; + if ((formType != 0 && fileFormType != formType) || + (formType == 0 && (fileFormType == ID_IFZS || fileFormType == ID_IFSF))) + return false; + if ((int)size > stream->size() || (size & 1) || (size < 4)) return false; size -= 4; @@ -75,6 +89,58 @@ bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) { return true; } +bool QuetzalReader::getSavegameDescription(Common::SeekableReadStream *rs, Common::String &saveName) { + QuetzalReader r; + if (!r.open(rs, 0)) + return false; + + for (Iterator it = r.begin(); it != r.end(); ++it) { + if ((*it)._id == ID_ANNO) { + Common::SeekableReadStream *s = it.getStream(); + saveName = readString(s); + delete s; + + return true; + } + } + + saveName = _("Untitled Savegame"); + return true; +} + +bool QuetzalReader::getSavegameMetaInfo(Common::SeekableReadStream *rs, SaveStateDescriptor &ssd) { + QuetzalReader r; + if (!r.open(rs, 0)) + return false; + + ssd.setDescription(_("Untitled Savegame")); + + for (Iterator it = r.begin(); it != r.end(); ++it) { + if ((*it)._id == ID_ANNO) { + Common::SeekableReadStream *s = it.getStream(); + ssd.setDescription(readString(s)); + delete s; + + return true; + } else if ((*it)._id == ID_SCVM) { + Common::SeekableReadStream *s = it.getStream(); + int year = s->readUint16BE(); + int month = s->readUint16BE(); + int day = s->readUint16BE(); + int hour = s->readUint16BE(); + int minute = s->readUint16BE(); + uint32 playTime = s->readUint32BE(); + delete s; + + ssd.setSaveDate(year, month, day); + ssd.setSaveTime(hour, minute); + ssd.setPlayTime(playTime); + } + } + + return true; +} + /*--------------------------------------------------------------------------*/ Common::WriteStream &QuetzalWriter::add(uint32 chunkId) { @@ -122,19 +188,26 @@ void QuetzalWriter::addCommonChunks(const Common::String &saveName) { ws.writeByte(0); } - // Write 'SCVM' chunk with gameplay statistics + // Write 'SCVM' chunk with game version & gameplay statistics { Common::WriteStream &ws = add(ID_SCVM); // Write out the save date/time TimeDate td; g_system->getTimeAndDate(td); - ws.writeSint16LE(td.tm_year + 1900); - ws.writeSint16LE(td.tm_mon + 1); - ws.writeSint16LE(td.tm_mday); - ws.writeSint16LE(td.tm_hour); - ws.writeSint16LE(td.tm_min); - ws.writeUint32LE(g_vm->_events->getTotalPlayTicks()); + ws.writeSint16BE(td.tm_year + 1900); + ws.writeSint16BE(td.tm_mon + 1); + ws.writeSint16BE(td.tm_mday); + ws.writeSint16BE(td.tm_hour); + ws.writeSint16BE(td.tm_min); + ws.writeUint32BE(g_vm->_events->getTotalPlayTicks()); + + // Write out intrepreter type, language, and game Id + ws.writeByte(g_vm->getInterpreterType()); + ws.writeByte(g_vm->getLanguage()); + Common::String md5 = g_vm->getGameMD5(); + ws.write(md5.c_str(), md5.size()); + ws.writeByte('\0'); } } diff --git a/engines/glk/quetzal.h b/engines/glk/quetzal.h index 454786f31c..b398571f38 100644 --- a/engines/glk/quetzal.h +++ b/engines/glk/quetzal.h @@ -27,6 +27,7 @@ #include "common/endian.h" #include "common/memstream.h" #include "common/stream.h" +#include "engines/savestate.h" #include "glk/blorb.h" namespace Glk { @@ -121,7 +122,7 @@ public: /** * Opens a Quetzal file for access */ - bool open(Common::SeekableReadStream *stream, uint32 formType = ID_IFSF); + bool open(Common::SeekableReadStream *stream, uint32 formType = 0); /** * Return an iterator for the beginning of the chunks list @@ -132,6 +133,16 @@ public: * Return an iterator for the beginning of the chunks list */ Iterator end() { return Iterator(_stream, _chunks, _chunks.size()); } + + /** + * Loads a Quetzal save and extracts it's description from an ANNO chunk + */ + static bool getSavegameDescription(Common::SeekableReadStream *rs, Common::String &saveName); + + /** + * Loads a Quetzal save and extract's it's description and meta info + */ + static bool getSavegameMetaInfo(Common::SeekableReadStream *rs, SaveStateDescriptor &ssd); }; /** diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp index e0271baea8..be8ae24ae7 100644 --- a/engines/glk/streams.cpp +++ b/engines/glk/streams.cpp @@ -785,10 +785,6 @@ FileStream::FileStream(Streams *streams, frefid_t fref, uint fmode, uint rock, b if (!_outFile) error("Could open file for writing - %s", fname.c_str()); - // For creating savegames, write out the header. Frotz is a special case, - // since the Quetzal format is used for compatibility - if (fref->_slotNumber != -1 && g_vm->getInterpreterType() != INTERPRETER_FROTZ) - writeSavegameHeader(_outFile, fref->_description); } else if (fmode == filemode_Read) { if (_file.open(fname)) { _inStream = &_file; @@ -799,23 +795,6 @@ FileStream::FileStream(Streams *streams, frefid_t fref, uint fmode, uint rock, b if (!_inStream) error("Could not open for reading - %s", fname.c_str()); - - if (_inFile) { - // It's a save file, so skip over the header - SavegameHeader header; - if (!(g_vm->getInterpreterType() == INTERPRETER_FROTZ ? - Frotz::FrotzMetaEngine::readSavegameHeader(_inStream, header) : - readSavegameHeader(_inStream, header))) - error("Invalid savegame"); - - if (g_vm->getInterpreterType() != INTERPRETER_FROTZ) { - if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage() - || header._md5 != g_vm->getGameMD5()) - error("Savegame is for a different game"); - - g_vm->_events->setTotalPlayTicks(header._totalFrames); - } - } } } @@ -1359,73 +1338,6 @@ uint FileStream::getLineUni(uint32 *ubuf, uint len) { } } -static Common::String readString(Common::ReadStream *src) { - char c; - Common::String result; - while ((c = src->readByte()) != 0) - result += c; - - return result; -} - -bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header) { - header._totalFrames = 0; - - // Validate the header Id - if (stream->readUint32BE() != MKTAG('G', 'A', 'R', 'G')) - return false; - - // Check the savegame version - header._version = stream->readByte(); - if (header._version > SAVEGAME_VERSION) - error("Savegame is too recent"); - - // Read the interpreter, language, and game Id - header._interpType = stream->readByte(); - header._language = stream->readByte(); - header._md5 = readString(stream); - - // Read in name - header._saveName = readString(stream); - - // Read in save date/time - header._year = stream->readUint16LE(); - header._month = stream->readUint16LE(); - header._day = stream->readUint16LE(); - header._hour = stream->readUint16LE(); - header._minute = stream->readUint16LE(); - header._totalFrames = stream->readUint32LE(); - - return true; -} - -void FileStream::writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName) { - // Write out a savegame header - stream->writeUint32BE(MKTAG('G', 'A', 'R', 'G')); - stream->writeByte(SAVEGAME_VERSION); - - // Write out intrepreter type, language, and game Id - stream->writeByte(g_vm->getInterpreterType()); - stream->writeByte(g_vm->getLanguage()); - Common::String md5 = g_vm->getGameMD5(); - stream->write(md5.c_str(), md5.size()); - stream->writeByte('\0'); - - // Write savegame name - stream->write(saveName.c_str(), saveName.size()); - stream->writeByte('\0'); - - // Write out the save date/time - TimeDate td; - g_system->getTimeAndDate(td); - stream->writeUint16LE(td.tm_year + 1900); - stream->writeUint16LE(td.tm_mon + 1); - stream->writeUint16LE(td.tm_mday); - stream->writeUint16LE(td.tm_hour); - stream->writeUint16LE(td.tm_min); - stream->writeUint32LE(g_vm->_events->getTotalPlayTicks()); -} - /*--------------------------------------------------------------------------*/ Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) { diff --git a/engines/glk/streams.h b/engines/glk/streams.h index 14ca91dfc0..2d6d48f1e1 100644 --- a/engines/glk/streams.h +++ b/engines/glk/streams.h @@ -66,23 +66,6 @@ struct StreamResult { }; typedef StreamResult stream_result_t; -struct SavegameHeader { - uint8 _version; - byte _interpType; - byte _language; - Common::String _md5; - Common::String _saveName; - int _year, _month, _day; - int _hour, _minute; - int _totalFrames; - - /** - * Constructor - */ - SavegameHeader() : _version(0), _interpType(0), _language(0), _year(0), _month(0), _day(0), - _hour(0), _minute(0), _totalFrames(0) {} -}; - /** * File details */ @@ -476,16 +459,6 @@ private: int getCharUtf8(); public: /** - * Read a savegame header from a stream - */ - static bool readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header); - - /** - * Write out a savegame header - */ - static void writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName); -public: - /** * Constructor */ FileStream(Streams *streams, frefid_t fref, uint fmode, uint rock, bool unicode); |