diff --git a/doomsday/plugins/common/include/gamestatereader.h b/doomsday/plugins/common/include/gamestatereader.h index 0c076b3607..838883e22e 100644 --- a/doomsday/plugins/common/include/gamestatereader.h +++ b/doomsday/plugins/common/include/gamestatereader.h @@ -81,27 +81,44 @@ class GameStateReaderFactory { DENG_ASSERT(recognizer != 0 && maker != 0); ReaderInfo info; - info.recognize = recognizer; - info.makeInstance = maker; + info.recognize = recognizer; + info.newReader = maker; readers.push_back(info); } /** - * Returns a new IGameStateReader instance appropriate for the specified save game @a info. + * Determines whether a IGameStateReader appropriate for the specified save game @a info + * is available and if so, reads the game session header. * - * @param info SaveInfo to attempt to read game session header into. - * @param path Path to the resource file to be recognized. + * @param saveInfo The game session header will be written here if recognized. + * @param path Path to the resource file to be recognized. + * + * @return @c true= the game session header was read successfully. + * + * @see recognizeAndMakeReader() + */ + bool recognize(SaveInfo &saveInfo, Str const *path) const + { + return readGameSessionHeader(saveInfo, path) != 0; + } + + /** + * Determines whether a IGameStateReader appropriate for the specified save game @a info + * is available and if so, reads the game session header and returns a new reader instance + * for deserializing the game state. + * + * @param saveInfo The game session header will be written here if recognized. + * @param path Path to the resource file to be recognized. * * @return New reader instance if recognized; otherwise @c 0. Ownership given to the caller. + * + * @see recognize() */ - IGameStateReader *newReaderFor(SaveInfo &info, Str const *path) + IGameStateReader *recognizeAndMakeReader(SaveInfo &saveInfo, Str const *path) const { - DENG2_FOR_EACH_CONST(ReaderInfos, i, readers) + if(ReaderInfo const *rdrInfo = readGameSessionHeader(saveInfo, path)) { - if(i->recognize(info, path)) - { - return i->makeInstance(); - } + return rdrInfo->newReader(); } return 0; // Unrecognized } @@ -109,10 +126,22 @@ class GameStateReaderFactory private: struct ReaderInfo { GameStateRecognizeFunc recognize; - GameStateReaderMakeFunc makeInstance; + GameStateReaderMakeFunc newReader; }; typedef std::list ReaderInfos; ReaderInfos readers; + + ReaderInfo const *readGameSessionHeader(SaveInfo &info, Str const *path) const + { + DENG2_FOR_EACH_CONST(ReaderInfos, i, readers) + { + if(i->recognize(info, path)) + { + return &*i; + } + } + return 0; // Unrecognized + } }; /** diff --git a/doomsday/plugins/common/include/p_saveg.h b/doomsday/plugins/common/include/p_saveg.h index 09b7c3a0a4..ce533fbe39 100644 --- a/doomsday/plugins/common/include/p_saveg.h +++ b/doomsday/plugins/common/include/p_saveg.h @@ -140,6 +140,15 @@ void SV_ClearTargetPlayers(void); */ void SV_DeclareGameStateReader(GameStateRecognizeFunc recognizer, GameStateReaderMakeFunc maker); +/** + * Determines whether the resource file on @a path is interpretable as a potentially loadable + * savegame state. + * + * @param info SaveInfo to attempt to read game session header into. + * @param path Path to the resource file to be recognized. + */ +bool SV_RecognizeGameState(SaveInfo &info, Str const *path); + class MapStateReader; class MapStateWriter; diff --git a/doomsday/plugins/common/include/saveinfo.h b/doomsday/plugins/common/include/saveinfo.h index e799216e96..532881c08b 100644 --- a/doomsday/plugins/common/include/saveinfo.h +++ b/doomsday/plugins/common/include/saveinfo.h @@ -45,8 +45,6 @@ class SaveInfo static SaveInfo *newWithCurrentSessionMetadata(Str const *description); - static SaveInfo *fromReader(Reader *reader); - SaveInfo &operator = (SaveInfo const &other); /** @@ -55,6 +53,23 @@ class SaveInfo */ bool isLoadable(); + /** + * Attempt to update the save info from a saved game session. If the given file @a path + * is invalid or the saved game state could not be recognized the save info is returned + * to a valid but non-loadable state. + * + * @param path Path to the resource file containing the game session header. + * + * @see isLoadable() + */ + void updateFromFile(Str const *path); + + /** + * Update the metadata associated with the save using values derived from the current game + * session. Note that this does @em not affect the copy of this save on disk. + */ + void applyCurrentSessionMetadata(); + /** * Returns the unique "identity key" of the game session. */ @@ -108,24 +123,19 @@ class SaveInfo void setGameRules(GameRuleset const &newRules); /** - * Serializes the game session info using @a writer. + * Serializes the game session header using @a writer. */ void write(Writer *writer) const; /** - * Deserializes the game session info using @a reader. + * Deserializes the game session header using @a reader. */ void read(Reader *reader); - /** - * Update the metadata associated with the save using values derived from the current game - * session. Note that this does @em not affect the copy of this save on disk. - */ - void applyCurrentSessionMetadata(); - public: /// @todo refactor away: int magic() const; void setMagic(int newMagic); + static SaveInfo *fromReader(Reader *reader); private: DENG2_PRIVATE(d) @@ -143,7 +153,6 @@ typedef void *SaveInfo; SaveInfo *SaveInfo_New(void); SaveInfo *SaveInfo_Dup(SaveInfo const *other); -SaveInfo *SaveInfo_FromReader(Reader *reader); void SaveInfo_Delete(SaveInfo *info); diff --git a/doomsday/plugins/common/src/p_saveg.cpp b/doomsday/plugins/common/src/p_saveg.cpp index 9ef326a90c..d002c77fec 100644 --- a/doomsday/plugins/common/src/p_saveg.cpp +++ b/doomsday/plugins/common/src/p_saveg.cpp @@ -878,9 +878,15 @@ void SV_DeclareGameStateReader(GameStateRecognizeFunc recognizer, GameStateReade gameStateReaderFactory.declareReader(recognizer, maker); } +bool SV_RecognizeGameState(SaveInfo &info, Str const *path) +{ + DENG_ASSERT(inited); + return gameStateReaderFactory.recognize(info, path); +} + static std::auto_ptr gameStateReaderFor(SaveInfo &info, Str const *path) { - std::auto_ptr p(gameStateReaderFactory.newReaderFor(info, path)); + std::auto_ptr p(gameStateReaderFactory.recognizeAndMakeReader(info, path)); if(!p.get()) { /// @throw Error The saved game state format was not recognized. @@ -1074,9 +1080,8 @@ void SV_LoadGameClient(uint sessionId) return; } - SaveInfo *info = new SaveInfo; Reader *reader = SV_NewReader(); - info->read(reader); + SaveInfo *info = SaveInfo::fromReader(reader); curInfo = info; @@ -1093,8 +1098,7 @@ void SV_LoadGameClient(uint sessionId) if(!Uri_Equality(gameMapUri, info->mapUri())) { G_NewGame(info->mapUri(), 0/*default*/, &info->gameRules()); - /// @todo Necessary? - G_SetGameAction(GA_NONE); + G_SetGameAction(GA_NONE); /// @todo Necessary? } else { diff --git a/doomsday/plugins/common/src/saveinfo.cpp b/doomsday/plugins/common/src/saveinfo.cpp index 563ffc8f2b..9c9b8f23a1 100644 --- a/doomsday/plugins/common/src/saveinfo.cpp +++ b/doomsday/plugins/common/src/saveinfo.cpp @@ -23,6 +23,7 @@ #include "g_common.h" #include "p_tick.h" +#include "p_saveg.h" #include "p_saveio.h" #include #include @@ -277,6 +278,32 @@ bool SaveInfo::isLoadable() return true; // It's good! } +void SaveInfo::updateFromFile(Str const *path) +{ + if(!path || Str_IsEmpty(path)) + { + // The save path cannot be accessed for some reason. Perhaps its a network path? + setDescription(0); + setSessionId(0); + return; + } + + // Is this a recognized game state? + if(!SV_RecognizeGameState(*this, path)) + { + // Clear the info for this slot. + setDescription(0); + setSessionId(0); + return; + } + + // Ensure we have a valid description. + if(Str_IsEmpty(description())) + { + setDescription(AutoStr_FromText("UNNAMED")); + } +} + void SaveInfo::write(Writer *writer) const { Writer_WriteInt32(writer, d->magic); @@ -443,11 +470,6 @@ SaveInfo *SaveInfo_Dup(SaveInfo const *other) return new SaveInfo(*other); } -SaveInfo *SaveInfo_FromReader(Reader *reader) -{ - return SaveInfo::fromReader(reader); -} - void SaveInfo_Delete(SaveInfo *info) { if(info) delete info; diff --git a/doomsday/plugins/common/src/saveslots.cpp b/doomsday/plugins/common/src/saveslots.cpp index 17311fccf7..02a17acd72 100644 --- a/doomsday/plugins/common/src/saveslots.cpp +++ b/doomsday/plugins/common/src/saveslots.cpp @@ -21,13 +21,6 @@ #include "common.h" #include "saveslots.h" -#include "gamestatereader.h" -#if __JDOOM__ -# include "doomv9gamestatereader.h" -#endif -#if __JHERETIC__ -# include "hereticv13gamestatereader.h" -#endif #include "p_saveio.h" #include #include @@ -96,55 +89,6 @@ DENG2_PIMPL(SaveSlots) return &infos[slot]; } - bool recognizeGameState(SaveInfo &info, Str const *path) - { - if(GameStateReader::recognize(info, path)) - { - return true; - } - // Perhaps an original game state? -#if __JDOOM__ - if(DoomV9GameStateReader::recognize(info, path)) - { - return true; - } -#endif -#if __JHERETIC__ - if(HereticV13GameStateReader::recognize(info, path)) - { - return true; - } -#endif - return false; - } - - void updateInfo(Str const *path, SaveInfo &info) - { - if(!path || Str_IsEmpty(path)) - { - // The save path cannot be accessed for some reason. Perhaps its a network path? - // Clear the info for this slot. - info.setDescription(0); - info.setSessionId(0); - return; - } - - // Is this a recognized game state? - if(!recognizeGameState(info, path)) - { - // Clear the info for this slot. - info.setDescription(0); - info.setSessionId(0); - return; - } - - // Ensure we have a valid description. - if(Str_IsEmpty(info.description())) - { - info.setDescription(AutoStr_FromText("UNNAMED")); - } - } - /// Re-build save info by re-scanning the save paths and populating the list. void buildInfos() { @@ -166,11 +110,11 @@ DENG2_PIMPL(SaveSlots) /// the default game-save file naming convention. for(int i = 0; i < (signed)infos.size(); ++i) { - updateInfo(self.composeSavePathForSlot(i), *infos[i]); + infos[i]->updateFromFile(self.composeSavePathForSlot(i)); } - updateInfo(self.composeSavePathForSlot(AUTO_SLOT), *autoInfo); + autoInfo->updateFromFile(self.composeSavePathForSlot(AUTO_SLOT)); #if __JHEXEN__ - updateInfo(self.composeSavePathForSlot(BASE_SLOT), *baseInfo); + baseInfo->updateFromFile(self.composeSavePathForSlot(BASE_SLOT)); #endif } @@ -324,6 +268,8 @@ void SaveSlots::clearSlot(int slot) throw InvalidSlotError("SaveSlots::clearSlot", "Invalid slot " + de::String::number(slot)); } + SaveInfo &info = saveInfo(slot); + if(d->shouldAnnounceWhenClearing(slot)) { AutoStr *ident = composeSlotIdentifier(slot); @@ -339,7 +285,8 @@ void SaveSlots::clearSlot(int slot) AutoStr *path = composeSavePathForSlot(slot); SV_RemoveFile(path); - d->updateInfo(path, saveInfo(slot)); + info.setDescription(0); + info.setSessionId(0); } void SaveSlots::copySlot(int sourceSlot, int destSlot)