diff options
| author | Bastien Bouclet | 2016-07-10 15:36:43 +0200 | 
|---|---|---|
| committer | Bastien Bouclet | 2016-07-10 22:01:02 +0200 | 
| commit | c06e347d90687c4549b18f158f087af878cc4636 (patch) | |
| tree | 421acf4fb6111bf99777f5b470ea5b06978e2a7b /engines/mohawk | |
| parent | 1c0c540fa78bc6eef49c3d42a6a7512820fe3622 (diff) | |
| download | scummvm-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/mohawk')
| -rw-r--r-- | engines/mohawk/riven_saveload.cpp | 83 | 
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();  | 
