aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorBastien Bouclet2016-07-10 15:36:43 +0200
committerBastien Bouclet2016-07-10 22:01:02 +0200
commitc06e347d90687c4549b18f158f087af878cc4636 (patch)
tree421acf4fb6111bf99777f5b470ea5b06978e2a7b /engines
parent1c0c540fa78bc6eef49c3d42a6a7512820fe3622 (diff)
downloadscummvm-rg350-c06e347d90687c4549b18f158f087af878cc4636.tar.gz
scummvm-rg350-c06e347d90687c4549b18f158f087af878cc4636.tar.bz2
scummvm-rg350-c06e347d90687c4549b18f158f087af878cc4636.zip
MOHAWK: Make the Riven saved games loadable by the original engine
Diffstat (limited to 'engines')
-rw-r--r--engines/mohawk/riven_saveload.cpp83
1 files changed, 54 insertions, 29 deletions
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 6af66f7a2d..7bb8582edc 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();