diff options
author | Max Horn | 2008-02-04 10:15:21 +0000 |
---|---|---|
committer | Max Horn | 2008-02-04 10:15:21 +0000 |
commit | 5fb7f7a4d66c48be928440c3142b196a479ca94c (patch) | |
tree | 14d1855b57dc2306ef094887a1b735b2722148b1 | |
parent | dd7fcd686790ea6a2e9021eac5b9e1c8bff88d26 (diff) | |
download | scummvm-rg350-5fb7f7a4d66c48be928440c3142b196a479ca94c.tar.gz scummvm-rg350-5fb7f7a4d66c48be928440c3142b196a479ca94c.tar.bz2 scummvm-rg350-5fb7f7a4d66c48be928440c3142b196a479ca94c.zip |
Commited updated version of my own patch #1868402: Basic savestate plugin API
svn-id: r30786
-rw-r--r-- | backends/plugins/dynamic-plugin.h | 4 | ||||
-rw-r--r-- | base/commandLine.cpp | 52 | ||||
-rw-r--r-- | base/game.cpp | 11 | ||||
-rw-r--r-- | base/game.h | 77 | ||||
-rw-r--r-- | base/plugins.cpp | 4 | ||||
-rw-r--r-- | base/plugins.h | 3 | ||||
-rw-r--r-- | engines/metaengine.h | 39 | ||||
-rw-r--r-- | engines/scumm/detection.cpp | 36 | ||||
-rw-r--r-- | engines/scumm/dialogs.cpp | 4 | ||||
-rw-r--r-- | engines/scumm/saveload.cpp | 35 | ||||
-rw-r--r-- | engines/scumm/script_v5.cpp | 4 | ||||
-rw-r--r-- | engines/scumm/script_v8.cpp | 6 | ||||
-rw-r--r-- | engines/scumm/scumm.h | 2 |
13 files changed, 252 insertions, 25 deletions
diff --git a/backends/plugins/dynamic-plugin.h b/backends/plugins/dynamic-plugin.h index 2cf92ce602..81d7fbc4c5 100644 --- a/backends/plugins/dynamic-plugin.h +++ b/backends/plugins/dynamic-plugin.h @@ -67,6 +67,10 @@ public: return _metaengine->detectGames(fslist); } + SaveStateList listSaves(const char *target) const { + return _metaengine->listSaves(target); + } + virtual bool loadPlugin() { // Query the plugin's name MetaAllocFunc metaAlloc = (MetaAllocFunc)findSymbol("PLUGIN_MetaEngine_alloc"); diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 856ac9e173..59a9c07995 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -350,6 +350,12 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, char **ar DO_LONG_COMMAND("test-detector") END_OPTION + DO_LONG_OPTION("list-saves") + // FIXME: Need to document this. + // TODO: Make the argument optional. If no argument is given, list all savegames + // for all configured targets. + return "list-saves"; + END_OPTION DO_OPTION('c', "config") END_OPTION @@ -571,7 +577,7 @@ static void listTargets() { using namespace Common; const ConfigManager::DomainMap &domains = ConfMan.getGameDomains(); - ConfigManager::DomainMap::const_iterator iter = domains.begin(); + ConfigManager::DomainMap::const_iterator iter; for (iter = domains.begin(); iter != domains.end(); ++iter) { Common::String name(iter->_key); Common::String description(iter->_value.get("description")); @@ -587,9 +593,50 @@ static void listTargets() { } printf("%-20s %s\n", name.c_str(), description.c_str()); + } } +/** List all saves states for the given target. */ +static void listSaves(const char *target) { + // FIXME HACK + g_system->initBackend(); + + // Grab the "target" domain, if any + const Common::ConfigManager::Domain *domain = ConfMan.getDomain(target); + + // Grab the gameid from the domain resp. use the target as gameid + Common::String gameid; + if (domain) + gameid = domain->get("gameid"); + if (gameid.empty()) + gameid = target; + gameid.toLowercase(); // Normalize it to lower case + + // Find the plugin that will handle the specified gameid + const Plugin *plugin = 0; + GameDescriptor game = Base::findGame(gameid, &plugin); + + if (!plugin) { + error("Could not find any plugin to handle gameid '%s' (target '%s')", gameid.c_str(), target); + return; + } + + // Query the plugin for a list of savegames + SaveStateList saveList = plugin->listSaves(target); + + // TODO: Include more info about the target (desc, engine name, ...) ??? + printf("Saves for target '%s':\n", target); + printf(" Slot Description \n" + " ---- ------------------------------------------------------\n"); + + for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { + printf(" %-4s %s\n", x->save_slot().c_str(), x->description().c_str()); + // TODO: Could also iterate over the full hashmap, printing all key-value pairs + } +} + + #ifdef DETECTOR_TESTING_HACK static void runDetectorTest() { // HACK: The following code can be used to test the detection code of our @@ -671,6 +718,9 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { } else if (command == "list-games") { listGames(); return false; + } else if (command == "list-saves") { + listSaves(settings["list-saves"].c_str()); + return false; } else if (command == "version") { printf("%s\n", gScummVMFullVersion); printf("Features compiled in: %s\n", gScummVMFeatures); diff --git a/base/game.cpp b/base/game.cpp index 9aaaf3a6af..a79cfddec9 100644 --- a/base/game.cpp +++ b/base/game.cpp @@ -25,6 +25,8 @@ #include "base/game.h" #include "base/plugins.h" +#include "graphics/surface.h" + const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) { const PlainGameDescriptor *g = list; @@ -66,6 +68,15 @@ void GameDescriptor::updateDesc(const char *extra) { } } +void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) { + if (_thumbnail && _thumbnail != t) { + _thumbnail->free(); + delete _thumbnail; + } + _thumbnail = t; +} + + namespace Base { // TODO: Find a better name & place for this function. diff --git a/base/game.h b/base/game.h index 0764b7fbac..7dddea7b30 100644 --- a/base/game.h +++ b/base/game.h @@ -30,6 +30,10 @@ #include "common/array.h" #include "common/hash-str.h" +namespace Graphics { + class Surface; +} + /** * A simple structure used to map gameids (like "monkey", "sword1", ...) to * nice human readable and descriptive game titles (like "The Secret of Monkey Island"). @@ -67,7 +71,7 @@ public: setVal("description", pgd.description); } - GameDescriptor(Common::String g, Common::String d, Common::Language l = Common::UNK_LANG, + GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l = Common::UNK_LANG, Common::Platform p = Common::kPlatformUnknown) { setVal("gameid", g); setVal("description", d); @@ -103,6 +107,77 @@ public: } }; +/** + * A hashmap describing details about a given save state. + * TODO + * Guaranteed to contain save_slot, filename and description values. + * Additional ideas: Playtime, creation date, thumbnail, ... + */ +class SaveStateDescriptor : public Common::StringMap { +protected: + Graphics::Surface *_thumbnail; // can be NULL +public: + SaveStateDescriptor() : _thumbnail(0) { + setVal("save_slot", "-1"); // FIXME: default to 0 (first slot) or to -1 (invalid slot) ? + setVal("description", ""); + setVal("filename", ""); + } + + SaveStateDescriptor(int s, const Common::String &d, const Common::String &f) : _thumbnail(0) { + char buf[16]; + sprintf(buf, "%d", s); + setVal("save_slot", buf); + setVal("description", d); + setVal("filename", f); + } + + SaveStateDescriptor(const Common::String &s, const Common::String &d, const Common::String &f) : _thumbnail(0) { + setVal("save_slot", s); + setVal("description", d); + setVal("filename", f); + } + + ~SaveStateDescriptor() { + setThumbnail(0); + } + + /** The saveslot id, as it would be passed to the "-x" command line switch. */ + Common::String &save_slot() { return getVal("save_slot"); } + + /** The saveslot id, as it would be passed to the "-x" command line switch (read-only variant). */ + const Common::String &save_slot() const { return getVal("save_slot"); } + + /** A human readable description of the save state. */ + Common::String &description() { return getVal("description"); } + + /** A human readable description of the save state (read-only variant). */ + const Common::String &description() const { return getVal("description"); } + + /** The filename of the savestate, for use with the SaveFileManager API. */ + Common::String &filename() { return getVal("filename"); } + + /** The filename of the savestate, for use with the SaveFileManager API (read-only variant). */ + const Common::String &filename() const { return getVal("filename"); } + + /** + * Return a thumbnail graphics surface representing the savestate visually + * This is usually a scaled down version of the game graphics. The size + * should be either 160x100 or 160x120 pixels, depending on the aspect + * ratio of the game. If another ratio is required, contact the core team. + * + * TODO: it is probably a bad idea to read this for *all* games at once, + * at least on low-end devices. So this info should probably normally only + * be included optionally. I.e. only upon a query for a specific savegame... + * To this end, add a getFullSaveStateInfo(target, slot) to the plugin API. + */ + const Graphics::Surface *getThumbnail() const { return _thumbnail; } + + + void setThumbnail(Graphics::Surface *t); +}; + +/** List of savestates. */ +typedef Common::Array<SaveStateDescriptor> SaveStateList; class Plugin; diff --git a/base/plugins.cpp b/base/plugins.cpp index 67ead04649..da6fe36826 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -67,6 +67,10 @@ public: GameList detectGames(const FSList &fslist) const { return _metaengine->detectGames(fslist); } + + SaveStateList listSaves(const char *target) const { + return _metaengine->listSaves(target); + } }; class StaticPluginProvider : public PluginProvider { diff --git a/base/plugins.h b/base/plugins.h index 966d65d959..9f8383f165 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -47,6 +47,7 @@ class Plugin { public: virtual ~Plugin() {} +// virtual bool isLoaded() const = 0; // TODO virtual bool loadPlugin() = 0; virtual void unloadPlugin() = 0; @@ -58,6 +59,8 @@ public: virtual GameDescriptor findGame(const char *gameid) const = 0; virtual GameList detectGames(const FSList &fslist) const = 0; + virtual SaveStateList listSaves(const char *target) const = 0; + virtual PluginError createInstance(OSystem *syst, Engine **engine) const = 0; }; diff --git a/engines/metaengine.h b/engines/metaengine.h index b0b71de126..df124c57c5 100644 --- a/engines/metaengine.h +++ b/engines/metaengine.h @@ -46,15 +46,54 @@ class MetaEngine { public: virtual ~MetaEngine() {} + /** Returns the name of the engine. */ virtual const char *getName() const = 0; + + /** Returns some copyright information about the engine. */ virtual const char *getCopyright() const = 0; + // virtual int getVersion() const = 0; // TODO! + /** Returns a list of games supported by this engine. */ virtual GameList getSupportedGames() const = 0; + + /** Query the engine for a GameDescriptor for the specified gameid, if any. */ virtual GameDescriptor findGame(const char *gameid) const = 0; + + /** + * Runs the engine's game detector on the given list of files, and returns a + * (possibly empty) list of games supported by the engine which it was able + * to detect amongst the given files. + */ virtual GameList detectGames(const FSList &fslist) const = 0; + /** + * Tries to instantiate an engine instance based on the settings of + * the currently active ConfMan target. That is, the MetaEngine should + * query the ConfMan singleton for the target, gameid, path etc. data. + * + * @param syst Pointer to the global OSystem object + * @param engine Pointer to a pointer which the MetaEngine sets to + * the newly create Engine, or 0 in case of an error + * @return a PluginError describing the error which occurred, or kNoError + */ virtual PluginError createInstance(OSystem *syst, Engine **engine) const = 0; + + /** + * Return a list of all save states associated with the given target. + * + * In general, the caller will already have ensured that this (Meta)Engine + * is responsible for the specified target by using findGame on it resp. + * on the associated gameid from the relevant ConfMan entry, if present. + * + * The default implementation returns an empty list. + * + * @param target name of a config manager target + * @return a list of save state descriptors + */ + virtual SaveStateList listSaves(const char *target) const { + return SaveStateList(); + } }; #endif diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index b1f14acc61..2b30780a6b 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -29,6 +29,8 @@ #include "common/fs.h" #include "common/list.h" #include "common/md5.h" +#include "common/savefile.h" +#include "common/system.h" #include "scumm/detection.h" #include "scumm/detection_tables.h" @@ -675,6 +677,8 @@ public: virtual GameList detectGames(const FSList &fslist) const; virtual PluginError createInstance(OSystem *syst, Engine **engine) const; + + virtual SaveStateList listSaves(const char *target) const; }; GameList ScummMetaEngine::getSupportedGames() const { @@ -928,4 +932,36 @@ const char *ScummMetaEngine::getCopyright() const { "Humongous SCUMM Games (C) Humongous"; } +namespace Scumm { + extern bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion); +} + +SaveStateList ScummMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringList filenames; + Common::String saveDesc; + Common::String pattern = target; + pattern += ".s??"; + + 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 2 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 2); + + if (slotNum >= 0 && slotNum <= 99) { + Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); + if (in) { + Scumm::getSavegameName(in, saveDesc, 0); // FIXME: heversion?!? + saveList.push_back(SaveStateDescriptor(slotNum, saveDesc, *file)); + delete in; + } + } + } + + return saveList; +} + REGISTER_PLUGIN(SCUMM, ScummMetaEngine); diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index e008ce2179..23efa05dd2 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -421,16 +421,14 @@ void SaveLoadChooser::updateInfos() { Common::StringList generateSavegameList(ScummEngine *scumm, bool saveMode) { // Get savegame descriptions Common::StringList descriptions; - char name[32]; uint i = saveMode ? 1 : 0; //the autosave is on slot #0 bool avail_saves[81]; scumm->listSavegames(avail_saves, ARRAYSIZE(avail_saves)); for (; i < ARRAYSIZE(avail_saves); i++) { + Common::String name; if (avail_saves[i]) scumm->getSavegameName(i, name); - else - name[0] = 0; descriptions.push_back(name); } diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index b9e3a9287b..8d23e47bbf 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -431,39 +431,46 @@ void ScummEngine::listSavegames(bool *marks, int num) { } } -bool ScummEngine::getSavegameName(int slot, char *desc) { +bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion); + +bool ScummEngine::getSavegameName(int slot, Common::String &desc) { + Common::InSaveFile *in = 0; + bool result = false; char filename[256]; - Common::SeekableReadStream *in; - SaveGameHeader hdr; + desc.clear(); makeSavegameName(filename, slot, false); - if (!(in = _saveFileMan->openForLoading(filename))) { - strcpy(desc, ""); - return false; + in = _saveFileMan->openForLoading(filename); + if (in) { + result = Scumm::getSavegameName(in, desc, _game.heversion); + delete in; } + return result; +} + +bool getSavegameName(Common::InSaveFile *in, Common::String &desc, int heversion) { + SaveGameHeader hdr; if (!loadSaveGameHeader(in, hdr)) { - delete in; - strcpy(desc, "Invalid savegame"); + desc = "Invalid savegame"; return false; } - delete in; if (hdr.ver > CURRENT_VER) hdr.ver = TO_LE_32(hdr.ver); if (hdr.ver < VER(7) || hdr.ver > CURRENT_VER) { - strcpy(desc, "Invalid version"); + desc = "Invalid version"; return false; } // We (deliberately) broke HE savegame compatibility at some point. - if (hdr.ver < VER(57) && _game.heversion >= 60) { - strcpy(desc, "Unsupported version"); + if (hdr.ver < VER(57) && heversion >= 60) { + desc = "Unsupported version"; return false; } - memcpy(desc, hdr.name, sizeof(hdr.name)); - desc[sizeof(hdr.name) - 1] = 0; + hdr.name[sizeof(hdr.name) - 1] = 0; + desc = hdr.name; return true; } diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index d54ec4b45a..431321f459 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -942,7 +942,6 @@ void ScummEngine_v5::loadVars() { int slotSize; byte* slotContent; int savegameId; - char name[32]; bool avail_saves[100]; if (a == STRINGID_IQ_SERIES && b == STRINGID_IQ_SERIES) { @@ -960,9 +959,10 @@ void ScummEngine_v5::loadVars() { // load savegame names savegameId = slot - a + 1; + Common::String name; if (avail_saves[savegameId] && getSavegameName(savegameId, name)) { int pos; - char *ptr = name; + const char *ptr = name.c_str(); // slotContent ends with {'\0','@'} -> max. length = slotSize-2 for (pos = 0; pos < slotSize - 2; ++pos) { if (!ptr[pos]) diff --git a/engines/scumm/script_v8.cpp b/engines/scumm/script_v8.cpp index b97626d3d9..08629afb07 100644 --- a/engines/scumm/script_v8.cpp +++ b/engines/scumm/script_v8.cpp @@ -1235,9 +1235,9 @@ void ScummEngine_v8::o8_kernelSetFunctions() { removeBlastTexts(); break; case 25: { // saveGameReadName - char name[30]; + Common::String name; if (getSavegameName(args[1], name)) { - int size = resStrLen((const byte *)name) + 1; + int size = name.size() + 1; _res->nukeResource(rtString, args[2]); ArrayHeader *ah = (ArrayHeader *)_res->createResource(rtString, args[2], size + sizeof(ArrayHeader)); @@ -1245,7 +1245,7 @@ void ScummEngine_v8::o8_kernelSetFunctions() { ah->dim1 = TO_LE_16(size + 1); ah->dim2 = TO_LE_16(1); - memcpy(getStringAddress(args[2]), name, size); + memcpy(getStringAddress(args[2]), name.c_str(), size); } break; } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index f636fee825..767aa53cf8 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -623,7 +623,7 @@ protected: int getKeyState(int key); public: - bool getSavegameName(int slot, char *desc); + bool getSavegameName(int slot, Common::String &desc); void listSavegames(bool *marks, int num); void requestSave(int slot, const char *name, bool temporary = false); |