aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2008-03-27 18:03:00 +0000
committerJohannes Schickel2008-03-27 18:03:00 +0000
commit3bea667a10d823706a516caf8bd7d93d012c765d (patch)
tree14cb903a66efdacc8164e15a14615fe598fdad06
parent165c937e20853d704efd56e383e0bb3042e92ddb (diff)
downloadscummvm-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.cpp39
-rw-r--r--engines/kyra/kyra.h26
-rw-r--r--engines/kyra/kyra_v1.h2
-rw-r--r--engines/kyra/kyra_v2.h2
-rw-r--r--engines/kyra/saveload.cpp152
-rw-r--r--engines/kyra/saveload_v1.cpp23
-rw-r--r--engines/kyra/saveload_v2.cpp54
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;