diff options
| author | Johannes Schickel | 2008-03-27 18:03:00 +0000 | 
|---|---|---|
| committer | Johannes Schickel | 2008-03-27 18:03:00 +0000 | 
| commit | 3bea667a10d823706a516caf8bd7d93d012c765d (patch) | |
| tree | 14cb903a66efdacc8164e15a14615fe598fdad06 | |
| parent | 165c937e20853d704efd56e383e0bb3042e92ddb (diff) | |
| download | scummvm-rg350-3bea667a10d823706a516caf8bd7d93d012c765d.tar.gz scummvm-rg350-3bea667a10d823706a516caf8bd7d93d012c765d.tar.bz2 scummvm-rg350-3bea667a10d823706a516caf8bd7d93d012c765d.zip  | |
- Implemented support for --list-saves in Kyra engine
- Added support for variable length savegame name field
- Changed savegame identifier
- Increased savegame file version
svn-id: r31268
| -rw-r--r-- | engines/kyra/detection.cpp | 39 | ||||
| -rw-r--r-- | engines/kyra/kyra.h | 26 | ||||
| -rw-r--r-- | engines/kyra/kyra_v1.h | 2 | ||||
| -rw-r--r-- | engines/kyra/kyra_v2.h | 2 | ||||
| -rw-r--r-- | engines/kyra/saveload.cpp | 152 | ||||
| -rw-r--r-- | engines/kyra/saveload_v1.cpp | 23 | ||||
| -rw-r--r-- | engines/kyra/saveload_v2.cpp | 54 | 
7 files changed, 186 insertions, 112 deletions
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 80d9ca64ec..4a0ba0fc26 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -29,6 +29,7 @@  #include "common/config-manager.h"  #include "common/advancedDetector.h" +#include "common/savefile.h"  #include "base/plugins.h" @@ -445,15 +446,17 @@ class KyraMetaEngine : public Common::AdvancedMetaEngine {  public:  	KyraMetaEngine() : Common::AdvancedMetaEngine(detectionParams) {} -	virtual const char *getName() const { +	const char *getName() const {  		return "Legend of Kyrandia Engine";  	} -	virtual const char *getCopyright() const { +	const char *getCopyright() const {  		return "The Legend of Kyrandia (C) Westwood Studios";  	} -	virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const; +	bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const; + +	SaveStateList listSaves(const char *target) const;  };  bool KyraMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const { @@ -489,10 +492,38 @@ bool KyraMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common  		break;  	default:  		res = false; -		error("Kyra engine: unknown gameID"); +		warning("Kyra engine: unknown gameID");  	}  	return res;  } +SaveStateList KyraMetaEngine::listSaves(const char *target) const { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	Kyra::KyraEngine::SaveHeader header; +	Common::String pattern = target; +	pattern += ".???"; + +	Common::StringList filenames; +	filenames = saveFileMan->listSavefiles(pattern.c_str()); +	sort(filenames.begin(), filenames.end());	// Sort (hopefully ensuring we are sorted numerically..) + +	SaveStateList saveList; +	for (Common::StringList::const_iterator file = filenames.begin(); file != filenames.end(); file++) { +		// Obtain the last 3 digits of the filename, since they correspond to the save slot +		int slotNum = atoi(file->c_str() + file->size() - 3); +		 +		if (slotNum >= 0 && slotNum <= 999) { +			Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); +			if (in) { +				if (Kyra::KyraEngine::readSaveHeader(in, header) == Kyra::KyraEngine::kRSHENoError) +					saveList.push_back(SaveStateDescriptor(slotNum, header.description, *file)); +				delete in; +			} +		} +	} + +	return saveList; +} +  REGISTER_PLUGIN(KYRA, PLUGIN_TYPE_ENGINE, KyraMetaEngine); diff --git a/engines/kyra/kyra.h b/engines/kyra/kyra.h index 8bb8e159b2..fc37b2fb94 100644 --- a/engines/kyra/kyra.h +++ b/engines/kyra/kyra.h @@ -38,6 +38,8 @@ class InSaveFile;  class OutSaveFile;  } // end of namespace Common +class KyraMetaEngine; +  namespace Kyra {  struct GameFlags { @@ -101,6 +103,7 @@ class ScriptHelper;  class KyraEngine : public Engine {  friend class Debugger; +friend class ::KyraMetaEngine;  public:  	KyraEngine(OSystem *system, const GameFlags &flags);  	virtual ~KyraEngine(); @@ -225,10 +228,27 @@ protected:  	static const int8 _addYPosTable[];  	// save/load -	virtual uint32 saveGameID() const = 0; -  	const char *getSavegameFilename(int num); -	Common::InSaveFile *openSaveForReading(const char *filename, uint32 &version, char *saveName); + +	struct SaveHeader { +		Common::String description; +		uint32 version; +		byte gameID; +		uint32 flags; + +		bool originalSave;	// savegame from original interpreter +		bool oldHeader;		// old scummvm save header +	}; + +	enum kReadSaveHeaderError { +		kRSHENoError = 0, +		kRSHEInvalidType = 1, +		kRSHEInvalidVersion = 2, +		kRSHEIoError = 3 +	}; +	static kReadSaveHeaderError readSaveHeader(Common::InSaveFile *file, SaveHeader &header); + +	Common::InSaveFile *openSaveForReading(const char *filename, SaveHeader &header);  	Common::OutSaveFile *openSaveForWriting(const char *filename, const char *saveName) const;  }; diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 5d4350f857..ee31fb97ff 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -281,8 +281,6 @@ public:  	void snd_voiceWaitForFinish(bool ingame = true);  protected: -	uint32 saveGameID() const { return 'KYRA'; } -  	void saveGame(const char *fileName, const char *saveName);  	void loadGame(const char *fileName); diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h index ac6e5a3628..5a6f55ceeb 100644 --- a/engines/kyra/kyra_v2.h +++ b/engines/kyra/kyra_v2.h @@ -1201,8 +1201,6 @@ protected:  	int _dbgPass;  	// save/load specific -	uint32 saveGameID() const { return 'HOFS'; } -  	void saveGame(const char *fileName, const char *saveName);  	void loadGame(const char *fileName);  }; diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index 7d6fcaa0c1..9347da4aeb 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -29,16 +29,82 @@  #include "kyra/kyra.h" -#define CURRENT_SAVE_VERSION 8 +#define CURRENT_SAVE_VERSION 9 -#define GF_FLOPPY (1 <<  0) -#define GF_TALKIE (1 <<  1) +#define GF_FLOPPY  (1 <<  0) +#define GF_TALKIE  (1 <<  1)  #define GF_FMTOWNS (1 <<  2)  namespace Kyra { -Common::InSaveFile *KyraEngine::openSaveForReading(const char *filename, uint32 &version, char *saveName) { -	debugC(9, kDebugLevelMain, "KyraEngine::openSaveForReading('%s', %p, %p)", filename, (const void*)&version, saveName); +KyraEngine::kReadSaveHeaderError KyraEngine::readSaveHeader(Common::InSaveFile *in, SaveHeader &header) { +	uint32 type = in->readUint32BE(); +	header.originalSave = false; +	header.oldHeader = false; +	header.flags = 0; + +	if (type == MKID_BE('KYRA') || type == MKID_BE('ARYK')) { // old Kyra1 header ID +		header.gameID = GI_KYRA1; +		header.oldHeader = true; +	} else if (type == MKID_BE('HOFS')) { // old Kyra2 header ID +		header.gameID = GI_KYRA2; +		header.oldHeader = true; +	} else if (type == MKID_BE('WWSV')) { +		header.gameID = in->readByte(); +	} else { +		// try checking for original save header +		const int descriptionSize[2] = { 30, 80 }; +		char descriptionBuffer[81]; + +		bool saveOk = false; + +		for (uint i = 0; i < ARRAYSIZE(descriptionSize) && !saveOk; ++i) { +			in->seek(0, SEEK_SET); +			in->read(descriptionBuffer, descriptionSize[i]); +			descriptionBuffer[descriptionSize[i]] = 0; +			 +			type = in->readUint32BE(); +			header.version = in->readUint16LE(); +			if (type == MKID_BE('MBL3') && header.version == 100) { +				saveOk = true; +				header.description = descriptionBuffer; +				header.gameID = GI_KYRA2; +				break; +			} +		} + +		if (saveOk) { +			header.originalSave = true; +			header.description = descriptionBuffer; +			return kRSHENoError; +		} else { +			return kRSHEInvalidType; +		} +	} + +	header.version = in->readUint32BE(); +	if (header.version > CURRENT_SAVE_VERSION || (header.oldHeader && header.version > 8) || (type == MKID_BE('ARYK') && header.version > 3)) +		return kRSHEInvalidVersion; + +	// Versions prior to 9 are using a fixed length description field +	if (header.version <= 8) { +		char buffer[31]; +		in->read(buffer, 31); +		header.description = buffer; +	} else { +		header.description = ""; +		for (char c = 0; (c = in->readByte()) != 0;) +			header.description += c; +	} + +	if (header.version >= 2) +		header.flags = in->readUint32BE(); + +	return (in->ioFailed() ? kRSHEIoError : kRSHENoError); +} + +Common::InSaveFile *KyraEngine::openSaveForReading(const char *filename, SaveHeader &header) { +	debugC(9, kDebugLevelMain, "KyraEngine::openSaveForReading('%s', -)", filename);  	Common::InSaveFile *in = 0;  	if (!(in = _saveFileMan->openForLoading(filename))) { @@ -46,52 +112,45 @@ Common::InSaveFile *KyraEngine::openSaveForReading(const char *filename, uint32  		return 0;  	} -	uint32 type = in->readUint32BE(); - -	// FIXME: The kyra savegame code used to be endian unsafe. Uncomment the -	// following line to graciously handle old savegames from LE machines. -	// if (type != MKID_BE('KYRA') && type != MKID_BE('ARYK')) { -	if (type != MKID_BE(saveGameID())) { -		warning("No ScummVM Kyra engine savefile header."); -		delete in; -		return 0; -	} +	kReadSaveHeaderError errorCode = KyraEngine::readSaveHeader(in, header); +	if (errorCode != kRSHENoError) { +		if (errorCode == kRSHEInvalidType) +			warning("No ScummVM Kyra engine savefile header."); +		else if (errorCode == kRSHEInvalidVersion) +			warning("Savegame is not the right version (%u, '%s')", header.version, header.oldHeader ? "true" : "false"); +		else if (errorCode == kRSHEIoError) +			warning("Load failed ('%s').", filename); -	version = in->readUint32BE(); -	if (version > CURRENT_SAVE_VERSION) { -		warning("Savegame is not the right version (%u)", version);  		delete in;  		return 0;  	} -	char saveNameBuffer[31]; -	if (!saveName) -		saveName = saveNameBuffer; -	in->read(saveName, 31); - -	if (_flags.gameID == GI_KYRA1 && version < 2) { -		warning("Make sure your savefile was from this version! (too old savefile version to detect that)"); -	} else { -		uint32 flags = in->readUint32BE(); -		if ((flags & GF_FLOPPY) && (_flags.isTalkie || _flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) { -			warning("Can not load DOS Floppy savefile for this (non DOS Floppy) gameversion"); -			delete in; -			return 0; -		} else if ((flags & GF_TALKIE) && !(_flags.isTalkie)) { -			warning("Can not load DOS CD-ROM savefile for this (non DOS CD-ROM) gameversion"); -			delete in; -			return 0; -		} else if ((flags & GF_FMTOWNS) && !(_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) { -			warning("Can not load FM-Towns/PC98 savefile for this (non FM-Towns/PC98) gameversion"); -			delete in; -			return 0; +	if (!header.originalSave) { +		if (!header.oldHeader) { +			if (header.gameID != _flags.gameID) { +				warning("Trying to load game state from other game (save game: %u, running game: %u)", header.gameID, _flags.gameID); +				delete in; +				return 0; +			}  		} -	} -	if (in->ioFailed()) { -		error("Load failed ('%s', '%s').", filename, saveName); -		delete in; -		return 0; +		if (header.version < 2) { +			warning("Make sure your savefile was from this version! (too old savefile version to detect that)"); +		} else { +			if ((header.flags & GF_FLOPPY) && (_flags.isTalkie || _flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) { +				warning("Can not load DOS Floppy savefile for this (non DOS Floppy) gameversion"); +				delete in; +				return 0; +			} else if ((header.flags & GF_TALKIE) && !(_flags.isTalkie)) { +				warning("Can not load DOS CD-ROM savefile for this (non DOS CD-ROM) gameversion"); +				delete in; +				return 0; +			} else if ((header.flags & GF_FMTOWNS) && !(_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) { +				warning("Can not load FM-Towns/PC98 savefile for this (non FM-Towns/PC98) gameversion"); +				delete in; +				return 0; +			} +		}  	}  	return in; @@ -109,9 +168,10 @@ Common::OutSaveFile *KyraEngine::openSaveForWriting(const char *filename, const  	}  	// Savegame version -	out->writeUint32BE(saveGameID()); +	out->writeUint32BE(MKID_BE('WWSV')); +	out->writeByte(_flags.gameID);  	out->writeUint32BE(CURRENT_SAVE_VERSION); -	out->write(saveName, 31); +	out->write(saveName, strlen(saveName)+1);  	if (_flags.isTalkie)  		out->writeUint32BE(GF_TALKIE);  	else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) diff --git a/engines/kyra/saveload_v1.cpp b/engines/kyra/saveload_v1.cpp index 37ad1fc8d3..3168d150fb 100644 --- a/engines/kyra/saveload_v1.cpp +++ b/engines/kyra/saveload_v1.cpp @@ -38,12 +38,17 @@ namespace Kyra {  void KyraEngine_v1::loadGame(const char *fileName) {  	debugC(9, kDebugLevelMain, "KyraEngine_v1::loadGame('%s')", fileName); -	uint32 version = 0; -	char saveName[31]; -	Common::InSaveFile *in = openSaveForReading(fileName, version, saveName); +	SaveHeader header; +	Common::InSaveFile *in = openSaveForReading(fileName, header);  	if (!in)  		return; +	if (header.originalSave) { +		// no support for original savefile in Kyrandia 1 (yet) +		delete in; +		return; +	} +  	snd_playSoundEffect(0x0A);  	snd_playWanderScoreViaMap(0, 1); @@ -99,7 +104,7 @@ void KyraEngine_v1::loadGame(const char *fileName) {  	_poisonDeathCounter = in->readByte();  	_animator->_brandonDrawFrame = in->readUint16BE(); -	_timer->loadDataFromFile(in, version); +	_timer->loadDataFromFile(in, header.version);  	memset(_flagsTable, 0, sizeof(_flagsTable));  	uint32 flagsSize = in->readUint32BE(); @@ -131,7 +136,7 @@ void KyraEngine_v1::loadGame(const char *fileName) {  			_roomTable[sceneId].needInit[i] = in->readByte();  		}  	} -	if (version >= 3) { +	if (header.version >= 3) {  		_lastMusicCommand = in->readSint16BE();  		if (_lastMusicCommand != -1)  			snd_playWanderScoreViaMap(_lastMusicCommand, 1); @@ -140,7 +145,7 @@ void KyraEngine_v1::loadGame(const char *fileName) {  	// Version 4 stored settings in the savegame. As of version 5, they are  	// handled by the config manager. -	if (version == 4) { +	if (header.version == 4) {  		in->readByte(); // Text speed  		in->readByte(); // Walk speed  		in->readByte(); // Music @@ -148,7 +153,7 @@ void KyraEngine_v1::loadGame(const char *fileName) {  		in->readByte(); // Voice  	} -	if (version >= 7) { +	if (header.version >= 7) {  		_curSfxFile = in->readByte();  		// In the first version when this entry was introduced, @@ -200,9 +205,9 @@ void KyraEngine_v1::loadGame(const char *fileName) {  	setMousePos(brandonX, brandonY);  	if (in->ioFailed()) -		error("Load failed ('%s', '%s').", fileName, saveName); +		error("Load failed ('%s', '%s').", fileName, header.description.c_str());  	else -		debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", saveName); +		debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", header.description.c_str());  	// We didn't explicitly set the walk speed, but it's saved as part of  	// the _timers array, so we need to re-sync it with _configWalkspeed. diff --git a/engines/kyra/saveload_v2.cpp b/engines/kyra/saveload_v2.cpp index 0da58233ff..44707b35af 100644 --- a/engines/kyra/saveload_v2.cpp +++ b/engines/kyra/saveload_v2.cpp @@ -131,47 +131,11 @@ void KyraEngine_v2::saveGame(const char *fileName, const char *saveName) {  void KyraEngine_v2::loadGame(const char *fileName) {  	debugC(9, kDebugLevelMain, "KyraEngine_v2::loadGame('%s')", fileName); -	uint32 version = 0; -	char saveName[31]; -	Common::InSaveFile *in = openSaveForReading(fileName, version, saveName); +	SaveHeader header; +	Common::InSaveFile *in = openSaveForReading(fileName, header);  	if (!in) { -		// check for original savefile -		if ((in = _saveFileMan->openForLoading(fileName))) { -			in->seek(0x50, SEEK_CUR); - -			uint8 type[4]; -			uint8 acceptedType[4] = { 0x4D, 0x42, 0x4C, 0x33 }; // 'MBL3' -			in->read(type, sizeof(type)); -			uint16 origVersion = in->readUint16LE(); -			 -			debug(1, "Savegame type: '%c%c%c%c' version: %d", type[0], type[1], type[2], type[3], version); - -			if (!memcmp(type, acceptedType, 4) && origVersion == 100) { -				warning("Trying to load savegame from original interpreter, while this is possible, it is not officially supported."); - -				in->seek(0, SEEK_SET); - -				// read first 31 bytes of original description -				in->read(saveName, 30); -				saveName[30] = 0; -				// skip last part of original description -				in->seek(0x50-30, SEEK_CUR); -				version = 0xF000 + origVersion; -				// skip type -				in->seek(4, SEEK_CUR); -				// skip version -				in->seek(2, SEEK_CUR); -			} else { -				delete in; -				in = 0; -			} -		} - -		if (!in) { -			showMessageFromCCode(0x35, 0x84, 0); -			snd_playSoundEffect(0x0D); -			return; -		} +		showMessageFromCCode(0x35, 0x84, 0); +		snd_playSoundEffect(0x0D);  	}  	bool setFlag1EE = (queryGameFlag(0x1EE) != 0); @@ -187,8 +151,8 @@ void KyraEngine_v2::loadGame(const char *fileName) {  	_screen->hideMouse(); -	if (version < 0xF000) { -		_timer->loadDataFromFile(in, version); +	if (!header.originalSave) { +		_timer->loadDataFromFile(in, header.version);  		uint32 flagsSize = in->readUint32BE();  		assert(flagsSize <= sizeof(_flagsTable)); @@ -264,8 +228,6 @@ void KyraEngine_v2::loadGame(const char *fileName) {  		_sceneExit3 = in->readUint16BE();  		_sceneExit4 = in->readUint16BE();  	} else { -		version -= 0xF000; -  		/*word_2AB05 = */in->readUint16LE();  		_lastMusicCommand = in->readSint16LE();  		_newChapterFile = in->readByte(); @@ -363,9 +325,9 @@ void KyraEngine_v2::loadGame(const char *fileName) {  	}  	if (in->ioFailed()) -		error("Load failed ('%s', '%s').", fileName, saveName); +		error("Load failed ('%s', '%s').", fileName, header.description.c_str());  	else -		debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", saveName); +		debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", header.description.c_str());  	delete in;  | 
