aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/plugins/dynamic-plugin.h4
-rw-r--r--base/commandLine.cpp52
-rw-r--r--base/game.cpp11
-rw-r--r--base/game.h77
-rw-r--r--base/plugins.cpp4
-rw-r--r--base/plugins.h3
-rw-r--r--engines/metaengine.h39
-rw-r--r--engines/scumm/detection.cpp36
-rw-r--r--engines/scumm/dialogs.cpp4
-rw-r--r--engines/scumm/saveload.cpp35
-rw-r--r--engines/scumm/script_v5.cpp4
-rw-r--r--engines/scumm/script_v8.cpp6
-rw-r--r--engines/scumm/scumm.h2
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);