aboutsummaryrefslogtreecommitdiff
path: root/engines/glk
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk')
-rw-r--r--engines/glk/POTFILES1
-rw-r--r--engines/glk/detection.cpp28
-rw-r--r--engines/glk/frotz/detection.cpp35
-rw-r--r--engines/glk/frotz/detection.h5
-rw-r--r--engines/glk/quetzal.cpp89
-rw-r--r--engines/glk/quetzal.h13
-rw-r--r--engines/glk/streams.cpp88
-rw-r--r--engines/glk/streams.h27
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);