mirror of
https://github.com/libretro/scummvm.git
synced 2024-11-27 11:20:40 +00:00
Commited updated version of my own patch #1868402: Basic savestate plugin API
svn-id: r30786
This commit is contained in:
parent
dd7fcd6867
commit
5fb7f7a4d6
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
77
base/game.h
77
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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
desc.clear();
|
||||
makeSavegameName(filename, slot, 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;
|
||||
|
||||
makeSavegameName(filename, slot, false);
|
||||
if (!(in = _saveFileMan->openForLoading(filename))) {
|
||||
strcpy(desc, "");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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])
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user