Skip to content

Commit

Permalink
Refactor|libcommon|SaveInfo|SaveSlots: Improved SoC wrt savegame reco…
Browse files Browse the repository at this point in the history
…gnition

SaveInfo can be updated from a saved game state directly.
  • Loading branch information
danij-deng committed Feb 16, 2014
1 parent c517f25 commit f6aadad
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 93 deletions.
53 changes: 41 additions & 12 deletions doomsday/plugins/common/include/gamestatereader.h
Expand Up @@ -81,38 +81,67 @@ 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
}

private:
struct ReaderInfo {
GameStateRecognizeFunc recognize;
GameStateReaderMakeFunc makeInstance;
GameStateReaderMakeFunc newReader;
};
typedef std::list<ReaderInfo> 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
}
};

/**
Expand Down
9 changes: 9 additions & 0 deletions doomsday/plugins/common/include/p_saveg.h
Expand Up @@ -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;

Expand Down
31 changes: 20 additions & 11 deletions doomsday/plugins/common/include/saveinfo.h
Expand Up @@ -45,8 +45,6 @@ class SaveInfo

static SaveInfo *newWithCurrentSessionMetadata(Str const *description);

static SaveInfo *fromReader(Reader *reader);

SaveInfo &operator = (SaveInfo const &other);

/**
Expand All @@ -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.
*/
Expand Down Expand Up @@ -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)
Expand All @@ -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);

Expand Down
14 changes: 9 additions & 5 deletions doomsday/plugins/common/src/p_saveg.cpp
Expand Up @@ -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<IGameStateReader> gameStateReaderFor(SaveInfo &info, Str const *path)
{
std::auto_ptr<IGameStateReader> p(gameStateReaderFactory.newReaderFor(info, path));
std::auto_ptr<IGameStateReader> p(gameStateReaderFactory.recognizeAndMakeReader(info, path));
if(!p.get())
{
/// @throw Error The saved game state format was not recognized.
Expand Down Expand Up @@ -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;

Expand All @@ -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
{
Expand Down
32 changes: 27 additions & 5 deletions doomsday/plugins/common/src/saveinfo.cpp
Expand Up @@ -23,6 +23,7 @@

#include "g_common.h"
#include "p_tick.h"
#include "p_saveg.h"
#include "p_saveio.h"
#include <cstdlib>
#include <cstring>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
67 changes: 7 additions & 60 deletions doomsday/plugins/common/src/saveslots.cpp
Expand Up @@ -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 <de/String>
#include <vector>
Expand Down Expand Up @@ -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()
{
Expand All @@ -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
}

Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit f6aadad

Please sign in to comment.