From c06e347d90687c4549b18f158f087af878cc4636 Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Sun, 10 Jul 2016 15:36:43 +0200 Subject: MOHAWK: Make the Riven saved games loadable by the original engine --- engines/mohawk/riven_saveload.cpp | 83 +++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 29 deletions(-) (limited to 'engines') 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 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 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(); -- cgit v1.2.3