[Scummvm-cvs-logs] scummvm master -> 504ffd2aba0fcb67216242475cff4bbf54650a96

bgK bastien.bouclet at gmail.com
Sun Jul 10 22:03:08 CEST 2016


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

Summary:
c06e347d90 MOHAWK: Make the Riven saved games loadable by the original engine
792548f28f MOHAWK: The French DVD version of Riven is just a repackaged CD version
f78bb08b18 MOHAWK: Save ScummVM specific metadata in the Riven saves
504ffd2aba MOHAWK: Switch Riven saves to a slot based naming scheme


Commit: c06e347d90687c4549b18f158f087af878cc4636
    https://github.com/scummvm/scummvm/commit/c06e347d90687c4549b18f158f087af878cc4636
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2016-07-10T22:01:02+02:00

Commit Message:
MOHAWK: Make the Riven saved games loadable by the original engine

Changed paths:
    engines/mohawk/riven_saveload.cpp



diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 6af66f7..7bb8582 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -72,8 +72,11 @@ Common::Error RivenSaveLoad::loadGame(Common::String filename) {
 	Common::Array<uint32> rawVariables;
 
 	while (!vars->eos()) {
-		vars->readUint32BE();	// Unknown (Stack?)
-		vars->readUint32BE();	// Unknown (0 or 1)
+		// The original engine stores the variables values in an array. All the slots in
+		// the array may not be in use, which is why it needs a reference counter and
+		// a flag to tell if the value has been set.
+		vars->readUint32BE();	// Reference counter
+		vars->readUint32BE();	// Variable initialized flag
 		rawVariables.push_back(vars->readUint32BE());
 	}
 
@@ -162,14 +165,18 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genVARSSection() {
 	Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
 
 	for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
-		stream->writeUint32BE(0); // Unknown
-		stream->writeUint32BE(0); // Unknown
+		stream->writeUint32BE(1); // Reference counter
+		stream->writeUint32BE(1); // Variable initialized flag
 		stream->writeUint32BE(it->_value);
 	}
 
 	return stream;
 }
 
+static int stringCompareToIgnoreCase(const Common::String &s1, const Common::String &s2) {
+	return s1.compareToIgnoreCase(s2) < 0;
+}
+
 Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
 	Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
 
@@ -181,8 +188,28 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
 		curPos += it->_key.size() + 1;
 	}
 
-	for (uint16 i = 0; i < _vm->_vars.size(); i++)
-		stream->writeUint16BE(i);
+	// The original engine does not store the variables in a HashMap, but in a "NameList"
+	// for the keys and an array for the values. The NameList data structure maintains an array
+	// of indices in the string table sorted by case insensitive key alphabetical order.
+	// It is used to perform fast key -> index lookups.
+	// ScummVM does not need the sorted array, but has to write it anyway for the saved games
+	// to be compatible with original engine.
+	Common::Array<Common::String> sortedKeys;
+	for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
+		sortedKeys.push_back(it->_key);
+	}
+	Common::sort(sortedKeys.begin(), sortedKeys.end(), stringCompareToIgnoreCase);
+
+	for (uint i = 0; i < sortedKeys.size(); i++) {
+		uint16 varIndex = 0;
+		for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
+			if (it->_key == sortedKeys[i]) {
+				stream->writeUint16BE(varIndex);
+				break;
+			}
+			varIndex++;
+		}
+	}
 
 	for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
 		stream->write(it->_key.c_str(), it->_key.size());
@@ -214,10 +241,6 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	// games need this, we should think about coming up with some
 	// more common way of outputting resources to an archive.
 
-	// TODO: Make these saves work with the original interpreter.
-	// Not sure why they don't work yet (they still can be loaded
-	// by ScummVM).
-
 	// Make sure we have the right extension
 	if (!filename.matchString("*.rvn", true))
 		filename += ".rvn";
@@ -262,15 +285,17 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	saveFile->writeUint16BE(4); // 4 Type Table Entries
 
 	// Hardcode Entries (32 bytes - total: 64)
-	saveFile->writeUint32BE(ID_VERS);
+	// The original engine relies on the entries being sorted by tag alphabetical order
+	// to optimize its lookup algorithm.
+	saveFile->writeUint32BE(ID_NAME);
 	saveFile->writeUint16BE(46); // Resource table offset
 	saveFile->writeUint16BE(38); // String table offset
 
-	saveFile->writeUint32BE(ID_NAME);
+	saveFile->writeUint32BE(ID_VARS);
 	saveFile->writeUint16BE(52);
 	saveFile->writeUint16BE(40);
 
-	saveFile->writeUint32BE(ID_VARS);
+	saveFile->writeUint32BE(ID_VERS);
 	saveFile->writeUint16BE(58);
 	saveFile->writeUint16BE(42);
 
@@ -281,23 +306,23 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	// Pseudo-String Table (2 bytes - total: 66)
 	saveFile->writeUint16BE(0); // We don't need a name list
 
-	// Psuedo-Name Tables (8 bytes - total: 74)
+	// Pseudo-Name Tables (8 bytes - total: 74)
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 
-	// VERS Section (Resource Table) (6 bytes - total: 80)
+	// NAME Section (Resource Table) (6 bytes - total: 80)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 
-	// NAME Section (Resource Table) (6 bytes - total: 86)
+	// VARS Section (Resource Table) (6 bytes - total: 86)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(2);
 
-	// VARS Section (Resource Table) (6 bytes - total: 92)
+	// VERS Section (Resource Table) (6 bytes - total: 92)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(3);
@@ -310,37 +335,37 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	// File Table (4 bytes - total: 102)
 	saveFile->writeUint32BE(4);
 
-	// VERS Section (File Table) (10 bytes - total: 112)
+	// NAME Section (File Table) (10 bytes - total: 112)
 	saveFile->writeUint32BE(142);
-	saveFile->writeUint16BE(versSection->size() & 0xFFFF);
-	saveFile->writeByte((versSection->size() & 0xFF0000) >> 16);
-	saveFile->writeByte(0);
-	saveFile->writeUint16BE(0);
-
-	// NAME Section (File Table) (10 bytes - total: 122)
-	saveFile->writeUint32BE(142 + versSection->size());
 	saveFile->writeUint16BE(nameSection->size() & 0xFFFF);
 	saveFile->writeByte((nameSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
-	// VARS Section (File Table) (10 bytes - total: 132)
-	saveFile->writeUint32BE(142 + versSection->size() + nameSection->size());
+	// VARS Section (File Table) (10 bytes - total: 122)
+	saveFile->writeUint32BE(142 + nameSection->size());
 	saveFile->writeUint16BE(varsSection->size() & 0xFFFF);
 	saveFile->writeByte((varsSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
+	// VERS Section (File Table) (10 bytes - total: 132)
+	saveFile->writeUint32BE(142 + nameSection->size() + varsSection->size());
+	saveFile->writeUint16BE(versSection->size() & 0xFFFF);
+	saveFile->writeByte((versSection->size() & 0xFF0000) >> 16);
+	saveFile->writeByte(0);
+	saveFile->writeUint16BE(0);
+
 	// ZIPS Section (File Table) (10 bytes - total: 142)
-	saveFile->writeUint32BE(142 + versSection->size() + nameSection->size() + varsSection->size());
+	saveFile->writeUint32BE(142 + nameSection->size() + varsSection->size() + versSection->size());
 	saveFile->writeUint16BE(zipsSection->size() & 0xFFFF);
 	saveFile->writeByte((zipsSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
-	saveFile->write(versSection->getData(), versSection->size());
 	saveFile->write(nameSection->getData(), nameSection->size());
 	saveFile->write(varsSection->getData(), varsSection->size());
+	saveFile->write(versSection->getData(), versSection->size());
 	saveFile->write(zipsSection->getData(), zipsSection->size());
 
 	saveFile->finalize();


Commit: 792548f28fd0529077f01a56a026892dfcdcb7b0
    https://github.com/scummvm/scummvm/commit/792548f28fd0529077f01a56a026892dfcdcb7b0
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2016-07-10T22:01:02+02:00

Commit Message:
MOHAWK: The French DVD version of Riven is just a repackaged CD version

Changed paths:
    engines/mohawk/detection_tables.h



diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index e3eab89..d9aba26 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -337,6 +337,24 @@ static const MohawkGameDescription gameDescriptions[] = {
 	},
 
 	// Riven: The Sequel to Myst
+	// Version 1.0 (5CD), 1.02 (DVD, From "Myst: La Trilogie")
+	// From gamin
+	{
+			{
+					"riven",
+					"",
+					AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
+					Common::FR_FRA,
+					Common::kPlatformWindows,
+					ADGF_UNSTABLE,
+					GUIO1(GUIO_NOASPECT)
+			},
+			GType_RIVEN,
+			0,
+			0,
+	},
+
+	// Riven: The Sequel to Myst
 	// Version 1.0 (5CD) - Italian
 	// From dodomorandi on bug #6629
 	{
@@ -425,24 +443,6 @@ static const MohawkGameDescription gameDescriptions[] = {
 	},
 
 	// Riven: The Sequel to Myst
-	// Version ? (DVD, From "Myst: La Trilogie")
-	// From gamin
-	{
-		{
-			"riven",
-			"",
-			AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"),
-			Common::FR_FRA,
-			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
-			GUIO1(GUIO_NOASPECT)
-		},
-		GType_RIVEN,
-		GF_DVD,
-		0,
-	},
-
-	// Riven: The Sequel to Myst
 	// Version 1.02 (DVD, From "Myst: Antologia")
 	// From pykman
 	{


Commit: f78bb08b1850b349f28b3cb5f1357fdebd5b8e00
    https://github.com/scummvm/scummvm/commit/f78bb08b1850b349f28b3cb5f1357fdebd5b8e00
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2016-07-10T22:01:03+02:00

Commit Message:
MOHAWK: Save ScummVM specific metadata in the Riven saves

- Thumbnail
- Save date
- Save description
- Total play time

Changed paths:
    engines/mohawk/detection.cpp
    engines/mohawk/resource.h
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_saveload.h



diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 7c20299..e1aac64 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -41,6 +41,7 @@
 
 #ifdef ENABLE_RIVEN
 #include "mohawk/riven.h"
+#include "mohawk/riven_saveload.h"
 #endif
 
 namespace Mohawk {
@@ -240,14 +241,18 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
 		}
 
 		Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
-	} else
+	}
 #endif
+#ifdef ENABLE_RIVEN
 	if (strstr(target, "riven")) {
 		filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
 
-		for (uint32 i = 0; i < filenames.size(); i++)
-			saveList.push_back(SaveStateDescriptor(i, filenames[i]));
+		for (uint32 i = 0; i < filenames.size(); i++) {
+			Common::String description = Mohawk::RivenSaveLoad::querySaveDescription(filenames[i]);
+			saveList.push_back(SaveStateDescriptor(i, description));
+		}
 	}
+#endif
 
 	return saveList;
 }
@@ -270,6 +275,12 @@ SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int
 #ifdef ENABLE_MYST
 	if (strstr(target, "myst")) {
 		return Mohawk::MystGameState::querySaveMetaInfos(slot);
+	}
+#endif
+#ifdef ENABLE_RIVEN
+	if (strstr(target, "riven")) {
+		Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
+		return Mohawk::RivenSaveLoad::querySaveMetaInfos(filenames[slot].c_str());
 	} else
 #endif
 	{
diff --git a/engines/mohawk/resource.h b/engines/mohawk/resource.h
index d9074a5..12c5a13 100644
--- a/engines/mohawk/resource.h
+++ b/engines/mohawk/resource.h
@@ -68,6 +68,8 @@ namespace Mohawk {
 #define ID_VARS MKTAG('V','A','R','S') // Variable Values
 #define ID_VERS MKTAG('V','E','R','S') // Version Info
 #define ID_ZIPS MKTAG('Z','I','P','S') // Zip Mode Status
+#define ID_META MKTAG('M','E','T','A') // ScummVM save metadata
+#define ID_THMB MKTAG('T','H','M','B') // ScummVM save thumbnail
 
 // Zoombini Resource FourCC's
 #define ID_SND  MKTAG( 0 ,'S','N','D') // Standard Mohawk Sound
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 7bb8582..4d52179 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -24,10 +24,38 @@
 #include "mohawk/riven.h"
 #include "mohawk/riven_saveload.h"
 
-#include "common/util.h"
+#include "common/system.h"
+#include "graphics/thumbnail.h"
 
 namespace Mohawk {
 
+RivenSaveMetadata::RivenSaveMetadata() {
+	saveDay = 0;
+	saveMonth = 0;
+	saveYear = 0;
+	saveHour = 0;
+	saveMinute = 0;
+	totalPlayTime = 0;
+}
+
+bool RivenSaveMetadata::sync(Common::Serializer &s) {
+	static const Common::Serializer::Version kCurrentVersion = 1;
+
+	if (!s.syncVersion(kCurrentVersion)) {
+		return false;
+	}
+
+	s.syncAsByte(saveDay);
+	s.syncAsByte(saveMonth);
+	s.syncAsUint16BE(saveYear);
+	s.syncAsByte(saveHour);
+	s.syncAsByte(saveMinute);
+	s.syncString(saveDescription);
+	s.syncAsUint32BE(totalPlayTime);
+
+	return true;
+}
+
 RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) {
 }
 
@@ -38,11 +66,96 @@ Common::StringArray RivenSaveLoad::generateSaveGameList() {
 	return _saveFileMan->listSavefiles("*.rvn");
 }
 
+Common::String RivenSaveLoad::querySaveDescription(const Common::String &filename) {
+	Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
+	if (!loadFile) {
+		return "";
+	}
+
+	MohawkArchive mhk;
+	if (!mhk.openStream(loadFile)) {
+		return "";
+	}
+
+	if (!mhk.hasResource(ID_META, 1)) {
+		return "";
+	}
+
+	Common::SeekableReadStream *metaStream = mhk.getResource(ID_META, 1);
+	if (!metaStream) {
+		return "";
+	}
+
+	Common::Serializer serializer = Common::Serializer(metaStream, nullptr);
+
+	RivenSaveMetadata metadata;
+	if (!metadata.sync(serializer)) {
+		delete metaStream;
+		return "";
+	}
+
+	delete metaStream;
+
+	return metadata.saveDescription;
+}
+
+SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const Common::String &filename) {
+	Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
+	if (!loadFile) {
+		return SaveStateDescriptor();
+	}
+
+	MohawkArchive mhk;
+	if (!mhk.openStream(loadFile)) {
+		return SaveStateDescriptor();
+	}
+
+	if (!mhk.hasResource(ID_META, 1)) {
+		return SaveStateDescriptor();
+	}
+
+	Common::SeekableReadStream *metaStream = mhk.getResource(ID_META, 1);
+	if (!metaStream) {
+		return SaveStateDescriptor();
+	}
+
+	Common::Serializer serializer = Common::Serializer(metaStream, nullptr);
+
+	RivenSaveMetadata metadata;
+	if (!metadata.sync(serializer)) {
+		delete metaStream;
+		return SaveStateDescriptor();
+	}
+
+	SaveStateDescriptor descriptor;
+	descriptor.setDescription(metadata.saveDescription);
+	descriptor.setPlayTime(metadata.totalPlayTime);
+	descriptor.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay);
+	descriptor.setSaveTime(metadata.saveHour, metadata.saveMinute);
+
+	delete metaStream;
+
+	if (!mhk.hasResource(ID_THMB, 1)) {
+		return descriptor;
+	}
+
+	Common::SeekableReadStream *thmbStream = mhk.getResource(ID_THMB, 1);
+	if (!thmbStream) {
+		return descriptor;
+	}
+
+	descriptor.setThumbnail(Graphics::loadThumbnail(*thmbStream));
+
+	delete thmbStream;
+
+	return descriptor;
+}
+
 Common::Error RivenSaveLoad::loadGame(Common::String filename) {
 	if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
 		return Common::kNoError;
 
-	Common::InSaveFile *loadFile =  _saveFileMan->openForLoading(filename);
+	Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
 	if (!loadFile)
 		return Common::kReadingFailed;
 
@@ -147,6 +260,20 @@ Common::Error RivenSaveLoad::loadGame(Common::String filename) {
 	}
 
 	delete zips;
+
+	// Load the ScummVM specific save metadata
+	if (mhk->hasResource(ID_META, 1)) {
+		Common::SeekableReadStream *metadataStream = mhk->getResource(ID_META, 1);
+		Common::Serializer serializer = Common::Serializer(metadataStream, nullptr);
+
+		RivenSaveMetadata metadata;
+		metadata.sync(serializer);
+
+		// Set the saved total play time
+		_vm->setTotalPlayTime(metadata.totalPlayTime);
+
+		delete metadataStream;
+	}
 	delete mhk;
 
 	return Common::kNoError;
@@ -233,6 +360,34 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genZIPSSection() {
 	return stream;
 }
 
+Common::MemoryWriteStreamDynamic *RivenSaveLoad::genTHMBSection() const {
+	Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+
+	Graphics::saveThumbnail(*stream);
+
+	return stream;
+}
+
+Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::String &desc) const {
+	Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+	Common::Serializer serializer = Common::Serializer(nullptr, stream);
+
+	TimeDate t;
+	_vm->_system->getTimeAndDate(t);
+
+	RivenSaveMetadata metadata;
+	metadata.saveDay = t.tm_mday;
+	metadata.saveMonth = t.tm_mon + 1;
+	metadata.saveYear = t.tm_year + 1900;
+	metadata.saveHour = t.tm_hour;
+	metadata.saveMinute = t.tm_min;
+	metadata.saveDescription = desc;
+	metadata.totalPlayTime = _vm->getTotalPlayTime();
+	metadata.sync(serializer);
+
+	return stream;
+}
+
 Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	// NOTE: This code is designed to only output a Mohawk archive
 	// for a Riven saved game. It's hardcoded to do this because
@@ -255,16 +410,20 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 
 	debug (0, "Saving game to \'%s\'", filename.c_str());
 
-	Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
+	Common::MemoryWriteStreamDynamic *metaSection = genMETASection(filename);
 	Common::MemoryWriteStreamDynamic *nameSection = genNAMESection();
+	Common::MemoryWriteStreamDynamic *thmbSection = genTHMBSection();
 	Common::MemoryWriteStreamDynamic *varsSection = genVARSSection();
+	Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
 	Common::MemoryWriteStreamDynamic *zipsSection = genZIPSSection();
 
 	// Let's calculate the file size!
-	uint32 fileSize = 142;
-	fileSize += versSection->size();
+	uint32 fileSize = 194;
+	fileSize += metaSection->size();
 	fileSize += nameSection->size();
+	fileSize += thmbSection->size();
 	fileSize += varsSection->size();
+	fileSize += versSection->size();
 	fileSize += zipsSection->size();
 
 	// MHWK Header (8 bytes - total: 8)
@@ -277,93 +436,129 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	saveFile->writeUint16BE(1); // Compaction -- original saves have this too
 	saveFile->writeUint32BE(fileSize); // Subtract off the MHWK header size
 	saveFile->writeUint32BE(28); // Absolute offset: right after both headers
-	saveFile->writeUint16BE(70); // File Table Offset
-	saveFile->writeUint16BE(44); // File Table Size (4 bytes count + 4 entries * 10 bytes per entry)
+	saveFile->writeUint16BE(102); // File Table Offset
+	saveFile->writeUint16BE(64); // File Table Size (4 bytes count + 6 entries * 10 bytes per entry)
 
 	// Type Table (4 bytes - total: 32)
-	saveFile->writeUint16BE(36); // String table offset After the Type Table Entries
-	saveFile->writeUint16BE(4); // 4 Type Table Entries
+	saveFile->writeUint16BE(52); // String table offset After the Type Table Entries
+	saveFile->writeUint16BE(6); // 6 Type Table Entries
 
-	// Hardcode Entries (32 bytes - total: 64)
+	// Hardcode Entries (48 bytes - total: 80)
 	// The original engine relies on the entries being sorted by tag alphabetical order
 	// to optimize its lookup algorithm.
+	saveFile->writeUint32BE(ID_META);
+	saveFile->writeUint16BE(66); // Resource table offset
+	saveFile->writeUint16BE(54); // String table offset
+
 	saveFile->writeUint32BE(ID_NAME);
-	saveFile->writeUint16BE(46); // Resource table offset
-	saveFile->writeUint16BE(38); // String table offset
+	saveFile->writeUint16BE(72);
+	saveFile->writeUint16BE(56);
+
+	saveFile->writeUint32BE(ID_THMB);
+	saveFile->writeUint16BE(78);
+	saveFile->writeUint16BE(58);
 
 	saveFile->writeUint32BE(ID_VARS);
-	saveFile->writeUint16BE(52);
-	saveFile->writeUint16BE(40);
+	saveFile->writeUint16BE(84);
+	saveFile->writeUint16BE(60);
 
 	saveFile->writeUint32BE(ID_VERS);
-	saveFile->writeUint16BE(58);
-	saveFile->writeUint16BE(42);
+	saveFile->writeUint16BE(90);
+	saveFile->writeUint16BE(62);
 
 	saveFile->writeUint32BE(ID_ZIPS);
+	saveFile->writeUint16BE(96);
 	saveFile->writeUint16BE(64);
-	saveFile->writeUint16BE(44);
 
-	// Pseudo-String Table (2 bytes - total: 66)
+	// Pseudo-String Table (2 bytes - total: 82)
 	saveFile->writeUint16BE(0); // We don't need a name list
 
-	// Pseudo-Name Tables (8 bytes - total: 74)
+	// Pseudo-Name Tables (12 bytes - total: 94)
+	saveFile->writeUint16BE(0);
+	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 	saveFile->writeUint16BE(0);
 
-	// NAME Section (Resource Table) (6 bytes - total: 80)
+	// META Section (Resource Table) (6 bytes - total: 100)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 
-	// VARS Section (Resource Table) (6 bytes - total: 86)
+	// NAME Section (Resource Table) (6 bytes - total: 106)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(2);
 
-	// VERS Section (Resource Table) (6 bytes - total: 92)
+	// THMB Section (Resource Table) (6 bytes - total: 112)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(3);
 
-	// ZIPS Section (Resource Table) (6 bytes - total: 98)
+	// VARS Section (Resource Table) (6 bytes - total: 118)
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(1);
 	saveFile->writeUint16BE(4);
 
-	// File Table (4 bytes - total: 102)
-	saveFile->writeUint32BE(4);
+	// VERS Section (Resource Table) (6 bytes - total: 124)
+	saveFile->writeUint16BE(1);
+	saveFile->writeUint16BE(1);
+	saveFile->writeUint16BE(5);
 
-	// NAME Section (File Table) (10 bytes - total: 112)
-	saveFile->writeUint32BE(142);
+	// ZIPS Section (Resource Table) (6 bytes - total: 130)
+	saveFile->writeUint16BE(1);
+	saveFile->writeUint16BE(1);
+	saveFile->writeUint16BE(6);
+
+	// File Table (4 bytes - total: 134)
+	saveFile->writeUint32BE(6);
+
+	// META Section (File Table) (10 bytes - total: 144)
+	saveFile->writeUint32BE(194);
+	saveFile->writeUint16BE(metaSection->size() & 0xFFFF);
+	saveFile->writeByte((metaSection->size() & 0xFF0000) >> 16);
+	saveFile->writeByte(0);
+	saveFile->writeUint16BE(0);
+
+	// NAME Section (File Table) (10 bytes - total: 154)
+	saveFile->writeUint32BE(194 + metaSection->size());
 	saveFile->writeUint16BE(nameSection->size() & 0xFFFF);
 	saveFile->writeByte((nameSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
-	// VARS Section (File Table) (10 bytes - total: 122)
-	saveFile->writeUint32BE(142 + nameSection->size());
+	// THMB Section (File Table) (10 bytes - total: 164)
+	saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size());
+	saveFile->writeUint16BE(thmbSection->size() & 0xFFFF);
+	saveFile->writeByte((thmbSection->size() & 0xFF0000) >> 16);
+	saveFile->writeByte(0);
+	saveFile->writeUint16BE(0);
+
+	// VARS Section (File Table) (10 bytes - total: 174)
+	saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size());
 	saveFile->writeUint16BE(varsSection->size() & 0xFFFF);
 	saveFile->writeByte((varsSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
-	// VERS Section (File Table) (10 bytes - total: 132)
-	saveFile->writeUint32BE(142 + nameSection->size() + varsSection->size());
+	// VERS Section (File Table) (10 bytes - total: 184)
+	saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size() + varsSection->size());
 	saveFile->writeUint16BE(versSection->size() & 0xFFFF);
 	saveFile->writeByte((versSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
-	// ZIPS Section (File Table) (10 bytes - total: 142)
-	saveFile->writeUint32BE(142 + nameSection->size() + varsSection->size() + versSection->size());
+	// ZIPS Section (File Table) (10 bytes - total: 194)
+	saveFile->writeUint32BE(194 + metaSection->size() + nameSection->size() + thmbSection->size() + varsSection->size() + versSection->size());
 	saveFile->writeUint16BE(zipsSection->size() & 0xFFFF);
 	saveFile->writeByte((zipsSection->size() & 0xFF0000) >> 16);
 	saveFile->writeByte(0);
 	saveFile->writeUint16BE(0);
 
+	saveFile->write(metaSection->getData(), metaSection->size());
 	saveFile->write(nameSection->getData(), nameSection->size());
+	saveFile->write(thmbSection->getData(), thmbSection->size());
 	saveFile->write(varsSection->getData(), varsSection->size());
 	saveFile->write(versSection->getData(), versSection->size());
 	saveFile->write(zipsSection->getData(), zipsSection->size());
@@ -371,9 +566,11 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	saveFile->finalize();
 
 	delete saveFile;
-	delete versSection;
+	delete metaSection;
 	delete nameSection;
+	delete thmbSection;
 	delete varsSection;
+	delete versSection;
 	delete zipsSection;
 
 	return Common::kNoError;
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index a6ddce5..2ef4326 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -23,10 +23,13 @@
 #ifndef MOHAWK_SAVELOAD_H
 #define MOHAWK_SAVELOAD_H
 
+#include "common/serializer.h"
 #include "common/savefile.h"
 #include "common/str.h"
 #include "common/memstream.h"
 
+#include "engines/savestate.h"
+
 namespace Mohawk {
 
 class MohawkEngine_Riven;
@@ -36,6 +39,22 @@ enum {
 	kDVDSaveGameVersion = 0x00010100
 };
 
+struct RivenSaveMetadata {
+	uint8 saveDay;
+	uint8 saveMonth;
+	uint16 saveYear;
+
+	uint8 saveHour;
+	uint8 saveMinute;
+
+	uint32 totalPlayTime;
+
+	Common::String saveDescription;
+
+	RivenSaveMetadata();
+	bool sync(Common::Serializer &s);
+};
+
 class RivenSaveLoad {
 public:
 	RivenSaveLoad(MohawkEngine_Riven*, Common::SaveFileManager*);
@@ -46,13 +65,18 @@ public:
 	Common::Error saveGame(Common::String);
 	void deleteSave(Common::String);
 
+	static SaveStateDescriptor querySaveMetaInfos(const Common::String &filename);
+	static Common::String querySaveDescription(const Common::String &filename);
+
 private:
 	MohawkEngine_Riven *_vm;
 	Common::SaveFileManager *_saveFileMan;
 
-	Common::MemoryWriteStreamDynamic *genVERSSection();
 	Common::MemoryWriteStreamDynamic *genNAMESection();
+	Common::MemoryWriteStreamDynamic *genMETASection(const Common::String &desc) const;
+	Common::MemoryWriteStreamDynamic *genTHMBSection() const;
 	Common::MemoryWriteStreamDynamic *genVARSSection();
+	Common::MemoryWriteStreamDynamic *genVERSSection();
 	Common::MemoryWriteStreamDynamic *genZIPSSection();
 };
 


Commit: 504ffd2aba0fcb67216242475cff4bbf54650a96
    https://github.com/scummvm/scummvm/commit/504ffd2aba0fcb67216242475cff4bbf54650a96
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2016-07-10T22:01:03+02:00

Commit Message:
MOHAWK: Switch Riven saves to a slot based naming scheme

Existing saves are compatible but must be renamed to riven-###.rvn

Changed paths:
    engines/mohawk/detection.cpp
    engines/mohawk/riven.cpp
    engines/mohawk/riven_saveload.cpp
    engines/mohawk/riven_saveload.h



diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index e1aac64..246d3ec 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -199,6 +199,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const;
 	virtual int getMaximumSaveSlot() const { return 999; }
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -215,41 +216,55 @@ bool MohawkMetaEngine::hasFeature(MetaEngineFeature f) const {
 		|| (f == kSavesSupportPlayTime);
 }
 
+SaveStateList MohawkMetaEngine::listSavesForPrefix(const char *prefix, const char *extension) const {
+	Common::String pattern = Common::String::format("%s-###.%s", prefix, extension);
+	Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
+	size_t prefixLen = strlen(prefix);
+
+	SaveStateList saveList;
+	for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+		// Extract the slot number from the filename
+		char slot[4];
+		slot[0] = (*filename)[prefixLen + 1];
+		slot[1] = (*filename)[prefixLen + 2];
+		slot[2] = (*filename)[prefixLen + 3];
+		slot[3] = '\0';
+
+		int slotNum = atoi(slot);
+
+		saveList.push_back(SaveStateDescriptor(slotNum, ""));
+	}
+
+	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+
+	return saveList;
+}
+
 SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
-	Common::StringArray filenames;
 	SaveStateList saveList;
 
 	// Loading games is only supported in Myst/Riven currently.
 #ifdef ENABLE_MYST
 	if (strstr(target, "myst")) {
-		filenames = g_system->getSavefileManager()->listSavefiles("myst-###.mys");
-		size_t prefixLen = sizeof("myst") - 1;
-
-		for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
-			// Extract the slot number from the filename
-			char slot[4];
-			slot[0] = (*filename)[prefixLen + 1];
-			slot[1] = (*filename)[prefixLen + 2];
-			slot[2] = (*filename)[prefixLen + 3];
-			slot[3] = '\0';
-
-			int slotNum = atoi(slot);
+		saveList = listSavesForPrefix("myst", "mys");
 
+		for (SaveStateList::iterator save = saveList.begin(); save != saveList.end(); ++save) {
 			// Read the description from the save
-			Common::String description = Mohawk::MystGameState::querySaveDescription(slotNum);
-			saveList.push_back(SaveStateDescriptor(slotNum, description));
+			int slot = save->getSaveSlot();
+			Common::String description = Mohawk::MystGameState::querySaveDescription(slot);
+			save->setDescription(description);
 		}
-
-		Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
 	}
 #endif
 #ifdef ENABLE_RIVEN
 	if (strstr(target, "riven")) {
-		filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
+		saveList = listSavesForPrefix("riven", "rvn");
 
-		for (uint32 i = 0; i < filenames.size(); i++) {
-			Common::String description = Mohawk::RivenSaveLoad::querySaveDescription(filenames[i]);
-			saveList.push_back(SaveStateDescriptor(i, description));
+		for (SaveStateList::iterator save = saveList.begin(); save != saveList.end(); ++save) {
+			// Read the description from the save
+			int slot = save->getSaveSlot();
+			Common::String description = Mohawk::RivenSaveLoad::querySaveDescription(slot);
+			save->setDescription(description);
 		}
 	}
 #endif
@@ -263,12 +278,13 @@ void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
 #ifdef ENABLE_MYST
 	if (strstr(target, "myst")) {
 		Mohawk::MystGameState::deleteSave(slot);
-	} else
+	}
 #endif
+#ifdef ENABLE_RIVEN
 	if (strstr(target, "riven")) {
-		Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
-		g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str());
+		Mohawk::RivenSaveLoad::deleteSave(slot);
 	}
+#endif
 }
 
 SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
@@ -279,8 +295,7 @@ SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int
 #endif
 #ifdef ENABLE_RIVEN
 	if (strstr(target, "riven")) {
-		Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
-		return Mohawk::RivenSaveLoad::querySaveMetaInfos(filenames[slot].c_str());
+		return Mohawk::RivenSaveLoad::querySaveMetaInfos(slot);
 	} else
 #endif
 	{
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 0f764ae..aa168a3 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -176,13 +176,10 @@ Common::Error MohawkEngine_Riven::run() {
 		changeToCard(6);
 	} else if (ConfMan.hasKey("save_slot")) {
 		// Load game from launcher/command line if requested
-		uint32 gameToLoad = ConfMan.getInt("save_slot");
-		Common::StringArray savedGamesList = _saveLoad->generateSaveGameList();
-		if (gameToLoad > savedGamesList.size())
-			error ("Could not find saved game");
+		int gameToLoad = ConfMan.getInt("save_slot");
 
 		// Attempt to load the game. On failure, just send us to the main menu.
-		if (_saveLoad->loadGame(savedGamesList[gameToLoad]).getCode() != Common::kNoError) {
+		if (_saveLoad->loadGame(gameToLoad).getCode() != Common::kNoError) {
 			changeToStack(kStackAspit);
 			changeToCard(1);
 		}
@@ -734,16 +731,11 @@ void MohawkEngine_Riven::runLoadDialog() {
 }
 
 Common::Error MohawkEngine_Riven::loadGameState(int slot) {
-	return _saveLoad->loadGame(_saveLoad->generateSaveGameList()[slot]);
+	return _saveLoad->loadGame(slot);
 }
 
 Common::Error MohawkEngine_Riven::saveGameState(int slot, const Common::String &desc) {
-	Common::StringArray saveList = _saveLoad->generateSaveGameList();
-
-	if ((uint)slot < saveList.size())
-		_saveLoad->deleteSave(saveList[slot]);
-
-	return _saveLoad->saveGame(desc);
+	return _saveLoad->saveGame(slot, desc);
 }
 
 Common::String MohawkEngine_Riven::getStackName(uint16 stack) const {
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 4d52179..755f877 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -62,11 +62,12 @@ RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *sa
 RivenSaveLoad::~RivenSaveLoad() {
 }
 
-Common::StringArray RivenSaveLoad::generateSaveGameList() {
-	return _saveFileMan->listSavefiles("*.rvn");
+Common::String RivenSaveLoad::buildSaveFilename(const int slot) {
+	return Common::String::format("riven-%03d.rvn", slot);
 }
 
-Common::String RivenSaveLoad::querySaveDescription(const Common::String &filename) {
+Common::String RivenSaveLoad::querySaveDescription(const int slot) {
+	Common::String filename = buildSaveFilename(slot);
 	Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
 	if (!loadFile) {
 		return "";
@@ -99,7 +100,8 @@ Common::String RivenSaveLoad::querySaveDescription(const Common::String &filenam
 	return metadata.saveDescription;
 }
 
-SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const Common::String &filename) {
+SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const int slot) {
+	Common::String filename = buildSaveFilename(slot);
 	Common::InSaveFile *loadFile = g_system->getSavefileManager()->openForLoading(filename);
 	if (!loadFile) {
 		return SaveStateDescriptor();
@@ -151,10 +153,11 @@ SaveStateDescriptor RivenSaveLoad::querySaveMetaInfos(const Common::String &file
 	return descriptor;
 }
 
-Common::Error RivenSaveLoad::loadGame(Common::String filename) {
+Common::Error RivenSaveLoad::loadGame(const int slot) {
 	if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
 		return Common::kNoError;
 
+	Common::String filename = buildSaveFilename(slot);
 	Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
 	if (!loadFile)
 		return Common::kReadingFailed;
@@ -388,7 +391,7 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genMETASection(const Common::St
 	return stream;
 }
 
-Common::Error RivenSaveLoad::saveGame(Common::String filename) {
+Common::Error RivenSaveLoad::saveGame(const int slot, const Common::String &description) {
 	// NOTE: This code is designed to only output a Mohawk archive
 	// for a Riven saved game. It's hardcoded to do this because
 	// (as of right now) this is the only place in the engine
@@ -396,9 +399,7 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	// games need this, we should think about coming up with some
 	// more common way of outputting resources to an archive.
 
-	// Make sure we have the right extension
-	if (!filename.matchString("*.rvn", true))
-		filename += ".rvn";
+	Common::String filename = buildSaveFilename(slot);
 
 	// Convert class variables to variable numbers
 	_vm->_vars["currentstackid"] = _vm->getCurStack();
@@ -410,7 +411,7 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 
 	debug (0, "Saving game to \'%s\'", filename.c_str());
 
-	Common::MemoryWriteStreamDynamic *metaSection = genMETASection(filename);
+	Common::MemoryWriteStreamDynamic *metaSection = genMETASection(description);
 	Common::MemoryWriteStreamDynamic *nameSection = genNAMESection();
 	Common::MemoryWriteStreamDynamic *thmbSection = genTHMBSection();
 	Common::MemoryWriteStreamDynamic *varsSection = genVARSSection();
@@ -576,9 +577,11 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
 	return Common::kNoError;
 }
 
-void RivenSaveLoad::deleteSave(Common::String saveName) {
-	debug (0, "Deleting save file \'%s\'", saveName.c_str());
-	_saveFileMan->removeSavefile(saveName);
+void RivenSaveLoad::deleteSave(const int slot) {
+	Common::String filename = buildSaveFilename(slot);
+
+	debug (0, "Deleting save file \'%s\'", filename.c_str());
+	g_system->getSavefileManager()->removeSavefile(filename);
 }
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index 2ef4326..34bfbdc 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -60,18 +60,19 @@ public:
 	RivenSaveLoad(MohawkEngine_Riven*, Common::SaveFileManager*);
 	~RivenSaveLoad();
 
-	Common::StringArray generateSaveGameList();
-	Common::Error loadGame(Common::String);
-	Common::Error saveGame(Common::String);
-	void deleteSave(Common::String);
+	Common::Error loadGame(const int slot);
+	Common::Error saveGame(const int slot, const Common::String &description);
+	static void deleteSave(const int slot);
 
-	static SaveStateDescriptor querySaveMetaInfos(const Common::String &filename);
-	static Common::String querySaveDescription(const Common::String &filename);
+	static SaveStateDescriptor querySaveMetaInfos(const int slot);
+	static Common::String querySaveDescription(const int slot);
 
 private:
 	MohawkEngine_Riven *_vm;
 	Common::SaveFileManager *_saveFileMan;
 
+	static Common::String buildSaveFilename(const int slot);
+
 	Common::MemoryWriteStreamDynamic *genNAMESection();
 	Common::MemoryWriteStreamDynamic *genMETASection(const Common::String &desc) const;
 	Common::MemoryWriteStreamDynamic *genTHMBSection() const;






More information about the Scummvm-git-logs mailing list