[Scummvm-git-logs] scummvm master -> a9261a6d315a452dfb63ead326aa72fcc3075ae9

dreammaster paulfgilbert at gmail.com
Sun Jun 16 23:59:55 CEST 2019


This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
3acba22cba GLK: FROTZ: Move creation of Quetzal ANNO chunk into base Quetzal writer
3876fb6710 GLK: In progress transition to all sub-engines using Quetzal save files
553bb74f8c GLK: Further changeover of sub-engines to use new savegame code
919670a565 GLK: ADVSYS: Save/load fixes
611bea7d73 GLK: ADVSYS: Fix savegame area setup
a9261a6d31 GLK: ADVSYS: Do a look action after loading a savegame from launcher


Commit: 3acba22cba5831b641b0bdaab26c337327d5911e
    https://github.com/scummvm/scummvm/commit/3acba22cba5831b641b0bdaab26c337327d5911e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: FROTZ: Move creation of Quetzal ANNO chunk into base Quetzal writer

Changed paths:
    engines/glk/frotz/quetzal.cpp
    engines/glk/quetzal.cpp
    engines/glk/quetzal.h


diff --git a/engines/glk/frotz/quetzal.cpp b/engines/glk/frotz/quetzal.cpp
index d12d41d..64929c0 100644
--- a/engines/glk/frotz/quetzal.cpp
+++ b/engines/glk/frotz/quetzal.cpp
@@ -65,13 +65,6 @@ bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::Stri
 		ws.writeByte(pc & 0xff);
 	}
 
-	// Write 'ANNO' chunk
-	{
-		Common::WriteStream &ws = _writer.add(ID_ANNO);
-		ws.write(desc.c_str(), desc.size());
-		ws.writeByte(0);
-	}
-
 	// Write `CMem' chunk.
 	{
 		Common::WriteStream &ws = _writer.add(ID_CMem);
@@ -166,7 +159,7 @@ bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::Stri
 	}
 
 	// Write the save data out
-	_writer.save(svf, ID_IFZS);
+	_writer.save(svf, desc, ID_IFZS);
 
 	// After all that, still nothing went wrong!
 	return true;
diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp
index fb2f70c..1425f41 100644
--- a/engines/glk/quetzal.cpp
+++ b/engines/glk/quetzal.cpp
@@ -21,7 +21,10 @@
  */
 
 #include "glk/quetzal.h"
+#include "glk/glk_api.h"
+#include "glk/events.h"
 #include "common/memstream.h"
+#include "common/system.h"
 
 namespace Glk {
 
@@ -85,7 +88,10 @@ Common::WriteStream &QuetzalWriter::add(uint32 chunkId) {
 	return _chunks.back()._stream;
 }
 
-void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) {
+void QuetzalWriter::save(Common::WriteStream *out, const Common::String &saveName, uint32 formType) {
+	// Add chunks common to all Glk savegames
+	addCommonChunks(saveName);
+	
 	// Calculate the size of the chunks
 	uint size = 4;
 	for (uint idx = 0; idx < _chunks.size(); ++idx)
@@ -108,4 +114,28 @@ void QuetzalWriter::save(Common::WriteStream *out, uint32 formType) {
 	}
 }
 
+void QuetzalWriter::addCommonChunks(const Common::String &saveName) {
+	// Write 'ANNO' chunk
+	{
+		Common::WriteStream &ws = add(ID_ANNO);
+		ws.write(saveName.c_str(), saveName.size());
+		ws.writeByte(0);
+	}
+
+	// Write 'SCVM' chunk with 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());
+	}
+}
+
 } // End of namespace Glk
diff --git a/engines/glk/quetzal.h b/engines/glk/quetzal.h
index 19dd939..454786f 100644
--- a/engines/glk/quetzal.h
+++ b/engines/glk/quetzal.h
@@ -157,6 +157,11 @@ class QuetzalWriter {
 	};
 private:
 	Common::Array<Chunk> _chunks;
+
+	/**
+	 * Add chunks common to all Glk savegames
+	 */
+	void addCommonChunks(const Common::String &saveName);
 public:
 	/**
 	 * Clear
@@ -171,7 +176,7 @@ public:
 	/**
 	 * Save the added chunks to file
 	 */
-	void save(Common::WriteStream *out, uint32 formType = ID_IFSF);
+	void save(Common::WriteStream *out, const Common::String &saveName, uint32 formType = ID_IFSF);
 };
 
 } // End of namespace Glk


Commit: 3876fb67108284063b4d133cf087a04dae6993e4
    https://github.com/scummvm/scummvm/commit/3876fb67108284063b4d133cf087a04dae6993e4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: In progress transition to all sub-engines using Quetzal save files

Changed paths:
    engines/glk/POTFILES
    engines/glk/detection.cpp
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/detection.h
    engines/glk/quetzal.cpp
    engines/glk/quetzal.h
    engines/glk/streams.cpp
    engines/glk/streams.h


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
index 895fabd..2a9e70d 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 22b2d10..5de262d 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 b9d5e10..0c8a92b 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 7943385..6e16433 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 1425f41..1dd93a8 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 454786f..b398571 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 e0271ba..be8ae24 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 14ca91d..2d6d48f 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);


Commit: 553bb74f8c380ee31fb771c6619dad8e83fed8ce
    https://github.com/scummvm/scummvm/commit/553bb74f8c380ee31fb771c6619dad8e83fed8ce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: Further changeover of sub-engines to use new savegame code

Changed paths:
    engines/glk/POTFILES
    engines/glk/advsys/advsys.cpp
    engines/glk/advsys/advsys.h
    engines/glk/alan2/alan2.cpp
    engines/glk/alan2/alan2.h
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/serial.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/magnetic/magnetic.cpp
    engines/glk/magnetic/magnetic.h
    engines/glk/quetzal.cpp
    engines/glk/quetzal.h
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h
    engines/glk/tads/tads.cpp
    engines/glk/tads/tads.h


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
index 2a9e70d..cedc0e1 100644
--- a/engines/glk/POTFILES
+++ b/engines/glk/POTFILES
@@ -5,6 +5,7 @@ engines/glk/advsys/advsys.cpp
 engines/glk/advsys/vm.cpp
 engines/glk/alan2/alan2.cpp
 engines/glk/frotz/detection.cpp
+engines/glk/frotz/frotz.cpp
 engines/glk/glulxe/glulxe.cpp
 engines/glk/magnetic/magnetic.cpp
 engines/glk/scott/scott.cpp
diff --git a/engines/glk/advsys/advsys.cpp b/engines/glk/advsys/advsys.cpp
index ad84128..5100320 100644
--- a/engines/glk/advsys/advsys.cpp
+++ b/engines/glk/advsys/advsys.cpp
@@ -102,11 +102,13 @@ bool AdvSys::singleAction() {
 	return true;
 }
 
-Common::Error AdvSys::loadGameData(strid_t save) {
+Common::Error AdvSys::readSaveData(Common::SeekableReadStream *rs) {
+	rs->read(_saveArea, rs->size());
 	return Common::kNoError;
 }
 
-Common::Error AdvSys::saveGameData(strid_t save, const Common::String &desc) {
+Common::Error AdvSys::writeGameData(Common::WriteStream *ws) {
+	ws->write(_saveArea, _saveSize);
 	return Common::kNoError;
 }
 
diff --git a/engines/glk/advsys/advsys.h b/engines/glk/advsys/advsys.h
index 688a8fe..8b7b7fb 100644
--- a/engines/glk/advsys/advsys.h
+++ b/engines/glk/advsys/advsys.h
@@ -67,14 +67,15 @@ public:
 	}
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t save) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t save, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
 } // End of namespace AdvSys
diff --git a/engines/glk/alan2/alan2.cpp b/engines/glk/alan2/alan2.cpp
index da9dbf1..492d425 100644
--- a/engines/glk/alan2/alan2.cpp
+++ b/engines/glk/alan2/alan2.cpp
@@ -61,14 +61,14 @@ void Alan2::runGame() {
 	// TODO
 }
 
-Common::Error Alan2::loadGameData(strid_t file) {
+Common::Error Alan2::readSaveData(Common::SeekableReadStream *rs) {
 	// TODO
-	return Common::kNoError;
+	return Common::kReadingFailed;
 }
 
-Common::Error Alan2::saveGameData(strid_t file, const Common::String &desc) {
+Common::Error Alan2::writeGameData(Common::WriteStream *ws) {
 	// TODO
-	return Common::kNoError;
+	return Common::kWritingFailed;
 }
 
 bool Alan2::is_gamefile_valid() {
diff --git a/engines/glk/alan2/alan2.h b/engines/glk/alan2/alan2.h
index a6ded43..a8e500a 100644
--- a/engines/glk/alan2/alan2.h
+++ b/engines/glk/alan2/alan2.h
@@ -68,14 +68,15 @@ public:
 	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_ALAN2; }
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t file) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 
 	/**
 	 * Output a string to the screen
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 0c33f41..02ee090 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -26,6 +26,7 @@
 #include "glk/frotz/quetzal.h"
 #include "engines/util.h"
 #include "common/config-manager.h"
+#include "common/translation.h"
 
 namespace Glk {
 namespace Frotz {
@@ -90,17 +91,13 @@ void Frotz::initialize() {
 	z_restart();
 }
 
-Common::Error Frotz::saveGameData(strid_t file, const Common::String &desc) {
-	Quetzal q(story_fp);
-	bool success = q.save(*file, this, desc);
+Common::Error Frotz::loadGameState(int slot) {
+	FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
 
-	if (!success)
-		print_string("Error writing save file\n");
+	strid_t file = _streams->openFileStream(&ref, filemode_Read);
+	if (file == nullptr)
+		return Common::kReadingFailed;
 
-	return Common::kNoError;
-}
-
-Common::Error Frotz::loadGameData(strid_t file) {
 	Quetzal q(story_fp);
 	bool success = q.restore(*file, this) == 2;
 
@@ -123,14 +120,32 @@ Common::Error Frotz::loadGameData(strid_t file) {
 		 * seems to cover up most of the resulting badness.
 		 */
 		if (h_version > V3 && h_version != V6 && (h_screen_rows != old_screen_rows
-					|| h_screen_cols != old_screen_cols))
+			|| h_screen_cols != old_screen_cols))
 			erase_window(1);
 	} else {
-		error("Error reading save file");
+		error(_("Error reading save file"));
 	}
 
 	return Common::kNoError;
 }
 
+Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
+	Common::String msg;
+	FileReference ref(slot, desc, fileusage_BinaryMode | fileusage_SavedGame);
+
+	strid_t file = _streams->openFileStream(&ref, filemode_Write);
+	if (file == nullptr)
+		return Common::kWritingFailed;
+
+	Quetzal q(story_fp);
+	bool success = q.save(*file, this, desc);
+
+	if (!success)
+		print_string(_("Error writing save file\n"));
+
+	return Common::kNoError;
+
+}
+
 } // End of namespace Frotz
 } // End of namespace Glk
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index 8312e16..a8083a3 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -72,14 +72,25 @@ public:
 	virtual void runGame() override;
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from a given slot
 	 */
-	virtual Common::Error loadGameData(strid_t file) override;
+	virtual Common::Error loadGameState(int slot) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game to a given slot
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+
+	/**
+	 * Loading method not used for Frotz sub-engine
+	 */
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override { return Common::kReadingFailed; }
+
+	/**
+	 * Saving method not used for Frotz sub-engine
+	 */
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override { return Common::kWritingFailed; }
+
 };
 
 extern Frotz *g_vm;
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 7155cd8..b3760ca 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -33,6 +33,7 @@
 #include "glk/conf.h"
 #include "glk/events.h"
 #include "glk/picture.h"
+#include "glk/quetzal.h"
 #include "glk/screen.h"
 #include "glk/selection.h"
 #include "glk/sound.h"
@@ -182,10 +183,32 @@ Common::Error GlkEngine::loadGameState(int slot) {
 	if (file == nullptr)
 		return Common::kReadingFailed;
 
-	Common::Error result = loadGameData(file);
+	Common::ErrorCode errCode = Common::kNoError;
+	QuetzalReader r;
+	if (r.open(*file, ID_IFSF)) {
+		// First scan for a SCVM chunk. It has information of the game the save is for,
+		// so if present we can validate the save is for this game
+		for (QuetzalReader::Iterator it = r.begin(); it != r.end(); ++it) {
+			if ((*it)._id == ID_SCVM) {
+
+			}
+		}
+
+		if (errCode != Common::kNoError) {
+			// Scan for an uncompressed memory chunk
+			errCode = Common::kReadingFailed;		// Presume we won't find chunk
+			for (QuetzalReader::Iterator it = r.begin(); it != r.end(); ++it) {
+				if ((*it)._id == ID_UMem) {
+					Common::SeekableReadStream *rs = it.getStream();
+					errCode = readSaveData(rs).getCode();
+					delete rs;
+				}
+			}
+		}
+	}
 
 	file->close();
-	return result;
+	return errCode;
 }
 
 Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
@@ -196,10 +219,21 @@ Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
 	if (file == nullptr)
 		return Common::kWritingFailed;
 
-	Common::Error result = saveGameData(file, desc);
+	Common::ErrorCode errCode = Common::kNoError;
+	QuetzalWriter w;
+
+	// Add the uncompressed memory chunk with the game's save data
+	{
+		Common::WriteStream &ws = w.add(ID_UMem);
+		errCode = writeGameData(&ws).getCode();
+	}
+
+	if (errCode != Common::kNoError) {
+		w.save(*file, desc);
+	}
 
 	file->close();
-	return result;
+	return errCode;
 }
 
 void GlkEngine::beep() {
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index f96f384..739f177 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -198,14 +198,15 @@ public:
 	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
 
 	/**
-	 * Load a savegame from the passed file
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t file) = 0;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) = 0;
 
 	/**
-	 * Save the game to the passed file
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) = 0;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) = 0;
 
 	/**
 	 * Generate a beep
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index 5fd15a3..cd26241 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -681,12 +681,20 @@ PerformJump: /* goto label for successful jumping... ironic, no? */
 
 			case op_save:
 				push_callstub(inst[1].desttype, inst[1].value);
+#ifdef TODO
 				value = saveGameData(find_stream_by_id(inst[0].value), "Savegame").getCode() == Common::kNoError ? 0 : 1;
+#else
+				error("TODO");
+#endif
 				pop_callstub(value);
 				break;
 
 			case op_restore:
+#ifdef TODO
 				value = loadGameData(find_stream_by_id(inst[0].value)).getCode() == Common::kNoError ? 0 : 1;
+#else
+				error("TODO");
+#endif
 				if (value == 0) {
 					/* We've succeeded, and the stack now contains the callstub
 					   saved during saveundo. Ignore this opcode's operand. */
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 56a912e..bf1bad9 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -407,14 +407,15 @@ public:
 	}
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t str) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t str, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 
 	/**
 	 * \defgroup Main access methods
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
index d485160..81cfa9e 100644
--- a/engines/glk/glulxe/serial.cpp
+++ b/engines/glk/glulxe/serial.cpp
@@ -232,13 +232,13 @@ uint Glulxe::perform_restoreundo() {
 	return res;
 }
 
-Common::Error Glulxe::saveGameData(strid_t str, const Common::String &desc) {
+Common::Error Glulxe::writeGameData(Common::WriteStream *ws) {
 	dest_t dest;
 	int ix;
-	uint res, lx, val;
+	uint res = 0, lx, val;
 	uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0;
 	uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0;
-
+#ifdef TODO
 	stream_get_iosys(&val, &lx);
 	if (val != 2) {
 		/* Not using the Glk I/O system, so bail. This function only
@@ -246,14 +246,14 @@ Common::Error Glulxe::saveGameData(strid_t str, const Common::String &desc) {
 		fatal_error("Streams are only available in Glk I/O system.");
 	}
 
-	if (str == nullptr)
+	if (ws == nullptr)
 		return Common::kUnknownError;
 
 	dest.ismem = false;
 	dest.size = 0;
 	dest.pos = 0;
 	dest.ptr = nullptr;
-	dest.str = str;
+	dest.str = ws;
 
 	res = 0;
 
@@ -357,11 +357,11 @@ Common::Error Glulxe::saveGameData(strid_t str, const Common::String &desc) {
 	}
 
 	/* All done. */
-
+#endif
 	return res ? Common::kUnknownError : Common::kNoError;
 }
 
-Common::Error Glulxe::loadGameData(strid_t str) {
+Common::Error Glulxe::readSaveData(Common::SeekableReadStream *rs) {
 	dest_t dest;
 	int ix;
 	uint lx, res, val;
@@ -369,7 +369,7 @@ Common::Error Glulxe::loadGameData(strid_t str) {
 	uint heapsumlen = 0;
 	uint *heapsumarr = nullptr;
 	bool fromshell = false;
-
+#ifdef TODO
 	/* If profiling is enabled and active then fail. */
 #if VM_PROFILING
 	if (profile_profiling_active())
@@ -475,7 +475,7 @@ Common::Error Glulxe::loadGameData(strid_t str) {
 
 	if (res)
 		return Common::kUnknownError;
-
+#endif
 	return Common::kNoError;
 }
 
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index cd4660d..72db1a2 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -152,7 +152,7 @@ void Hugo::runGame() {
 	hugo_closefiles();
 }
 
-Common::Error Hugo::loadGameData(strid_t save) {
+Common::Error Hugo::readSaveData(Common::SeekableReadStream *rs) {
 	char testid[3], testserial[9];
 	int lbyte, hbyte;
 	int j;
@@ -160,18 +160,18 @@ Common::Error Hugo::loadGameData(strid_t save) {
 	long i;
 
 	/* Check ID */
-	testid[0] = (char)hugo_fgetc(save);
-	testid[1] = (char)hugo_fgetc(save);
+	testid[0] = (char)hugo_fgetc(rs);
+	testid[1] = (char)hugo_fgetc(rs);
 	testid[2] = '\0';
-	if (hugo_ferror(save)) goto RestoreError;
+	if (hugo_ferror(rs)) goto RestoreError;
 
 	if (strcmp(testid, id)) {
-		GUIErrorMessage("Incorrect save file.");
+		GUIErrorMessage("Incorrect rs file.");
 		goto RestoreError;
 	}
 
 	/* Check serial number */
-	if (!hugo_fgets(testserial, 9, save)) goto RestoreError;
+	if (!hugo_fgets(testserial, 9, rs)) goto RestoreError;
 	if (strcmp(testserial, serial))
 	{
 		GUIErrorMessage("Save file created by different version.");
@@ -181,7 +181,7 @@ Common::Error Hugo::loadGameData(strid_t save) {
 	/* Restore variables */
 	for (k=0; k<MAXGLOBALS+MAXLOCALS; k++)
 	{
-		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+		if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
 			goto RestoreError;
 		var[k] = lbyte + hbyte * 256;
 	}
@@ -193,11 +193,11 @@ Common::Error Hugo::loadGameData(strid_t save) {
 
 	while (i<codeend-(long)(objtable*16L))
 	{
-		if ((hbyte = hugo_fgetc(save))==EOF && hugo_ferror(save)) goto RestoreError;
+		if ((hbyte = hugo_fgetc(rs))==EOF && hugo_ferror(rs)) goto RestoreError;
 
 		if (hbyte==0)
 		{
-			if ((lbyte = hugo_fgetc(save))==EOF && hugo_ferror(save)) goto RestoreError;
+			if ((lbyte = hugo_fgetc(rs))==EOF && hugo_ferror(rs)) goto RestoreError;
 			SETMEM(objtable*16L+i, (unsigned char)lbyte);
 			i++;
 
@@ -217,28 +217,25 @@ Common::Error Hugo::loadGameData(strid_t save) {
 	}
 
 	/* Restore undo data */
-	if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+	if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
 		goto RestoreError;
 	undosize = lbyte + hbyte*256;
 
 	/* We can only restore undo data if it was saved by a port with
 	   the same MAXUNDO as us */
-	if (undosize==MAXUNDO)
-	{
-		for (k=0; k<MAXUNDO; k++)
-		{
-			for (j=0; j<5; j++)
-			{
-				if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+	if (undosize == MAXUNDO) {
+		for (k = 0; k < MAXUNDO; k++) {
+			for (j=0; j<5; j++) {
+				if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
 					goto RestoreError;
 				undostack[k][j] = lbyte + hbyte*256;
 			}
 		}
-		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
 		undoptr = lbyte + hbyte*256;
-		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
 		undoturn = lbyte + hbyte*256;
-		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
 		undoinvalid = (unsigned char)lbyte, undorecord = (unsigned char)hbyte;
 	}
 	else undoinvalid = true;
@@ -249,32 +246,31 @@ RestoreError:
 	return Common::kReadingFailed;
 }
 
-Common::Error Hugo::saveGameData(strid_t save, const Common::String &desc) {
+Common::Error Hugo::writeGameData(Common::WriteStream *ws) {
 	int c, j;
 	int lbyte, hbyte;
 	long i;
 	int samecount = 0;
 
 	/* Write ID */
-	if (hugo_fputc(id[0], save) == EOF || hugo_fputc(id[1], save) == EOF) goto SaveError;
+	if (hugo_fputc(id[0], ws) == EOF || hugo_fputc(id[1], ws) == EOF) goto SaveError;
 
 	/* Write serial number */
-	if (hugo_fputs(serial, save) == EOF) goto SaveError;
+	if (hugo_fputs(serial, ws) == EOF) goto SaveError;
 
 	/* Save variables */
 	for (c = 0; c<MAXGLOBALS + MAXLOCALS; c++)
 	{
 		hbyte = (unsigned int)var[c] / 256;
 		lbyte = (unsigned int)var[c] - hbyte * 256;
-		if (hugo_fputc(lbyte, save) == EOF || hugo_fputc(hbyte, save) == EOF) goto SaveError;
+		if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF) goto SaveError;
 	}
 
 	/* Save objtable to end of code space */
 
 	if (hugo_fseek(game, objtable * 16L, SEEK_SET)) goto SaveError;
 
-	for (i = 0; i <= codeend - (long)(objtable * 16L); i++)
-	{
+	for (i = 0; i <= codeend - (long)(objtable * 16L); i++) {
 		if ((lbyte = hugo_fgetc(game)) == EOF) goto SaveError;
 		hbyte = MEM(objtable * 16L + i);
 
@@ -282,45 +278,42 @@ Common::Error Hugo::saveGameData(strid_t save, const Common::String &desc) {
 		if (lbyte == hbyte && samecount<255) samecount++;
 
 		/* If memory differs (or samecount exceeds 1 byte) */
-		else
-		{
+		else {
 			if (samecount)
-				if (hugo_fputc(samecount, save) == EOF) goto SaveError;
+				if (hugo_fputc(samecount, ws) == EOF) goto SaveError;
 
 			if (lbyte != hbyte)
 			{
-				if (hugo_fputc(0, save) == EOF) goto SaveError;
-				if (hugo_fputc(hbyte, save) == EOF) goto SaveError;
+				if (hugo_fputc(0, ws) == EOF) goto SaveError;
+				if (hugo_fputc(hbyte, ws) == EOF) goto SaveError;
 				samecount = 0;
 			}
 			else samecount = 1;
 		}
 	}
 	if (samecount)
-		if (hugo_fputc(samecount, save) == EOF) goto SaveError;
+		if (hugo_fputc(samecount, ws) == EOF) goto SaveError;
 
 	/* Save undo data */
 
 	/* Save the number of turns in this port's undo stack */
 	hbyte = (unsigned int)MAXUNDO / 256;
 	lbyte = (unsigned int)MAXUNDO - hbyte * 256;
-	if (hugo_fputc(lbyte, save) == EOF || hugo_fputc(hbyte, save) == EOF)
+	if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF)
 		goto SaveError;
-	for (c = 0; c<MAXUNDO; c++)
-	{
-		for (j = 0; j<5; j++)
-		{
+	for (c = 0; c < MAXUNDO; c++) {
+		for (j = 0; j < 5; j++) {
 			hbyte = (unsigned int)undostack[c][j] / 256;
 			lbyte = (unsigned int)undostack[c][j] - hbyte * 256;
-			if (hugo_fputc(lbyte, save) == EOF || hugo_fputc(hbyte, save) == EOF)
+			if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF)
 				goto SaveError;
 		}
 	}
-	if (hugo_fputc(undoptr - (undoptr / 256) * 256, save) == EOF || hugo_fputc(undoptr / 256, save) == EOF)
+	if (hugo_fputc(undoptr - (undoptr / 256) * 256, ws) == EOF || hugo_fputc(undoptr / 256, ws) == EOF)
 		goto SaveError;
-	if (hugo_fputc(undoturn - (undoturn / 256) * 256, save) == EOF || hugo_fputc(undoturn / 256, save) == EOF)
+	if (hugo_fputc(undoturn - (undoturn / 256) * 256, ws) == EOF || hugo_fputc(undoturn / 256, ws) == EOF)
 		goto SaveError;
-	if (hugo_fputc(undoinvalid, save) == EOF || hugo_fputc(undorecord, save) == EOF)
+	if (hugo_fputc(undoinvalid, ws) == EOF || hugo_fputc(undorecord, ws) == EOF)
 		goto SaveError;
 
 	return Common::kNoError;
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index f2d83b8..1cd1682 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -1191,14 +1191,15 @@ public:
 	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_HUGO; }
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t save) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t save, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/magnetic/magnetic.cpp b/engines/glk/magnetic/magnetic.cpp
index c658b8c..7bd3fd6 100644
--- a/engines/glk/magnetic/magnetic.cpp
+++ b/engines/glk/magnetic/magnetic.cpp
@@ -58,14 +58,14 @@ void Magnetic::runGame() {
 	// TODO
 }
 
-Common::Error Magnetic::loadGameData(strid_t file) {
+Common::Error Magnetic::readSaveData(Common::SeekableReadStream *rs) {
 	// TODO
-	return Common::kNoError;
+	return Common::kReadingFailed;
 }
 
-Common::Error Magnetic::saveGameData(strid_t file, const Common::String &desc) {
+Common::Error Magnetic::writeGameData(Common::WriteStream *ws) {
 	// TODO
-	return Common::kNoError;
+	return Common::kWritingFailed;
 }
 
 bool Magnetic::is_gamefile_valid() {
diff --git a/engines/glk/magnetic/magnetic.h b/engines/glk/magnetic/magnetic.h
index 04f1333..c926ede 100644
--- a/engines/glk/magnetic/magnetic.h
+++ b/engines/glk/magnetic/magnetic.h
@@ -191,14 +191,15 @@ public:
 	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_MAGNETIC; }
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t file) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
 } // End of namespace Magnetic
diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp
index 1dd93a8..ae81d88 100644
--- a/engines/glk/quetzal.cpp
+++ b/engines/glk/quetzal.cpp
@@ -29,15 +29,6 @@
 
 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;
@@ -141,6 +132,15 @@ bool QuetzalReader::getSavegameMetaInfo(Common::SeekableReadStream *rs, SaveStat
 	return true;
 }
 
+Common::String QuetzalReader::readString(Common::ReadStream *src) {
+	char c;
+	Common::String result;
+	while ((c = src->readByte()) != 0)
+		result += c;
+
+	return result;
+}
+
 /*--------------------------------------------------------------------------*/
 
 Common::WriteStream &QuetzalWriter::add(uint32 chunkId) {
diff --git a/engines/glk/quetzal.h b/engines/glk/quetzal.h
index b398571..7f2dd30 100644
--- a/engines/glk/quetzal.h
+++ b/engines/glk/quetzal.h
@@ -143,6 +143,11 @@ public:
 	 * Loads a Quetzal save and extract's it's description and meta info
 	 */
 	static bool getSavegameMetaInfo(Common::SeekableReadStream *rs, SaveStateDescriptor &ssd);
+
+	/**
+	 * Support method for reading a string from a stream
+	 */
+	static Common::String readString(Common::ReadStream *src);
 };
 
 /**
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index b918f8c..11b01c9 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "glk/scott/scott.h"
+#include "glk/quetzal.h"
 #include "common/config-manager.h"
 #include "common/translation.h"
 
@@ -508,41 +509,44 @@ void Scott::lineInput(char *buf, size_t n) {
 	buf[ev.val1] = 0;
 }
 
-Common::Error Scott::saveGameData(strid_t file, const Common::String &desc) {
+Common::Error Scott::writeGameData(Common::WriteStream *ws) {
 	Common::String msg;
 
 	for (int ct = 0; ct < 16; ct++) {
 		msg = Common::String::format("%d %d\n", _counters[ct], _roomSaved[ct]);
-		glk_put_string_stream(file, msg.c_str());
+		ws->write(msg.c_str(), msg.size());
+		ws->writeByte(0);
 	}
 
 	msg = Common::String::format("%u %d %d %d %d %d\n",
 								 _bitFlags, (_bitFlags & (1 << DARKBIT)) ? 1 : 0,
 								 MY_LOC, _currentCounter, _savedRoom, _gameHeader._lightTime);
-	glk_put_string_stream(file, msg.c_str());
+	ws->write(msg.c_str(), msg.size());
+	ws->writeByte(0);
 
 	for (int ct = 0; ct <= _gameHeader._numItems; ct++) {
 		msg = Common::String::format("%hd\n", (short)_items[ct]._location);
-		glk_put_string_stream(file, msg.c_str());
+		ws->write(msg.c_str(), msg.size());
+		ws->writeByte(0);
 	}
 
 	output(_("Saved.\n"));
 	return Common::kNoError;
 }
 
-Common::Error Scott::loadGameData(strid_t file) {
-	char buf[128];
+Common::Error Scott::readSaveData(Common::SeekableReadStream *rs) {
+	Common::String line;
 	int ct = 0;
 	short lo;
 	short darkFlag;
 
 	for (ct = 0; ct < 16; ct++) {
-		glk_get_line_stream(file, buf, sizeof buf);
-		sscanf(buf, "%d %d", &_counters[ct], &_roomSaved[ct]);
+		line = QuetzalReader::readString(rs);
+		sscanf(line.c_str(), "%d %d", &_counters[ct], &_roomSaved[ct]);
 	}
 
-	glk_get_line_stream(file, buf, sizeof buf);
-	sscanf(buf, "%u %hd %d %d %d %d\n",
+	line = QuetzalReader::readString(rs);
+	sscanf(line.c_str(), "%u %hd %d %d %d %d\n",
 		   &_bitFlags, &darkFlag, &MY_LOC, &_currentCounter, &_savedRoom,
 		   &_gameHeader._lightTime);
 
@@ -550,8 +554,8 @@ Common::Error Scott::loadGameData(strid_t file) {
 	if (darkFlag)
 		_bitFlags |= (1 << 15);
 	for (ct = 0; ct <= _gameHeader._numItems; ct++) {
-		glk_get_line_stream(file, buf, sizeof buf);
-		sscanf(buf, "%hd\n", &lo);
+		line = QuetzalReader::readString(rs);
+		sscanf(line.c_str(), "%hd\n", &lo);
 		_items[ct]._location = (unsigned char)lo;
 	}
 
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 4739e94..b7527fe 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -177,14 +177,15 @@ public:
 	virtual void runGame() override;
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t file) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
 } // End of namespace Scott
diff --git a/engines/glk/tads/tads.cpp b/engines/glk/tads/tads.cpp
index 6ce2915..fc598db 100644
--- a/engines/glk/tads/tads.cpp
+++ b/engines/glk/tads/tads.cpp
@@ -42,14 +42,14 @@ bool TADS::hasFeature(EngineFeature f) const {
 	return GlkAPI::hasFeature(f);
 }
 
-Common::Error TADS::loadGameData(strid_t file) {
+Common::Error TADS::readSaveData(Common::SeekableReadStream *rs) {
 	// TODO
-	return Common::kNoError;
+	return Common::kReadingFailed;
 }
 
-Common::Error TADS::saveGameData(strid_t file, const Common::String &desc) {
+Common::Error TADS::writeGameData(Common::WriteStream *ws) {
 	// TODO
-	return Common::kNoError;
+	return Common::kWritingFailed;
 }
 
 } // End of namespace TADS
diff --git a/engines/glk/tads/tads.h b/engines/glk/tads/tads.h
index 6ad8780..f30cf9f 100644
--- a/engines/glk/tads/tads.h
+++ b/engines/glk/tads/tads.h
@@ -54,14 +54,15 @@ public:
 	virtual bool hasFeature(EngineFeature f) const override;
 
 	/**
-	 * Load a savegame from the passed stream
+	 * Load a savegame from the passed Quetzal file chunk stream
 	 */
-	virtual Common::Error loadGameData(strid_t file) override;
+	virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
 
 	/**
-	 * Save the game to the passed stream
+	 * Save the game. The passed write stream represents access to the UMem chunk
+	 * in the Quetzal save file that will be created
 	 */
-	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+	virtual Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
 extern TADS *g_vm;


Commit: 919670a565ca38162346beb77414562d6126273a
    https://github.com/scummvm/scummvm/commit/919670a565ca38162346beb77414562d6126273a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: ADVSYS: Save/load fixes

Changed paths:
    engines/glk/advsys/advsys.cpp
    engines/glk/glk.cpp
    engines/glk/quetzal.cpp
    engines/glk/streams.cpp


diff --git a/engines/glk/advsys/advsys.cpp b/engines/glk/advsys/advsys.cpp
index 5100320..ff0bf62 100644
--- a/engines/glk/advsys/advsys.cpp
+++ b/engines/glk/advsys/advsys.cpp
@@ -103,6 +103,9 @@ bool AdvSys::singleAction() {
 }
 
 Common::Error AdvSys::readSaveData(Common::SeekableReadStream *rs) {
+	if ((int)rs->size() != _saveSize)
+		return Common::kReadingFailed;
+
 	rs->read(_saveArea, rs->size());
 	return Common::kNoError;
 }
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index b3760ca..d154de2 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -190,11 +190,21 @@ Common::Error GlkEngine::loadGameState(int slot) {
 		// so if present we can validate the save is for this game
 		for (QuetzalReader::Iterator it = r.begin(); it != r.end(); ++it) {
 			if ((*it)._id == ID_SCVM) {
+				// Skip over date/time & playtime
+				Common::SeekableReadStream *rs = it.getStream();
+				rs->skip(14);
 
+				byte interpType = rs->readByte();
+				byte language = rs->readByte();
+				Common::String md5 = QuetzalReader::readString(rs);
+				delete rs;
+
+				if (interpType != getInterpreterType() || language != getLanguage() || md5 != getGameMD5())
+					errCode = Common::kReadingFailed;
 			}
 		}
 
-		if (errCode != Common::kNoError) {
+		if (errCode == Common::kNoError) {
 			// Scan for an uncompressed memory chunk
 			errCode = Common::kReadingFailed;		// Presume we won't find chunk
 			for (QuetzalReader::Iterator it = r.begin(); it != r.end(); ++it) {
@@ -202,6 +212,7 @@ Common::Error GlkEngine::loadGameState(int slot) {
 					Common::SeekableReadStream *rs = it.getStream();
 					errCode = readSaveData(rs).getCode();
 					delete rs;
+					break;
 				}
 			}
 		}
@@ -228,7 +239,7 @@ Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
 		errCode = writeGameData(&ws).getCode();
 	}
 
-	if (errCode != Common::kNoError) {
+	if (errCode == Common::kNoError) {
 		w.save(*file, desc);
 	}
 
diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp
index ae81d88..69b0475 100644
--- a/engines/glk/quetzal.cpp
+++ b/engines/glk/quetzal.cpp
@@ -45,10 +45,8 @@ bool QuetzalReader::open(Common::SeekableReadStream *stream, uint32 formType) {
 	uint32 size = stream->readUint32BE();
 	uint32 fileFormType = stream->readUint32BE();
 
-	if (formType != ID_IFSF)
-		return false;
 	if ((formType != 0 && fileFormType != formType) ||
-		(formType == 0 && (fileFormType == ID_IFZS || fileFormType == ID_IFSF)))
+		(formType == 0 && fileFormType != ID_IFZS && fileFormType != ID_IFSF))
 		return false;
 
 	if ((int)size > stream->size() || (size & 1) || (size < 4))
@@ -112,7 +110,6 @@ bool QuetzalReader::getSavegameMetaInfo(Common::SeekableReadStream *rs, SaveStat
 			ssd.setDescription(readString(s));
 			delete s;
 
-			return true;
 		} else if ((*it)._id == ID_SCVM) {
 			Common::SeekableReadStream *s = it.getStream();
 			int year = s->readUint16BE();
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index be8ae24..4d517a2 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -781,7 +781,7 @@ FileStream::FileStream(Streams *streams, frefid_t fref, uint fmode, uint rock, b
 	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
 
 	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
-		_outFile = g_system->getSavefileManager()->openForSaving(fname, fref->_slotNumber != -1 && g_vm->getInterpreterType() != INTERPRETER_FROTZ);
+		_outFile = g_system->getSavefileManager()->openForSaving(fname, false);
 		if (!_outFile)
 			error("Could open file for writing - %s", fname.c_str());
 


Commit: 611bea7d735df4e318001a6fb3cc5c398fc7dcf6
    https://github.com/scummvm/scummvm/commit/611bea7d735df4e318001a6fb3cc5c398fc7dcf6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: ADVSYS: Fix savegame area setup

Changed paths:
    engines/glk/advsys/advsys.cpp
    engines/glk/advsys/game.cpp


diff --git a/engines/glk/advsys/advsys.cpp b/engines/glk/advsys/advsys.cpp
index ff0bf62..4e2451e 100644
--- a/engines/glk/advsys/advsys.cpp
+++ b/engines/glk/advsys/advsys.cpp
@@ -97,13 +97,16 @@ bool AdvSys::singleAction() {
 		if (execute(_afterOffset) == ABORT)
 			return false;
 		break;
+
+	default:
+		break;
 	}
 
 	return true;
 }
 
 Common::Error AdvSys::readSaveData(Common::SeekableReadStream *rs) {
-	if ((int)rs->size() != _saveSize)
+	if (rs->size() != (int)_saveSize)
 		return Common::kReadingFailed;
 
 	rs->read(_saveArea, rs->size());
diff --git a/engines/glk/advsys/game.cpp b/engines/glk/advsys/game.cpp
index d051a35..eca1451 100644
--- a/engines/glk/advsys/game.cpp
+++ b/engines/glk/advsys/game.cpp
@@ -28,8 +28,8 @@ namespace Glk {
 namespace AdvSys {
 
 void Decrypter::decrypt(byte *data, size_t size) {
-	for (; --size; ++data)
-		*data = ~(*data + 30);
+	for (size_t idx = 0; idx < size; ++idx)
+		*data++ = ~(*data + 30);
 }
 
 /*--------------------------------------------------------------------------*/


Commit: a9261a6d315a452dfb63ead326aa72fcc3075ae9
    https://github.com/scummvm/scummvm/commit/a9261a6d315a452dfb63ead326aa72fcc3075ae9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-06-16T14:59:26-07:00

Commit Message:
GLK: ADVSYS: Do a look action after loading a savegame from launcher

Changed paths:
    engines/glk/advsys/advsys.cpp
    engines/glk/advsys/glk_interface.cpp
    engines/glk/advsys/glk_interface.h


diff --git a/engines/glk/advsys/advsys.cpp b/engines/glk/advsys/advsys.cpp
index 4e2451e..a61033a 100644
--- a/engines/glk/advsys/advsys.cpp
+++ b/engines/glk/advsys/advsys.cpp
@@ -46,6 +46,8 @@ void AdvSys::runGame() {
 			_saveSlot = -1;
 			if (err != Common::kNoError)
 				print(_("Sorry, the savegame couldn't be restored"));
+			else
+				_pendingLine = "look";		// Do a look action after loading the savegame
 		}
 
 		// Gameplay loop
diff --git a/engines/glk/advsys/glk_interface.cpp b/engines/glk/advsys/glk_interface.cpp
index 20f9f4d..c49dc98 100644
--- a/engines/glk/advsys/glk_interface.cpp
+++ b/engines/glk/advsys/glk_interface.cpp
@@ -47,6 +47,17 @@ Common::String GlkInterface::readLine() {
 	char line[200];
 
 	print(": ");
+
+	if (!_pendingLine.empty()) {
+		// The next input line has been manually provided, so return it
+		print(_pendingLine);
+		print("\n");
+
+		Common::String l = _pendingLine;
+		_pendingLine = "";
+		return l;
+	}
+
 	glk_request_line_event(_window, line, 199, 0);
 
 	do {
diff --git a/engines/glk/advsys/glk_interface.h b/engines/glk/advsys/glk_interface.h
index 9048818..9e37207 100644
--- a/engines/glk/advsys/glk_interface.h
+++ b/engines/glk/advsys/glk_interface.h
@@ -37,6 +37,7 @@ private:
 	winid_t _window;
 protected:
 	int _saveSlot;
+	Common::String _pendingLine;
 protected:
 	/**
 	 * GLK initialization





More information about the Scummvm-git-logs mailing list