diff options
-rw-r--r-- | engines/mohawk/detection.cpp | 41 | ||||
-rw-r--r-- | engines/mohawk/myst.cpp | 10 | ||||
-rw-r--r-- | engines/mohawk/myst_state.cpp | 205 | ||||
-rw-r--r-- | engines/mohawk/myst_state.h | 37 |
4 files changed, 255 insertions, 38 deletions
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index 926c296257..986b35c85e 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -35,6 +35,7 @@ #ifdef ENABLE_MYST #include "mohawk/myst.h" +#include "mohawk/myst_state.h" #endif #ifdef ENABLE_RIVEN @@ -184,13 +185,18 @@ public: virtual SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const { return 999; } virtual void removeSaveState(const char *target, int slot) const; + virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; }; bool MohawkMetaEngine::hasFeature(MetaEngineFeature f) const { return (f == kSupportsListSaves) || (f == kSupportsLoadingDuringStartup) - || (f == kSupportsDeleteSave); + || (f == kSupportsDeleteSave) + || (f == kSavesSupportMetaInfo) + || (f == kSavesSupportThumbnail) + || (f == kSavesSupportCreationDate) + || (f == kSavesSupportPlayTime); } SaveStateList MohawkMetaEngine::listSaves(const char *target) const { @@ -198,12 +204,15 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const { SaveStateList saveList; // Loading games is only supported in Myst/Riven currently. +#ifdef ENABLE_MYST if (strstr(target, "myst")) { - filenames = g_system->getSavefileManager()->listSavefiles("*.mys"); + filenames = Mohawk::MystGameState::generateSaveGameList(); for (uint32 i = 0; i < filenames.size(); i++) saveList.push_back(SaveStateDescriptor(i, filenames[i])); - } else if (strstr(target, "riven")) { + } else +#endif + if (strstr(target, "riven")) { filenames = g_system->getSavefileManager()->listSavefiles("*.rvn"); for (uint32 i = 0; i < filenames.size(); i++) @@ -215,15 +224,35 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const { void MohawkMetaEngine::removeSaveState(const char *target, int slot) const { // Removing saved games is only supported in Myst/Riven currently. +#ifdef ENABLE_MYST if (strstr(target, "myst")) { - Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.mys"); - g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); - } else if (strstr(target, "riven")) { + Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList(); + Mohawk::MystGameState::deleteSave(filenames[slot]); + } else +#endif + if (strstr(target, "riven")) { Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn"); g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); } } +SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int slot) const { +#ifdef ENABLE_MYST + if (strstr(target, "myst")) { + Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList(); + + if (slot >= (int) filenames.size()) { + return SaveStateDescriptor(); + } + + return Mohawk::MystGameState::querySaveMetaInfos(filenames[slot]); + } else +#endif + { + return SaveStateDescriptor(); + } +} + bool MohawkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Mohawk::MohawkGameDescription *gd = (const Mohawk::MohawkGameDescription *)desc; diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 852196e6ac..5337a5e773 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -236,7 +236,7 @@ Common::Error MohawkEngine_Myst::run() { // Load game from launcher/command line if requested if (ConfMan.hasKey("save_slot") && canLoadGameStateCurrently()) { uint32 gameToLoad = ConfMan.getInt("save_slot"); - Common::StringArray savedGamesList = _gameState->generateSaveGameList(); + Common::StringArray savedGamesList = MystGameState::generateSaveGameList(); if (gameToLoad > savedGamesList.size()) error ("Could not find saved game"); _gameState->load(savedGamesList[gameToLoad]); @@ -1066,19 +1066,19 @@ void MohawkEngine_Myst::loadResources() { } Common::Error MohawkEngine_Myst::loadGameState(int slot) { - if (_gameState->load(_gameState->generateSaveGameList()[slot])) + if (_gameState->load(MystGameState::generateSaveGameList()[slot])) return Common::kNoError; return Common::kUnknownError; } Common::Error MohawkEngine_Myst::saveGameState(int slot, const Common::String &desc) { - Common::StringArray saveList = _gameState->generateSaveGameList(); + Common::StringArray saveList = MystGameState::generateSaveGameList(); if ((uint)slot < saveList.size()) - _gameState->deleteSave(saveList[slot]); + MystGameState::deleteSave(saveList[slot]); - return _gameState->save(Common::String(desc)) ? Common::kNoError : Common::kUnknownError; + return _gameState->save(desc) ? Common::kNoError : Common::kUnknownError; } bool MohawkEngine_Myst::canLoadGameStateCurrently() { diff --git a/engines/mohawk/myst_state.cpp b/engines/mohawk/myst_state.cpp index 9bfbba67b6..06cd69b23c 100644 --- a/engines/mohawk/myst_state.cpp +++ b/engines/mohawk/myst_state.cpp @@ -26,11 +26,41 @@ #include "common/debug.h" #include "common/serializer.h" +#include "common/system.h" #include "common/textconsole.h" #include "common/util.h" +#include "graphics/thumbnail.h" + namespace Mohawk { +MystSaveMetadata::MystSaveMetadata() { + saveDay = 0; + saveMonth = 0; + saveYear = 0; + saveHour = 0; + saveMinute = 0; + totalPlayTime = 0; +} + +bool MystSaveMetadata::sync(Common::Serializer &s) { + static const Common::Serializer::Version kCurrentVersion = 1; + + if (!s.syncVersion(kCurrentVersion)) { + return false; + } + + s.syncAsByte(saveDay); + s.syncAsByte(saveMonth); + s.syncAsUint16LE(saveYear); + s.syncAsByte(saveHour); + s.syncAsByte(saveMinute); + s.syncString(saveDescription); + s.syncAsUint32LE(totalPlayTime); + + return true; +} + MystGameState::MystGameState(MohawkEngine_Myst *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) { // Most of the variables are zero at game start. memset(&_globals, 0, sizeof(_globals)); @@ -77,28 +107,15 @@ MystGameState::~MystGameState() { } Common::StringArray MystGameState::generateSaveGameList() { - return _saveFileMan->listSavefiles("*.mys"); + return g_system->getSavefileManager()->listSavefiles("*.mys"); } bool MystGameState::load(const Common::String &filename) { - Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename); - if (!loadFile) - return false; - - debugC(kDebugSaveLoad, "Loading game from '%s'", filename.c_str()); - - // First, let's make sure we're using a saved game file from this version of Myst - // By checking length of file... - int32 size = loadFile->size(); - if (size != 664 && size != 601) { - warning("Incompatible saved game version"); - delete loadFile; + if (!loadState(filename)) { return false; } - Common::Serializer s(loadFile, nullptr); - syncGameState(s, size == 664); - delete loadFile; + loadMetadata(filename); // Set Channelwood elevator state to down, because we start on the lower level _channelwood.elevatorState = 0; @@ -119,15 +136,72 @@ bool MystGameState::load(const Common::String &filename) { return true; } -bool MystGameState::save(const Common::String &fname) { - Common::String filename(fname); - // Make sure we have the right extension - if (!filename.hasSuffix(".mys") && !filename.hasSuffix(".MYS")) - filename += ".mys"; +bool MystGameState::loadState(const Common::String &filename) { + Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename); + if (!loadFile) { + return false; + } + + debugC(kDebugSaveLoad, "Loading game from '%s'", filename.c_str()); + + // First, let's make sure we're using a saved game file from this version of Myst + // By checking length of file... + int32 size = loadFile->size(); + if (size != 664 && size != 601) { + warning("Incompatible saved game version"); + delete loadFile; + return false; + } + + Common::Serializer s(loadFile, nullptr); + syncGameState(s, size == 664); + delete loadFile; + + return true; +} +void MystGameState::loadMetadata(const Common::String &filename) { + // Open the metadata file + Common::InSaveFile *metadataFile = openMetadataFile(filename); + if (!metadataFile) { + return; + } + + debugC(kDebugSaveLoad, "Loading metadata from '%s'", filename.c_str()); + + Common::Serializer m(metadataFile, nullptr); + + // Read the metadata file + if (_metadata.sync(m)) { + _vm->setTotalPlayTime(_metadata.totalPlayTime); + } + + delete metadataFile; +} + +bool MystGameState::save(const Common::String &filename) { + // Make sure the description does not have an extension + Common::String desc = filename; + if (filename.hasSuffix(".mys") || filename.hasSuffix(".MYS")) { + desc = removeExtension(filename); + } + + if (!saveState(desc)) { + return false; + } + + updateMetadateForSaving(desc); + + return saveMetadata(desc); +} + +bool MystGameState::saveState(const Common::String &desc) { + // Make sure we have the right extension + Common::String filename = desc + ".mys"; Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename); - if (!saveFile) + if (!saveFile) { return false; + } debugC(kDebugSaveLoad, "Saving game to '%s'", filename.c_str()); @@ -139,6 +213,88 @@ bool MystGameState::save(const Common::String &fname) { return true; } +void MystGameState::updateMetadateForSaving(const Common::String &desc) { + // Update save creation info + TimeDate t; + g_system->getTimeAndDate(t); + _metadata.saveYear = t.tm_year + 1900; + _metadata.saveMonth = t.tm_mon + 1; + _metadata.saveDay = t.tm_mday; + _metadata.saveHour = t.tm_hour; + _metadata.saveMinute = t.tm_min; + _metadata.saveDescription = desc; + _metadata.totalPlayTime = _vm->getTotalPlayTime(); +} + +bool MystGameState::saveMetadata(const Common::String &desc) { + // Write the metadata to a separate file so that the save files + // are still compatible with the original engine + Common::String metadataFilename = desc + ".mym"; + Common::OutSaveFile *metadataFile = _saveFileMan->openForSaving(metadataFilename); + if (!metadataFile) { + return false; + } + + // Save the metadata + Common::Serializer m(nullptr, metadataFile); + _metadata.sync(m); + + // Append a thumbnail + Graphics::saveThumbnail(*metadataFile); + + metadataFile->finalize(); + delete metadataFile; + + return true; +} + +SaveStateDescriptor MystGameState::querySaveMetaInfos(const Common::String filename) { + SaveStateDescriptor desc; + desc.setDescription(filename); + + // Open the metadata file + Common::InSaveFile *metadataFile = openMetadataFile(filename); + if (!metadataFile) { + return desc; + } + + Common::Serializer m(metadataFile, nullptr); + + // Read the metadata file + Mohawk::MystSaveMetadata metadata; + if (!metadata.sync(m)) { + delete metadataFile; + return desc; + } + + // Set the save description + desc.setDescription(metadata.saveDescription); + desc.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay); + desc.setSaveTime(metadata.saveHour, metadata.saveMinute); + desc.setPlayTime(metadata.totalPlayTime); + desc.setThumbnail(Graphics::loadThumbnail(*metadataFile)); + + delete metadataFile; + + return desc; +} + +Common::InSaveFile *MystGameState::openMetadataFile(const Common::String &filename) { + // Remove the extension + Common::String baseName = removeExtension(filename); + + // Open the metadata file + return g_system->getSavefileManager()->openForLoading(baseName + ".mym"); +} + +Common::String MystGameState::removeExtension(const Common::String &filename) { + Common::String baseName = filename; + for (uint i = 0; i < 4; i++) { + baseName.deleteLastChar(); + } + return baseName; +} + void MystGameState::syncGameState(Common::Serializer &s, bool isME) { // Globals first s.syncAsUint16LE(_globals.u0); @@ -317,7 +473,10 @@ void MystGameState::syncGameState(Common::Serializer &s, bool isME) { void MystGameState::deleteSave(const Common::String &saveName) { debugC(kDebugSaveLoad, "Deleting save file \'%s\'", saveName.c_str()); - _saveFileMan->removeSavefile(saveName.c_str()); + Common::String basename = removeExtension(saveName); + + g_system->getSavefileManager()->removeSavefile(saveName); + g_system->getSavefileManager()->removeSavefile(basename + ".mym"); } void MystGameState::addZipDest(uint16 stack, uint16 view) { diff --git a/engines/mohawk/myst_state.h b/engines/mohawk/myst_state.h index b07a0f2469..50359a5b52 100644 --- a/engines/mohawk/myst_state.h +++ b/engines/mohawk/myst_state.h @@ -27,6 +27,8 @@ #include "common/file.h" #include "common/str.h" +#include "engines/savestate.h" + namespace Common { class Serializer; } @@ -35,15 +37,33 @@ namespace Mohawk { class MohawkEngine_Myst; +struct MystSaveMetadata { + uint8 saveDay; + uint8 saveMonth; + uint16 saveYear; + + uint8 saveHour; + uint8 saveMinute; + + uint32 totalPlayTime; + + Common::String saveDescription; + + MystSaveMetadata(); + bool sync(Common::Serializer &s); +}; + class MystGameState { public: MystGameState(MohawkEngine_Myst*, Common::SaveFileManager*); ~MystGameState(); - Common::StringArray generateSaveGameList(); - bool load(const Common::String &); - bool save(const Common::String &); - void deleteSave(const Common::String &); + static Common::StringArray generateSaveGameList(); + static SaveStateDescriptor querySaveMetaInfos(const Common::String filename); + + bool load(const Common::String &filename); + bool save(const Common::String &filename); + static void deleteSave(const Common::String &saveName); void addZipDest(uint16 stack, uint16 view); bool isReachableZipDest(uint16 stack, uint16 view); @@ -268,8 +288,17 @@ public: uint32 generatorDepletionTime; } _stoneship; + MystSaveMetadata _metadata; + private: void syncGameState(Common::Serializer &s, bool isME); + static Common::InSaveFile *openMetadataFile(const Common::String &filename); + bool loadState(const Common::String &filename); + void loadMetadata(const Common::String &filename); + bool saveState(const Common::String &desc); + void updateMetadateForSaving(const Common::String &desc); + bool saveMetadata(const Common::String &desc); + static Common::String removeExtension(const Common::String &filename); // The values in these regions are lists of VIEW resources // which correspond to visited zip destinations |