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

This commit is contained in:
Bastien Bouclet 2016-07-10 15:36:43 +02:00
parent 1c0c540fa7
commit c06e347d90

View file

@ -72,8 +72,11 @@ Common::Error RivenSaveLoad::loadGame(Common::String filename) {
Common::Array<uint32> rawVariables; Common::Array<uint32> rawVariables;
while (!vars->eos()) { while (!vars->eos()) {
vars->readUint32BE(); // Unknown (Stack?) // The original engine stores the variables values in an array. All the slots in
vars->readUint32BE(); // Unknown (0 or 1) // 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()); rawVariables.push_back(vars->readUint32BE());
} }
@ -162,14 +165,18 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genVARSSection() {
Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic(); Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) { for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
stream->writeUint32BE(0); // Unknown stream->writeUint32BE(1); // Reference counter
stream->writeUint32BE(0); // Unknown stream->writeUint32BE(1); // Variable initialized flag
stream->writeUint32BE(it->_value); stream->writeUint32BE(it->_value);
} }
return stream; return stream;
} }
static int stringCompareToIgnoreCase(const Common::String &s1, const Common::String &s2) {
return s1.compareToIgnoreCase(s2) < 0;
}
Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() { Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic(); Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
@ -181,8 +188,28 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genNAMESection() {
curPos += it->_key.size() + 1; curPos += it->_key.size() + 1;
} }
for (uint16 i = 0; i < _vm->_vars.size(); i++) // The original engine does not store the variables in a HashMap, but in a "NameList"
stream->writeUint16BE(i); // 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++) { for (RivenVariableMap::const_iterator it = _vm->_vars.begin(); it != _vm->_vars.end(); it++) {
stream->write(it->_key.c_str(), it->_key.size()); 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 // games need this, we should think about coming up with some
// more common way of outputting resources to an archive. // 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 // Make sure we have the right extension
if (!filename.matchString("*.rvn", true)) if (!filename.matchString("*.rvn", true))
filename += ".rvn"; filename += ".rvn";
@ -262,15 +285,17 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
saveFile->writeUint16BE(4); // 4 Type Table Entries saveFile->writeUint16BE(4); // 4 Type Table Entries
// Hardcode Entries (32 bytes - total: 64) // 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(46); // Resource table offset
saveFile->writeUint16BE(38); // String table offset saveFile->writeUint16BE(38); // String table offset
saveFile->writeUint32BE(ID_NAME); saveFile->writeUint32BE(ID_VARS);
saveFile->writeUint16BE(52); saveFile->writeUint16BE(52);
saveFile->writeUint16BE(40); saveFile->writeUint16BE(40);
saveFile->writeUint32BE(ID_VARS); saveFile->writeUint32BE(ID_VERS);
saveFile->writeUint16BE(58); saveFile->writeUint16BE(58);
saveFile->writeUint16BE(42); saveFile->writeUint16BE(42);
@ -281,23 +306,23 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
// Pseudo-String Table (2 bytes - total: 66) // Pseudo-String Table (2 bytes - total: 66)
saveFile->writeUint16BE(0); // We don't need a name list 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);
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); 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(1); saveFile->writeUint16BE(1);
saveFile->writeUint16BE(2); 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(1); saveFile->writeUint16BE(1);
saveFile->writeUint16BE(3); saveFile->writeUint16BE(3);
@ -310,37 +335,37 @@ Common::Error RivenSaveLoad::saveGame(Common::String filename) {
// File Table (4 bytes - total: 102) // File Table (4 bytes - total: 102)
saveFile->writeUint32BE(4); saveFile->writeUint32BE(4);
// VERS Section (File Table) (10 bytes - total: 112) // NAME Section (File Table) (10 bytes - total: 112)
saveFile->writeUint32BE(142); 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->writeUint16BE(nameSection->size() & 0xFFFF);
saveFile->writeByte((nameSection->size() & 0xFF0000) >> 16); saveFile->writeByte((nameSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0); saveFile->writeByte(0);
saveFile->writeUint16BE(0); saveFile->writeUint16BE(0);
// VARS Section (File Table) (10 bytes - total: 132) // VARS Section (File Table) (10 bytes - total: 122)
saveFile->writeUint32BE(142 + versSection->size() + nameSection->size()); saveFile->writeUint32BE(142 + nameSection->size());
saveFile->writeUint16BE(varsSection->size() & 0xFFFF); saveFile->writeUint16BE(varsSection->size() & 0xFFFF);
saveFile->writeByte((varsSection->size() & 0xFF0000) >> 16); saveFile->writeByte((varsSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0); saveFile->writeByte(0);
saveFile->writeUint16BE(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) // 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->writeUint16BE(zipsSection->size() & 0xFFFF);
saveFile->writeByte((zipsSection->size() & 0xFF0000) >> 16); saveFile->writeByte((zipsSection->size() & 0xFF0000) >> 16);
saveFile->writeByte(0); saveFile->writeByte(0);
saveFile->writeUint16BE(0); saveFile->writeUint16BE(0);
saveFile->write(versSection->getData(), versSection->size());
saveFile->write(nameSection->getData(), nameSection->size()); saveFile->write(nameSection->getData(), nameSection->size());
saveFile->write(varsSection->getData(), varsSection->size()); saveFile->write(varsSection->getData(), varsSection->size());
saveFile->write(versSection->getData(), versSection->size());
saveFile->write(zipsSection->getData(), zipsSection->size()); saveFile->write(zipsSection->getData(), zipsSection->size());
saveFile->finalize(); saveFile->finalize();