mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-20 17:03:05 +00:00
import some code from scummvm to allow implement later multi engine support
This commit is contained in:
parent
697af45d7a
commit
ac7307728b
518
base/commandLine.cpp
Normal file
518
base/commandLine.cpp
Normal file
@ -0,0 +1,518 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/metaengine.h"
|
||||
#include "base/commandLine.h"
|
||||
#include "base/plugins.h"
|
||||
#include "base/version.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/system.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Base {
|
||||
|
||||
#ifndef DISABLE_COMMAND_LINE
|
||||
|
||||
static const char USAGE_STRING[] =
|
||||
"%s: %s\n"
|
||||
"Usage: %s [OPTIONS]... [GAME]\n"
|
||||
"\n"
|
||||
"Try '%s --help' for more options.\n"
|
||||
;
|
||||
|
||||
// DONT FIXME: DO NOT ORDER ALPHABETICALLY, THIS IS ORDERED BY IMPORTANCE/CATEGORY! :)
|
||||
#if defined(PALMOS_MODE) || defined(__SYMBIAN32__) || defined(__GP32__)
|
||||
static const char HELP_STRING[] = "NoUsageString"; // save more data segment space
|
||||
#else
|
||||
static const char HELP_STRING[] =
|
||||
"Residual - Virtual machine to run 3D adventure games\n"
|
||||
"Usage: %s [OPTIONS]... [GAME]\n"
|
||||
" -v, --version Display Residual version information and exit\n"
|
||||
" -h, --help Display a brief help text and exit\n"
|
||||
" -z, --list-games Display list of supported games and exit\n"
|
||||
" -t, --list-targets Display list of configured targets and exit\n"
|
||||
|
||||
"\n"
|
||||
" -c, --config=CONFIG Use alternate configuration file\n"
|
||||
" -p, --path=PATH Path to where the game is installed\n"
|
||||
" -f, --fullscreen Force full-screen mode\n"
|
||||
" -q, --language=LANG Select language (en,de,fr,it,pt,es,jp,zh,kr,se,gb,\n"
|
||||
" hb,ru,cz)\n"
|
||||
" -m, --music-volume=NUM Set the music volume, 0-127 (default: 127)\n"
|
||||
" -s, --sfx-volume=NUM Set the sfx volume, 0-127 (default: 127)\n"
|
||||
" -r, --speech-volume=NUM Set the speech volume, 0-127 (default: 127)\n"
|
||||
" --speech-mode=NUM Set the mode of speech 1-Text only, 2-Voice Only, 3-Voice and Text\n"
|
||||
" --text-speed=NUM Set talk speed for games (default: 7)\n"
|
||||
" --soft-renderer=BOOL Set the turn on/off software 3D renderer: TRUE/FALSE\n"
|
||||
" -d, --debuglevel=NUM Set debug verbosity level\n"
|
||||
" --debugflags=FLAGS Enables engine specific debug flags\n"
|
||||
" --savepath=PATH Path to where savegames are stored\n"
|
||||
" --extrapath=PATH Extra path to additional game data\n"
|
||||
" --output-rate=RATE Select output sample rate in Hz (e.g. 22050)\n"
|
||||
" --game-devel-mode=BOOL Set the turn on/off game engine development mode: TRUE/FALSE\n"
|
||||
" --joystick[=NUM] Enable joystick input (default: 0 = first joystick)\n"
|
||||
" --gamma=FLOAT Set the gamma correction\n"
|
||||
" --show-fps=BOOL Set the turn on/off display FPS info: TRUE/FALSE\n"
|
||||
" --engine-speed=NUM Set engine speed (default: 30)\n"
|
||||
" -b, --boot-param=NUM Pass number to the boot script (boot param)\n"
|
||||
"\n"
|
||||
;
|
||||
#endif
|
||||
|
||||
static const char *s_appName = "residual";
|
||||
|
||||
static void usage(const char *s, ...) GCC_PRINTF(1, 2);
|
||||
|
||||
static void usage(const char *s, ...) {
|
||||
char buf[STRINGBUFLEN];
|
||||
va_list va;
|
||||
|
||||
va_start(va, s);
|
||||
vsnprintf(buf, STRINGBUFLEN, s, va);
|
||||
va_end(va);
|
||||
|
||||
#if !(defined(__GP32__) || defined (__SYMBIAN32__))
|
||||
printf(USAGE_STRING, s_appName, buf, s_appName, s_appName);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif // DISABLE_COMMAND_LINE
|
||||
|
||||
|
||||
void registerDefaults() {
|
||||
ConfMan.registerDefault("platform", Common::kPlatformPC);
|
||||
ConfMan.registerDefault("language", "en");
|
||||
ConfMan.registerDefault("autosave_period", 5 * 60); // By default, trigger autosave every 5 minutes
|
||||
|
||||
ConfMan.registerDefault("music_volume", 127);
|
||||
ConfMan.registerDefault("sfx_volume", 127);
|
||||
ConfMan.registerDefault("voice_volume", 127);
|
||||
ConfMan.registerDefault("speech_mode", "3");
|
||||
|
||||
ConfMan.registerDefault("boot_param", "");
|
||||
ConfMan.registerDefault("text_speed", "70");
|
||||
|
||||
ConfMan.registerDefault("path", ".");
|
||||
|
||||
ConfMan.registerDefault("soft_renderer", "TRUE");
|
||||
ConfMan.registerDefault("fullscreen", "FALSE");
|
||||
ConfMan.registerDefault("gamma", "1.0");
|
||||
ConfMan.registerDefault("show_fps", "FALSE");
|
||||
ConfMan.registerDefault("engine_speed", "30");
|
||||
ConfMan.registerDefault("game_devel_mode", "");
|
||||
|
||||
ConfMan.registerDefault("joystick", "1");
|
||||
|
||||
ConfMan.registerDefault("disable_sdl_parachute", false);
|
||||
|
||||
ConfMan.registerDefault("record_mode", "none");
|
||||
ConfMan.registerDefault("record_file_name", "record.bin");
|
||||
ConfMan.registerDefault("record_temp_file_name", "record.tmp");
|
||||
ConfMan.registerDefault("record_time_file_name", "record.time");
|
||||
}
|
||||
|
||||
//
|
||||
// Various macros used by the command line parser.
|
||||
//
|
||||
|
||||
#ifndef DISABLE_COMMAND_LINE
|
||||
|
||||
// Use this for options which have an *optional* value
|
||||
#define DO_OPTION_OPT(shortCmd, longCmd, defaultVal) \
|
||||
if (isLongCmd ? (!strcmp(s+2, longCmd) || !memcmp(s+2, longCmd"=", sizeof(longCmd"=") - 1)) : (tolower(s[1]) == shortCmd)) { \
|
||||
s += 2; \
|
||||
if (isLongCmd) { \
|
||||
s += sizeof(longCmd) - 1; \
|
||||
if (*s == '=') \
|
||||
s++; \
|
||||
} \
|
||||
const char *option = s; \
|
||||
if (*s == '\0' && !isLongCmd) { option = s2; i++; } \
|
||||
if (!option || *option == '\0') option = defaultVal; \
|
||||
if (option) settings[longCmd] = option;
|
||||
|
||||
// Use this for options which have a required (string) value
|
||||
#define DO_OPTION(shortCmd, longCmd) \
|
||||
DO_OPTION_OPT(shortCmd, longCmd, 0) \
|
||||
if (!option) usage("Option '%s' requires an argument", argv[isLongCmd ? i : i-1]);
|
||||
|
||||
// Use this for options which have a required integer value
|
||||
#define DO_OPTION_INT(shortCmd, longCmd) \
|
||||
DO_OPTION(shortCmd, longCmd) \
|
||||
char *endptr = 0; \
|
||||
int intValue; intValue = (int)strtol(option, &endptr, 0); \
|
||||
if (endptr == NULL || *endptr != 0) usage("--%s: Invalid number '%s'", longCmd, option);
|
||||
|
||||
// Use this for boolean options; this distinguishes between "-x" and "-X",
|
||||
// resp. between "--some-option" and "--no-some-option".
|
||||
#define DO_OPTION_BOOL(shortCmd, longCmd) \
|
||||
if (isLongCmd ? (!strcmp(s+2, longCmd) || !strcmp(s+2, "no-"longCmd)) : (tolower(s[1]) == shortCmd)) { \
|
||||
bool boolValue = (islower(s[1]) != 0); \
|
||||
s += 2; \
|
||||
if (isLongCmd) { \
|
||||
boolValue = !strcmp(s, longCmd); \
|
||||
s += boolValue ? (sizeof(longCmd) - 1) : (sizeof("no-"longCmd) - 1); \
|
||||
} \
|
||||
if (*s != '\0') goto unknownOption; \
|
||||
const char *option = boolValue ? "true" : "false"; \
|
||||
settings[longCmd] = option;
|
||||
|
||||
// Use this for options which never have a value, i.e. for 'commands', like "--help".
|
||||
#define DO_COMMAND(shortCmd, longCmd) \
|
||||
if (isLongCmd ? (!strcmp(s+2, longCmd)) : (tolower(s[1]) == shortCmd)) { \
|
||||
s += 2; \
|
||||
if (isLongCmd) \
|
||||
s += sizeof(longCmd) - 1; \
|
||||
if (*s != '\0') goto unknownOption; \
|
||||
return longCmd;
|
||||
|
||||
|
||||
#define DO_LONG_OPTION_OPT(longCmd, d) DO_OPTION_OPT(0, longCmd, d)
|
||||
#define DO_LONG_OPTION(longCmd) DO_OPTION(0, longCmd)
|
||||
#define DO_LONG_OPTION_INT(longCmd) DO_OPTION_INT(0, longCmd)
|
||||
#define DO_LONG_OPTION_BOOL(longCmd) DO_OPTION_BOOL(0, longCmd)
|
||||
#define DO_LONG_COMMAND(longCmd) DO_COMMAND(0, longCmd)
|
||||
|
||||
// End an option handler
|
||||
#define END_OPTION \
|
||||
continue; \
|
||||
}
|
||||
|
||||
|
||||
Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv) {
|
||||
const char *s, *s2;
|
||||
|
||||
// argv[0] contains the name of the executable.
|
||||
if (argv && argv[0]) {
|
||||
s = strrchr(argv[0], '/');
|
||||
s_appName = s ? (s+1) : argv[0];
|
||||
}
|
||||
|
||||
// We store all command line settings into a string map.
|
||||
|
||||
// Iterate over all command line arguments and parse them into our string map.
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
s = argv[i];
|
||||
s2 = (i < argc-1) ? argv[i+1] : 0;
|
||||
|
||||
if (s[0] != '-') {
|
||||
// The argument doesn't start with a dash, so it's not an option.
|
||||
// Hence it must be the target name. We currently enforce that
|
||||
// this always comes last.
|
||||
if (i != argc - 1)
|
||||
usage("Stray argument '%s'", s);
|
||||
|
||||
// We defer checking whether this is a valid target to a later point.
|
||||
return s;
|
||||
} else {
|
||||
|
||||
bool isLongCmd = (s[0] == '-' && s[1] == '-');
|
||||
|
||||
DO_COMMAND('h', "help")
|
||||
END_OPTION
|
||||
|
||||
DO_COMMAND('v', "version")
|
||||
END_OPTION
|
||||
|
||||
DO_COMMAND('t', "list-targets")
|
||||
END_OPTION
|
||||
|
||||
DO_COMMAND('z', "list-games")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION('c', "config")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION_INT('b', "boot-param")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION_OPT('d', "debuglevel", "0")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("debugflags")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION_INT("output-rate")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION_BOOL('f', "fullscreen")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION_INT('m', "music-volume")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION('s', "sfx-volume")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION('r', "voice-volume")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION('q', "language")
|
||||
if (Common::parseLanguage(option) == Common::UNK_LANG)
|
||||
usage("Unrecognized language '%s'", option);
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION_OPT("joystick", "0")
|
||||
settings["joystick_num"] = option;
|
||||
settings.erase("joystick");
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("show-fps")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("soft-renderer")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("engine-speed")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("manny-state")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("movement")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("transcript")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("game-devel-mode")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("gamma")
|
||||
END_OPTION
|
||||
|
||||
DO_OPTION('p', "path")
|
||||
Common::FSNode path(option);
|
||||
if (!path.exists()) {
|
||||
usage("Non-existent game path '%s'", option);
|
||||
} else if (!path.isReadable()) {
|
||||
usage("Non-readable game path '%s'", option);
|
||||
}
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION_BOOL("disable-sdl-parachute")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("savepath")
|
||||
Common::FSNode path(option);
|
||||
if (!path.exists()) {
|
||||
usage("Non-existent savegames path '%s'", option);
|
||||
} else if (!path.isWritable()) {
|
||||
usage("Non-writable savegames path '%s'", option);
|
||||
}
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("extrapath")
|
||||
Common::FSNode path(option);
|
||||
if (!path.exists()) {
|
||||
usage("Non-existent extra path '%s'", option);
|
||||
} else if (!path.isReadable()) {
|
||||
usage("Non-readable extra path '%s'", option);
|
||||
}
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("text-speed")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("speech-mode")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("record-mode")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("record-file-name")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("record-temp-file-name")
|
||||
END_OPTION
|
||||
|
||||
DO_LONG_OPTION("record-time-file-name")
|
||||
END_OPTION
|
||||
|
||||
#ifdef IPHONE
|
||||
// This is automatically set when launched from the Springboard.
|
||||
DO_LONG_OPTION_OPT("launchedFromSB", 0)
|
||||
END_OPTION
|
||||
#endif
|
||||
|
||||
unknownOption:
|
||||
// If we get till here, the option is unhandled and hence unknown.
|
||||
usage("Unrecognized option '%s'", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
/** List all supported game IDs, i.e. all games which any loaded plugin supports. */
|
||||
static void listGames() {
|
||||
printf("Game ID Full Title \n"
|
||||
"-------------------- ------------------------------------------------------\n");
|
||||
|
||||
const EnginePlugin::List &plugins = EngineMan.getPlugins();
|
||||
EnginePlugin::List::const_iterator iter = plugins.begin();
|
||||
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
|
||||
GameList list = (**iter)->getSupportedGames();
|
||||
for (GameList::iterator v = list.begin(); v != list.end(); ++v) {
|
||||
printf("%-20s %s\n", v->gameid().c_str(), v->description().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** List all targets which are configured in the config file. */
|
||||
static void listTargets() {
|
||||
printf("Target Description \n"
|
||||
"-------------------- ------------------------------------------------------\n");
|
||||
|
||||
using namespace Common;
|
||||
const ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
|
||||
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"));
|
||||
|
||||
if (description.empty()) {
|
||||
// FIXME: At this point, we should check for a "gameid" override
|
||||
// to find the proper desc. In fact, the platform probably should
|
||||
// be taken into account, too.
|
||||
Common::String gameid(name);
|
||||
GameDescriptor g = EngineMan.findGame(gameid);
|
||||
if (g.description().size() > 0)
|
||||
description = g.description();
|
||||
}
|
||||
|
||||
printf("%-20s %s\n", name.c_str(), description.c_str());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else // DISABLE_COMMAND_LINE
|
||||
|
||||
|
||||
Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv) {
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
|
||||
#endif // DISABLE_COMMAND_LINE
|
||||
|
||||
|
||||
bool processSettings(Common::String &command, Common::StringMap &settings) {
|
||||
|
||||
#ifndef DISABLE_COMMAND_LINE
|
||||
|
||||
// Handle commands passed via the command line (like --list-targets and
|
||||
// --list-games). This must be done after the config file and the plugins
|
||||
// have been loaded.
|
||||
if (command == "list-targets") {
|
||||
listTargets();
|
||||
return false;
|
||||
} else if (command == "list-games") {
|
||||
listGames();
|
||||
return false;
|
||||
} else if (command == "version") {
|
||||
printf("%s\n", gResidualFullVersion);
|
||||
printf("Features compiled in: %s\n", gResidualFeatures);
|
||||
return false;
|
||||
} else if (command == "help") {
|
||||
printf(HELP_STRING, s_appName);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // DISABLE_COMMAND_LINE
|
||||
|
||||
|
||||
// If a target was specified, check whether there is either a game
|
||||
// domain (i.e. a target) matching this argument, or alternatively
|
||||
// whether there is a gameid matching that name.
|
||||
if (!command.empty()) {
|
||||
GameDescriptor gd = EngineMan.findGame(command);
|
||||
if (ConfMan.hasGameDomain(command) || !gd.gameid().empty()) {
|
||||
bool idCameFromCommandLine = false;
|
||||
|
||||
// WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching
|
||||
// undefined target adds launcher entry"
|
||||
//
|
||||
// We designate gameids which come strictly from command line
|
||||
// so AdvancedDetector will not save config file with invalid
|
||||
// gameid in case target autoupgrade was performed
|
||||
if (!ConfMan.hasGameDomain(command)) {
|
||||
idCameFromCommandLine = true;
|
||||
}
|
||||
|
||||
ConfMan.setActiveDomain(command);
|
||||
|
||||
if (idCameFromCommandLine)
|
||||
ConfMan.set("id_came_from_command_line", "1");
|
||||
|
||||
} else {
|
||||
#ifndef DISABLE_COMMAND_LINE
|
||||
usage("Unrecognized game target '%s'", command.c_str());
|
||||
#endif // DISABLE_COMMAND_LINE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The user can override the savepath with the SCUMMVM_SAVEPATH
|
||||
// environment variable. This is weaker than a --savepath on the
|
||||
// command line, but overrides the default savepath, hence it is
|
||||
// handled here, just before the command line gets parsed.
|
||||
#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__)
|
||||
if (!settings.contains("savepath")) {
|
||||
const char *dir = getenv("RESIDUAL_SAVEPATH");
|
||||
if (dir && *dir && strlen(dir) < MAXPATHLEN) {
|
||||
Common::FSNode saveDir(dir);
|
||||
if (!saveDir.exists()) {
|
||||
warning("Non-existent RESIDUAL_SAVEPATH save path. It will be ignored.");
|
||||
} else if (!saveDir.isWritable()) {
|
||||
warning("Non-writable RESIDUAL_SAVEPATH save path. It will be ignored.");
|
||||
} else {
|
||||
settings["savepath"] = dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Finally, store the command line settings into the config manager.
|
||||
for (Common::StringMap::const_iterator x = settings.begin(); x != settings.end(); ++x) {
|
||||
Common::String key(x->_key);
|
||||
Common::String value(x->_value);
|
||||
|
||||
// Replace any "-" in the key by "_" (e.g. change "save-slot" to "save_slot").
|
||||
for (Common::String::iterator c = key.begin(); c != key.end(); ++c)
|
||||
if (*c == '-')
|
||||
*c = '_';
|
||||
|
||||
// Store it into ConfMan.
|
||||
ConfMan.set(key, value, Common::ConfigManager::kTransientDomain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Base
|
40
base/commandLine.h
Normal file
40
base/commandLine.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BASE_OPTIONS_H
|
||||
#define BASE_OPTIONS_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Base {
|
||||
|
||||
void registerDefaults();
|
||||
Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv);
|
||||
bool processSettings(Common::String &command, Common::StringMap &settings);
|
||||
|
||||
} // End of namespace Base
|
||||
|
||||
#endif
|
1
base/internal_version.h
Normal file
1
base/internal_version.h
Normal file
@ -0,0 +1 @@
|
||||
#define RESIDUAL_VERSION "0.0.6svn"
|
1
base/internal_version.h.in
Normal file
1
base/internal_version.h.in
Normal file
@ -0,0 +1 @@
|
||||
#define RESIDUAL_VERSION "@VERSION@"
|
275
base/main.cpp
Normal file
275
base/main.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \mainpage %Residual Source Reference
|
||||
*
|
||||
* These pages contains a cross referenced documentation for the %Residual source code,
|
||||
* generated with Doxygen (http://www.doxygen.org) directly from the source.
|
||||
* Currently not much is actually properly documented, but at least you can get an overview
|
||||
* of almost all the classes, methods and variables, and how they interact.
|
||||
*/
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/metaengine.h"
|
||||
#include "base/commandLine.h"
|
||||
#include "base/plugins.h"
|
||||
#include "base/version.h"
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/system.h"
|
||||
|
||||
|
||||
static const EnginePlugin *detectPlugin() {
|
||||
const EnginePlugin *plugin = 0;
|
||||
|
||||
// Make sure the gameid is set in the config manager, and that it is lowercase.
|
||||
Common::String gameid(ConfMan.getActiveDomainName());
|
||||
assert(!gameid.empty());
|
||||
if (ConfMan.hasKey("gameid"))
|
||||
gameid = ConfMan.get("gameid");
|
||||
gameid.toLowercase();
|
||||
ConfMan.set("gameid", gameid);
|
||||
|
||||
// Query the plugins and find one that will handle the specified gameid
|
||||
printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveDomainName().c_str(), gameid.c_str());
|
||||
printf(" Looking for a plugin supporting this gameid... ");
|
||||
GameDescriptor game = EngineMan.findGame(gameid, &plugin);
|
||||
|
||||
if (plugin == 0) {
|
||||
printf("failed\n");
|
||||
warning("%s is an invalid gameid. Use the --list-games option to list supported gameid", gameid.c_str());
|
||||
return 0;
|
||||
} else {
|
||||
printf("%s\n", plugin->getName());
|
||||
}
|
||||
|
||||
// FIXME: Do we really need this one?
|
||||
printf(" Starting '%s'\n", game.description().c_str());
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
// TODO: specify the possible return values here
|
||||
static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const Common::String &edebuglevels) {
|
||||
// Determine the game data path, for validation and error messages
|
||||
Common::FSNode dir(ConfMan.get("path"));
|
||||
Common::Error err = Common::kNoError;
|
||||
Engine *engine = 0;
|
||||
|
||||
// Verify that the game path refers to an actual directory
|
||||
if (!(dir.exists() && dir.isDirectory()))
|
||||
err = Common::kInvalidPathError;
|
||||
|
||||
// Create the game engine
|
||||
if (err == Common::kNoError)
|
||||
err = (*plugin)->createInstance(&system, &engine);
|
||||
|
||||
// Check for errors
|
||||
if (!engine || err != Common::kNoError) {
|
||||
const char *errMsg = 0;
|
||||
switch (err) {
|
||||
case Common::kInvalidPathError:
|
||||
errMsg = "Invalid game path";
|
||||
break;
|
||||
case Common::kNoGameDataFoundError:
|
||||
errMsg = "Unable to locate game data";
|
||||
break;
|
||||
default:
|
||||
errMsg = "Unknown error";
|
||||
}
|
||||
|
||||
warning("%s failed to instantiate engine: %s (target '%s', path '%s')",
|
||||
plugin->getName(),
|
||||
errMsg,
|
||||
ConfMan.getActiveDomainName().c_str(),
|
||||
dir.getPath().c_str()
|
||||
);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Set the window caption to the game name
|
||||
Common::String caption(ConfMan.get("description"));
|
||||
|
||||
Common::String desc = EngineMan.findGame(ConfMan.get("gameid")).description();
|
||||
if (caption.empty() && !desc.empty())
|
||||
caption = desc;
|
||||
if (caption.empty())
|
||||
caption = ConfMan.getActiveDomainName(); // Use the domain (=target) name
|
||||
if (!caption.empty()) {
|
||||
system.setWindowCaption(caption.c_str());
|
||||
}
|
||||
|
||||
//
|
||||
// Setup various paths in the SearchManager
|
||||
//
|
||||
|
||||
// Add the game path to the directory search list
|
||||
SearchMan.addDirectory(dir.getPath(), dir, 0, 4);
|
||||
|
||||
// Add extrapath (if any) to the directory search list
|
||||
if (ConfMan.hasKey("extrapath")) {
|
||||
dir = Common::FSNode(ConfMan.get("extrapath"));
|
||||
SearchMan.addDirectory(dir.getPath(), dir);
|
||||
}
|
||||
|
||||
// If a second extrapath is specified on the app domain level, add that as well.
|
||||
if (ConfMan.hasKey("extrapath", Common::ConfigManager::kApplicationDomain)) {
|
||||
dir = Common::FSNode(ConfMan.get("extrapath", Common::ConfigManager::kApplicationDomain));
|
||||
SearchMan.addDirectory(dir.getPath(), dir);
|
||||
}
|
||||
|
||||
// On creation the engine should have set up all debug levels so we can use
|
||||
// the command line arugments here
|
||||
Common::StringTokenizer tokenizer(edebuglevels, " ,");
|
||||
while (!tokenizer.empty()) {
|
||||
Common::String token = tokenizer.nextToken();
|
||||
// if (!enableDebugChannel(token))
|
||||
// warning("Engine does not support debug level '%s'", token.c_str());
|
||||
}
|
||||
|
||||
// Inform backend that the engine is about to be run
|
||||
system.engineInit();
|
||||
|
||||
// Run the engine
|
||||
Common::Error result = engine->run();
|
||||
|
||||
// Inform backend that the engine finished
|
||||
system.engineDone();
|
||||
|
||||
// Free up memory
|
||||
delete engine;
|
||||
|
||||
// Reset the file/directory mappings
|
||||
SearchMan.clear();
|
||||
|
||||
// Return result (== 0 means no error)
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" int residual_main(int argc, const char * const argv[]) {
|
||||
Common::String specialDebug;
|
||||
Common::String command;
|
||||
|
||||
// Verify that the backend has been initialized (i.e. g_system has been set).
|
||||
assert(g_system);
|
||||
OSystem &system = *g_system;
|
||||
|
||||
// Register config manager defaults
|
||||
Base::registerDefaults();
|
||||
|
||||
// Parse the command line
|
||||
Common::StringMap settings;
|
||||
command = Base::parseCommandLine(settings, argc, argv);
|
||||
|
||||
// Load the config file (possibly overriden via command line):
|
||||
if (settings.contains("config")) {
|
||||
ConfMan.loadConfigFile(settings["config"]);
|
||||
settings.erase("config");
|
||||
} else {
|
||||
ConfMan.loadDefaultConfigFile();
|
||||
}
|
||||
|
||||
// Update the config file
|
||||
ConfMan.set("versioninfo", gResidualVersion, Common::ConfigManager::kApplicationDomain);
|
||||
|
||||
|
||||
// Load and setup the debuglevel and the debug flags. We do this at the
|
||||
// soonest possible moment to ensure debug output starts early on, if
|
||||
// requested.
|
||||
if (settings.contains("debuglevel")) {
|
||||
gDebugLevel = (enDebugLevels)strtol(settings["debuglevel"].c_str(), 0, 10);
|
||||
printf("Debuglevel (from command line): %d\n", gDebugLevel);
|
||||
settings.erase("debuglevel"); // This option should not be passed to ConfMan.
|
||||
} else if (ConfMan.hasKey("debuglevel"))
|
||||
gDebugLevel = (enDebugLevels)ConfMan.getInt("debuglevel");
|
||||
|
||||
if (settings.contains("debugflags")) {
|
||||
specialDebug = settings["debugflags"];
|
||||
settings.erase("debugflags");
|
||||
}
|
||||
|
||||
// Load the plugins.
|
||||
PluginManager::instance().loadPlugins();
|
||||
|
||||
// Process the remaining command line settings. Must be done after the
|
||||
// config file and the plugins have been loaded.
|
||||
if (!Base::processSettings(command, settings))
|
||||
return 0;
|
||||
|
||||
// Init the backend. Must take place after all config data (including
|
||||
// the command line params) was read.
|
||||
system.initBackend();
|
||||
|
||||
// Init the event manager. As the virtual keyboard is loaded here, it must
|
||||
// take place after the backend is initiated and the screen has been setup
|
||||
system.getEventManager()->init();
|
||||
|
||||
// Try to find a plugin which feels responsible for the specified game.
|
||||
const EnginePlugin *plugin = detectPlugin();
|
||||
if (plugin) {
|
||||
// Unload all plugins not needed for this game,
|
||||
// to save memory
|
||||
PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, plugin);
|
||||
|
||||
// Try to run the game
|
||||
Common::Error result = runGame(plugin, system, specialDebug);
|
||||
|
||||
// Did an error occur ?
|
||||
if (result != Common::kNoError) {
|
||||
// TODO: Show an informative error dialog if starting the selected game failed.
|
||||
}
|
||||
|
||||
// Quit unless an error occurred
|
||||
if (result == 0)
|
||||
goto exit;
|
||||
|
||||
// Discard any command line options. It's unlikely that the user
|
||||
// wanted to apply them to *all* games ever launched.
|
||||
ConfMan.getDomain(Common::ConfigManager::kTransientDomain)->clear();
|
||||
|
||||
// Clear the active config domain
|
||||
ConfMan.setActiveDomain("");
|
||||
|
||||
// PluginManager::instance().unloadPlugins();
|
||||
PluginManager::instance().loadPlugins();
|
||||
} else {
|
||||
// A dialog would be nicer, but we don't have any
|
||||
// screen to draw on yet.
|
||||
warning("Could not find any engine capable of running the selected game");
|
||||
}
|
||||
|
||||
exit:
|
||||
PluginManager::instance().unloadPlugins();
|
||||
PluginManager::destroy();
|
||||
Common::ConfigManager::destroy();
|
||||
Common::SearchManager::destroy();
|
||||
|
||||
return 0;
|
||||
}
|
33
base/main.h
Normal file
33
base/main.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BASE_MAIN_H
|
||||
#define BASE_MAIN_H
|
||||
|
||||
#include "common/sys.h"
|
||||
|
||||
extern "C" int residual_main(int argc, const char *const argv[]);
|
||||
|
||||
#endif
|
10
base/module.mk
Normal file
10
base/module.mk
Normal file
@ -0,0 +1,10 @@
|
||||
MODULE := base
|
||||
|
||||
MODULE_OBJS := \
|
||||
main.o \
|
||||
commandLine.o \
|
||||
plugins.o \
|
||||
version.o
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
299
base/plugins.cpp
Normal file
299
base/plugins.cpp
Normal file
@ -0,0 +1,299 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "common/func.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
#include "common/config-manager.h"
|
||||
#include "common/fs.h"
|
||||
#endif
|
||||
|
||||
// Plugin versioning
|
||||
|
||||
int pluginTypeVersions[PLUGIN_TYPE_MAX] = {
|
||||
PLUGIN_TYPE_ENGINE_VERSION,
|
||||
};
|
||||
|
||||
|
||||
// Abstract plugins
|
||||
|
||||
PluginType Plugin::getType() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
const char *Plugin::getName() const {
|
||||
return _pluginObject->getName();
|
||||
}
|
||||
|
||||
class StaticPlugin : public Plugin {
|
||||
public:
|
||||
StaticPlugin(PluginObject *pluginobject, PluginType type) {
|
||||
assert(pluginobject);
|
||||
assert(type < PLUGIN_TYPE_MAX);
|
||||
_pluginObject = pluginobject;
|
||||
_type = type;
|
||||
}
|
||||
|
||||
~StaticPlugin() {
|
||||
delete _pluginObject;
|
||||
}
|
||||
|
||||
virtual bool loadPlugin() { return true; }
|
||||
virtual void unloadPlugin() {}
|
||||
};
|
||||
|
||||
class StaticPluginProvider : public PluginProvider {
|
||||
public:
|
||||
StaticPluginProvider() {
|
||||
}
|
||||
|
||||
~StaticPluginProvider() {
|
||||
}
|
||||
|
||||
virtual PluginList getPlugins() {
|
||||
PluginList pl;
|
||||
|
||||
#define LINK_PLUGIN(ID) \
|
||||
extern PluginType g_##ID##_type; \
|
||||
extern PluginObject *g_##ID##_getObject(); \
|
||||
pl.push_back(new StaticPlugin(g_##ID##_getObject(), g_##ID##_type));
|
||||
|
||||
// "Loader" for the static plugins.
|
||||
// Iterate over all registered (static) plugins and load them.
|
||||
|
||||
// Engine plugins
|
||||
#if PLUGIN_ENABLED_STATIC(GRIM)
|
||||
LINK_PLUGIN(GRIM)
|
||||
#endif
|
||||
|
||||
return pl;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
|
||||
PluginList FilePluginProvider::getPlugins() {
|
||||
PluginList pl;
|
||||
|
||||
// Prepare the list of directories to search
|
||||
Common::FSList pluginDirs;
|
||||
|
||||
// Add the default directories
|
||||
pluginDirs.push_back(Common::FSNode("."));
|
||||
pluginDirs.push_back(Common::FSNode("plugins"));
|
||||
|
||||
// Add the provider's custom directories
|
||||
addCustomDirectories(pluginDirs);
|
||||
|
||||
// Add the user specified directory
|
||||
Common::String pluginsPath(ConfMan.get("pluginspath"));
|
||||
if (!pluginsPath.empty())
|
||||
pluginDirs.push_back(Common::FSNode(pluginsPath));
|
||||
|
||||
Common::FSList::const_iterator dir;
|
||||
for (dir = pluginDirs.begin(); dir != pluginDirs.end(); dir++) {
|
||||
// Load all plugins.
|
||||
// Scan for all plugins in this directory
|
||||
Common::FSList files;
|
||||
if (!dir->getChildren(files, Common::FSNode::kListFilesOnly)) {
|
||||
debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str());
|
||||
continue;
|
||||
} else {
|
||||
debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str());
|
||||
}
|
||||
|
||||
for (Common::FSList::const_iterator i = files.begin(); i != files.end(); ++i) {
|
||||
if (isPluginFilename(*i)) {
|
||||
pl.push_back(createPlugin(*i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pl;
|
||||
}
|
||||
|
||||
bool FilePluginProvider::isPluginFilename(const Common::FSNode &node) const {
|
||||
Common::String filename = node.getName();
|
||||
|
||||
#ifdef PLUGIN_PREFIX
|
||||
// Check the plugin prefix
|
||||
if (!filename.hasPrefix(PLUGIN_PREFIX))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#ifdef PLUGIN_SUFFIX
|
||||
// Check the plugin suffix
|
||||
if (!filename.hasSuffix(PLUGIN_SUFFIX))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePluginProvider::addCustomDirectories(Common::FSList &dirs) const {
|
||||
#ifdef PLUGIN_DIRECTORY
|
||||
dirs.push_back(Common::FSNode(PLUGIN_DIRECTORY));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DYNAMIC_MODULES
|
||||
|
||||
#pragma mark -
|
||||
|
||||
DECLARE_SINGLETON(PluginManager);
|
||||
|
||||
PluginManager::PluginManager() {
|
||||
// Always add the static plugin provider.
|
||||
addPluginProvider(new StaticPluginProvider());
|
||||
}
|
||||
|
||||
PluginManager::~PluginManager() {
|
||||
// Explicitly unload all loaded plugins
|
||||
unloadPlugins();
|
||||
|
||||
// Delete the plugin providers
|
||||
for (ProviderList::iterator pp = _providers.begin();
|
||||
pp != _providers.end();
|
||||
++pp) {
|
||||
delete *pp;
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::addPluginProvider(PluginProvider *pp) {
|
||||
_providers.push_back(pp);
|
||||
}
|
||||
|
||||
void PluginManager::loadPlugins() {
|
||||
for (ProviderList::iterator pp = _providers.begin();
|
||||
pp != _providers.end();
|
||||
++pp) {
|
||||
PluginList pl((*pp)->getPlugins());
|
||||
Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PluginManager::unloadPlugins() {
|
||||
for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
|
||||
unloadPluginsExcept((PluginType)i, NULL);
|
||||
}
|
||||
|
||||
void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin) {
|
||||
Plugin *found = NULL;
|
||||
for (PluginList::iterator p = _plugins[type].begin(); p != _plugins[type].end(); ++p) {
|
||||
if (*p == plugin) {
|
||||
found = *p;
|
||||
} else {
|
||||
(*p)->unloadPlugin();
|
||||
delete *p;
|
||||
}
|
||||
}
|
||||
_plugins[type].clear();
|
||||
if (found != NULL) {
|
||||
_plugins[type].push_back(found);
|
||||
}
|
||||
}
|
||||
|
||||
bool PluginManager::tryLoadPlugin(Plugin *plugin) {
|
||||
assert(plugin);
|
||||
// Try to load the plugin
|
||||
if (plugin->loadPlugin()) {
|
||||
// The plugin is valid, see if it provides the same module as an
|
||||
// already loaded one and should replace it.
|
||||
bool found = false;
|
||||
|
||||
PluginList::iterator pl = _plugins[plugin->getType()].begin();
|
||||
while (!found && pl != _plugins[plugin->getType()].end()) {
|
||||
if (!strcmp(plugin->getName(), (*pl)->getName())) {
|
||||
// Found a duplicated module. Replace the old one.
|
||||
found = true;
|
||||
delete *pl;
|
||||
*pl = plugin;
|
||||
debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
|
||||
}
|
||||
pl++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// If it provides a new module, just add it to the list of known plugins.
|
||||
_plugins[plugin->getType()].push_back(plugin);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Failed to load the plugin
|
||||
delete plugin;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Engine plugins
|
||||
|
||||
#include "engines/metaengine.h"
|
||||
|
||||
DECLARE_SINGLETON(EngineManager);
|
||||
|
||||
GameDescriptor EngineManager::findGame(const Common::String &gameName, const EnginePlugin **plugin) const {
|
||||
// Find the GameDescriptor for this target
|
||||
const EnginePlugin::List &plugins = getPlugins();
|
||||
GameDescriptor result;
|
||||
|
||||
if (plugin)
|
||||
*plugin = 0;
|
||||
|
||||
EnginePlugin::List::const_iterator iter = plugins.begin();
|
||||
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
|
||||
result = (**iter)->findGame(gameName.c_str());
|
||||
if (!result.gameid().empty()) {
|
||||
if (plugin)
|
||||
*plugin = *iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GameList EngineManager::detectGames(const Common::FSList &fslist) const {
|
||||
GameList candidates;
|
||||
|
||||
const EnginePlugin::List &plugins = getPlugins();
|
||||
|
||||
// Iterate over all known games and for each check if it might be
|
||||
// the game in the presented directory.
|
||||
EnginePlugin::List::const_iterator iter;
|
||||
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
|
||||
candidates.push_back((**iter)->detectGames(fslist));
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
const EnginePlugin::List &EngineManager::getPlugins() const {
|
||||
return (const EnginePlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
|
||||
}
|
292
base/plugins.h
Normal file
292
base/plugins.h
Normal file
@ -0,0 +1,292 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BASE_PLUGINS_H
|
||||
#define BASE_PLUGINS_H
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Common {
|
||||
class FSList;
|
||||
class FSNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @page pagePlugins An overview of the ScummVM plugin system
|
||||
* This is a brief overview of how plugins (dynamically loadable code modules)
|
||||
* work in ScummVM. We will explain how to write plugins, how they work internally,
|
||||
* and sketch how porters can add support for them in their ports.
|
||||
*
|
||||
* \section secPluginImpl Implementing a plugin
|
||||
* TODO
|
||||
*
|
||||
* \section secPluginUse Using plugins
|
||||
* TODO
|
||||
*
|
||||
* \section secPluginInternals How plugins work internally
|
||||
* TODO
|
||||
*
|
||||
* \section secPluginBackend How to add support for dynamic plugins to a port
|
||||
* TODO
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Plugin versioning
|
||||
|
||||
/** Global Plugin API version */
|
||||
#define PLUGIN_VERSION 1
|
||||
|
||||
enum PluginType {
|
||||
PLUGIN_TYPE_ENGINE = 0,
|
||||
|
||||
PLUGIN_TYPE_MAX
|
||||
};
|
||||
|
||||
// TODO: Make the engine API version depend on ScummVM's version
|
||||
// because of the backlinking (posibly from the SVN revision)
|
||||
#define PLUGIN_TYPE_ENGINE_VERSION 1
|
||||
|
||||
extern int pluginTypeVersions[PLUGIN_TYPE_MAX];
|
||||
|
||||
|
||||
// Plugin linking
|
||||
|
||||
#define STATIC_PLUGIN 1
|
||||
#define DYNAMIC_PLUGIN 2
|
||||
|
||||
#define PLUGIN_ENABLED_STATIC(ID) \
|
||||
(ENABLE_##ID && !PLUGIN_ENABLED_DYNAMIC(ID))
|
||||
|
||||
#define PLUGIN_ENABLED_DYNAMIC(ID) \
|
||||
(ENABLE_##ID && (ENABLE_##ID == DYNAMIC_PLUGIN) && DYNAMIC_MODULES)
|
||||
|
||||
/**
|
||||
* REGISTER_PLUGIN_STATIC is a convenience macro which is used to declare
|
||||
* the plugin interface for static plugins. Code (such as game engines)
|
||||
* which needs to implement a static plugin can simply invoke this macro
|
||||
* with a plugin ID, plugin type and PluginObject subclass, and the correct
|
||||
* wrapper code will be inserted.
|
||||
*
|
||||
* @see REGISTER_PLUGIN_DYNAMIC
|
||||
*/
|
||||
#define REGISTER_PLUGIN_STATIC(ID,TYPE,PLUGINCLASS) \
|
||||
PluginType g_##ID##_type = TYPE; \
|
||||
PluginObject *g_##ID##_getObject() { \
|
||||
return new PLUGINCLASS(); \
|
||||
} \
|
||||
void dummyFuncToAllowTrailingSemicolon()
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
|
||||
/**
|
||||
* REGISTER_PLUGIN_DYNAMIC is a convenience macro which is used to declare
|
||||
* the plugin interface for dynamically loadable plugins. Code (such as game engines)
|
||||
* which needs to implement a dynamic plugin can simply invoke this macro
|
||||
* with a plugin ID, plugin type and PluginObject subclass, and the correct
|
||||
* wrapper code will be inserted.
|
||||
*
|
||||
* @see REGISTER_PLUGIN_STATIC
|
||||
*/
|
||||
#define REGISTER_PLUGIN_DYNAMIC(ID,TYPE,PLUGINCLASS) \
|
||||
extern "C" { \
|
||||
PLUGIN_EXPORT int32 PLUGIN_getVersion() { return PLUGIN_VERSION; } \
|
||||
PLUGIN_EXPORT int32 PLUGIN_getType() { return TYPE; } \
|
||||
PLUGIN_EXPORT int32 PLUGIN_getTypeVersion() { return TYPE##_VERSION; } \
|
||||
PLUGIN_EXPORT PluginObject *PLUGIN_getObject() { \
|
||||
return new PLUGINCLASS(); \
|
||||
} \
|
||||
} \
|
||||
void dummyFuncToAllowTrailingSemicolon()
|
||||
|
||||
#endif // DYNAMIC_MODULES
|
||||
|
||||
|
||||
// Abstract plugins
|
||||
|
||||
/**
|
||||
* Abstract base class for the plugin objects which handle plugins
|
||||
* instantiation. Subclasses for this may be used for engine plugins
|
||||
* and other types of plugins.
|
||||
*
|
||||
* FIXME: This class needs better documentation, esp. how it differs from class Plugin
|
||||
*/
|
||||
class PluginObject {
|
||||
public:
|
||||
virtual ~PluginObject() {}
|
||||
|
||||
/** Returns the name of the plugin. */
|
||||
virtual const char *getName() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base class for the plugin system.
|
||||
* Subclasses for this can be used to wrap both static and dynamic
|
||||
* plugins.
|
||||
*
|
||||
* FIXME: This class needs better documentation, esp. how it differs from class PluginObject
|
||||
*/
|
||||
class Plugin {
|
||||
protected:
|
||||
PluginObject *_pluginObject;
|
||||
PluginType _type;
|
||||
|
||||
public:
|
||||
Plugin() : _pluginObject(0) {}
|
||||
virtual ~Plugin() {
|
||||
//if (isLoaded())
|
||||
//unloadPlugin();
|
||||
}
|
||||
|
||||
// virtual bool isLoaded() const = 0; // TODO
|
||||
virtual bool loadPlugin() = 0; // TODO: Rename to load() ?
|
||||
virtual void unloadPlugin() = 0; // TODO: Rename to unload() ?
|
||||
|
||||
PluginType getType() const;
|
||||
const char *getName() const;
|
||||
};
|
||||
|
||||
/** List of Plugin instances. */
|
||||
typedef Common::Array<Plugin *> PluginList;
|
||||
|
||||
/**
|
||||
* Convenience template to make it easier defining normal Plugin
|
||||
* subclasses. Namely, the PluginSubclass will manage PluginObjects
|
||||
* of a type specified via the PO_t template parameter.
|
||||
*/
|
||||
template<class PO_t>
|
||||
class PluginSubclass : public Plugin {
|
||||
public:
|
||||
PO_t *operator->() const {
|
||||
return (PO_t *)_pluginObject;
|
||||
}
|
||||
|
||||
typedef Common::Array<PluginSubclass *> List;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base class for Plugin factories. Subclasses of this
|
||||
* are responsible for creating plugin objects, e.g. by loading
|
||||
* loadable modules from storage media; by creating "fake" plugins
|
||||
* from static code; or whatever other means.
|
||||
*/
|
||||
class PluginProvider {
|
||||
public:
|
||||
virtual ~PluginProvider() {}
|
||||
|
||||
/**
|
||||
* Return a list of Plugin objects. The caller is responsible for actually
|
||||
* loading/unloading them (by invoking the appropriate Plugin methods).
|
||||
* Furthermore, the caller is responsible for deleting these objects
|
||||
* eventually.
|
||||
*
|
||||
* @return a list of Plugin instances
|
||||
*/
|
||||
virtual PluginList getPlugins() = 0;
|
||||
};
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
|
||||
/**
|
||||
* Abstract base class for Plugin factories which load binary code from files.
|
||||
* Subclasses only have to implement the createPlugin() method, and optionally
|
||||
* can overload the other protected methods to achieve custom behavior.
|
||||
*/
|
||||
class FilePluginProvider : public PluginProvider {
|
||||
public:
|
||||
/**
|
||||
* Return a list of Plugin objects loaded via createPlugin from disk.
|
||||
* For this, a list of directories is searched for plugin objects:
|
||||
* The current dir and its "plugins" subdirectory (if present), a list
|
||||
* of custom search dirs (see addCustomDirectories) and finally the
|
||||
* directory specified via the "pluginspath" config variable (if any).
|
||||
*
|
||||
* @return a list of Plugin instances
|
||||
*/
|
||||
virtual PluginList getPlugins();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Create a Plugin instance from a loadable code module with the specified name.
|
||||
* Subclasses of FilePluginProvider have to at least overload this method.
|
||||
* If the file is not found, or does not contain loadable code, 0 is returned instead.
|
||||
*
|
||||
* @param node the FSNode of the loadable code module
|
||||
* @return a pointer to a Plugin instance, or 0 if an error occurred.
|
||||
*/
|
||||
virtual Plugin *createPlugin(const Common::FSNode &node) const = 0;
|
||||
|
||||
/**
|
||||
* Check if the supplied file corresponds to a loadable plugin file in
|
||||
* the current platform. Usually, this will just check the file name.
|
||||
*
|
||||
* @param node the FSNode of the file to check
|
||||
* @return true if the filename corresponds to a plugin, false otherwise
|
||||
*/
|
||||
virtual bool isPluginFilename(const Common::FSNode &node) const;
|
||||
|
||||
/**
|
||||
* Optionally add to the list of directories to be searched for
|
||||
* plugins by getPlugins().
|
||||
*
|
||||
* @param dirs the reference to the list of directories to be used when
|
||||
* searching for plugins.
|
||||
*/
|
||||
virtual void addCustomDirectories(Common::FSList &dirs) const;
|
||||
};
|
||||
|
||||
#endif // DYNAMIC_MODULES
|
||||
|
||||
/**
|
||||
* Singleton class which manages all plugins, including loading them,
|
||||
* managing all Plugin class instances, and unloading them.
|
||||
*/
|
||||
class PluginManager : public Common::Singleton<PluginManager> {
|
||||
typedef Common::Array<PluginProvider *> ProviderList;
|
||||
private:
|
||||
PluginList _plugins[PLUGIN_TYPE_MAX];
|
||||
ProviderList _providers;
|
||||
|
||||
bool tryLoadPlugin(Plugin *plugin);
|
||||
|
||||
friend class Common::Singleton<SingletonBaseType>;
|
||||
PluginManager();
|
||||
|
||||
public:
|
||||
~PluginManager();
|
||||
|
||||
void addPluginProvider(PluginProvider *pp);
|
||||
|
||||
void loadPlugins();
|
||||
void unloadPlugins();
|
||||
void unloadPluginsExcept(PluginType type, const Plugin *plugin);
|
||||
|
||||
const PluginList &getPlugins(PluginType t) { return _plugins[t]; }
|
||||
};
|
||||
|
||||
#endif
|
49
base/version.cpp
Normal file
49
base/version.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/internal_version.h"
|
||||
#include "base/version.h"
|
||||
|
||||
const char *gResidualVersion = RESIDUAL_VERSION;
|
||||
const char *gResidualBuildDate = __DATE__ " " __TIME__;
|
||||
const char *gResidualVersionDate = RESIDUAL_VERSION " (" __DATE__ " " __TIME__ ")";
|
||||
const char *gResidualFullVersion = "Residual " RESIDUAL_VERSION " (" __DATE__ " " __TIME__ ")";
|
||||
const char *gResidualFeatures = ""
|
||||
#ifdef USE_TREMOR
|
||||
"Tremor "
|
||||
#else
|
||||
#ifdef USE_VORBIS
|
||||
"Vorbis "
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_FLAC
|
||||
"FLAC "
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAD
|
||||
"MP3 "
|
||||
#endif
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
"zLib "
|
||||
#endif
|
||||
;
|
||||
|
29
base/version.h
Normal file
29
base/version.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef BASE_VERSION_H
|
||||
#define BASE_VERSION_H
|
||||
|
||||
extern const char *gResidualVersion; // e.g. "0.0.6"
|
||||
extern const char *gResidualBuildDate; // e.g. "2008-06-15"
|
||||
extern const char *gResidualVersionDate; // e.g. "0.0.6 (2008-06-15)"
|
||||
extern const char *gResidualFullVersion; // e.g. "Residual 0.0.6 (2008-06-15)"
|
||||
extern const char *gResidualFeatures; // e.g. "ALSA MPEG2 zLib"
|
||||
|
||||
#endif
|
431
common/md5.cpp
Normal file
431
common/md5.cpp
Normal file
@ -0,0 +1,431 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* RFC 1321 compliant MD5 implementation,
|
||||
* by Christophe Devine <devine(at)cr0.net>
|
||||
* this program is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/md5.h"
|
||||
#include "common/util.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
typedef struct {
|
||||
uint32 total[2];
|
||||
uint32 state[4];
|
||||
uint8 buffer[64];
|
||||
} md5_context;
|
||||
|
||||
void md5_starts(md5_context *ctx);
|
||||
void md5_update(md5_context *ctx, const uint8 *input, uint32 length);
|
||||
void md5_finish(md5_context *ctx, uint8 digest[16]);
|
||||
|
||||
|
||||
#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i)
|
||||
#define PUT_UINT32(n, b, i) WRITE_LE_UINT32(b + i, n)
|
||||
|
||||
void md5_starts(md5_context *ctx) {
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xEFCDAB89;
|
||||
ctx->state[2] = 0x98BADCFE;
|
||||
ctx->state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
static void md5_process(md5_context *ctx, const uint8 data[64]) {
|
||||
uint32 X[16], A, B, C, D;
|
||||
|
||||
GET_UINT32(X[0], data, 0);
|
||||
GET_UINT32(X[1], data, 4);
|
||||
GET_UINT32(X[2], data, 8);
|
||||
GET_UINT32(X[3], data, 12);
|
||||
GET_UINT32(X[4], data, 16);
|
||||
GET_UINT32(X[5], data, 20);
|
||||
GET_UINT32(X[6], data, 24);
|
||||
GET_UINT32(X[7], data, 28);
|
||||
GET_UINT32(X[8], data, 32);
|
||||
GET_UINT32(X[9], data, 36);
|
||||
GET_UINT32(X[10], data, 40);
|
||||
GET_UINT32(X[11], data, 44);
|
||||
GET_UINT32(X[12], data, 48);
|
||||
GET_UINT32(X[13], data, 52);
|
||||
GET_UINT32(X[14], data, 56);
|
||||
GET_UINT32(X[15], data, 60);
|
||||
|
||||
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
||||
|
||||
#define P(a, b, c, d, k, s, t) \
|
||||
{ \
|
||||
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
|
||||
}
|
||||
|
||||
A = ctx->state[0];
|
||||
B = ctx->state[1];
|
||||
C = ctx->state[2];
|
||||
D = ctx->state[3];
|
||||
|
||||
#define F(x, y, z) (z ^ (x & (y ^ z)))
|
||||
|
||||
P(A, B, C, D, 0, 7, 0xD76AA478);
|
||||
P(D, A, B, C, 1, 12, 0xE8C7B756);
|
||||
P(C, D, A, B, 2, 17, 0x242070DB);
|
||||
P(B, C, D, A, 3, 22, 0xC1BDCEEE);
|
||||
P(A, B, C, D, 4, 7, 0xF57C0FAF);
|
||||
P(D, A, B, C, 5, 12, 0x4787C62A);
|
||||
P(C, D, A, B, 6, 17, 0xA8304613);
|
||||
P(B, C, D, A, 7, 22, 0xFD469501);
|
||||
P(A, B, C, D, 8, 7, 0x698098D8);
|
||||
P(D, A, B, C, 9, 12, 0x8B44F7AF);
|
||||
P(C, D, A, B, 10, 17, 0xFFFF5BB1);
|
||||
P(B, C, D, A, 11, 22, 0x895CD7BE);
|
||||
P(A, B, C, D, 12, 7, 0x6B901122);
|
||||
P(D, A, B, C, 13, 12, 0xFD987193);
|
||||
P(C, D, A, B, 14, 17, 0xA679438E);
|
||||
P(B, C, D, A, 15, 22, 0x49B40821);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x, y, z) (y ^ (z & (x ^ y)))
|
||||
|
||||
P(A, B, C, D, 1, 5, 0xF61E2562);
|
||||
P(D, A, B, C, 6, 9, 0xC040B340);
|
||||
P(C, D, A, B, 11, 14, 0x265E5A51);
|
||||
P(B, C, D, A, 0, 20, 0xE9B6C7AA);
|
||||
P(A, B, C, D, 5, 5, 0xD62F105D);
|
||||
P(D, A, B, C, 10, 9, 0x02441453);
|
||||
P(C, D, A, B, 15, 14, 0xD8A1E681);
|
||||
P(B, C, D, A, 4, 20, 0xE7D3FBC8);
|
||||
P(A, B, C, D, 9, 5, 0x21E1CDE6);
|
||||
P(D, A, B, C, 14, 9, 0xC33707D6);
|
||||
P(C, D, A, B, 3, 14, 0xF4D50D87);
|
||||
P(B, C, D, A, 8, 20, 0x455A14ED);
|
||||
P(A, B, C, D, 13, 5, 0xA9E3E905);
|
||||
P(D, A, B, C, 2, 9, 0xFCEFA3F8);
|
||||
P(C, D, A, B, 7, 14, 0x676F02D9);
|
||||
P(B, C, D, A, 12, 20, 0x8D2A4C8A);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x, y, z) (x ^ y ^ z)
|
||||
|
||||
P(A, B, C, D, 5, 4, 0xFFFA3942);
|
||||
P(D, A, B, C, 8, 11, 0x8771F681);
|
||||
P(C, D, A, B, 11, 16, 0x6D9D6122);
|
||||
P(B, C, D, A, 14, 23, 0xFDE5380C);
|
||||
P(A, B, C, D, 1, 4, 0xA4BEEA44);
|
||||
P(D, A, B, C, 4, 11, 0x4BDECFA9);
|
||||
P(C, D, A, B, 7, 16, 0xF6BB4B60);
|
||||
P(B, C, D, A, 10, 23, 0xBEBFBC70);
|
||||
P(A, B, C, D, 13, 4, 0x289B7EC6);
|
||||
P(D, A, B, C, 0, 11, 0xEAA127FA);
|
||||
P(C, D, A, B, 3, 16, 0xD4EF3085);
|
||||
P(B, C, D, A, 6, 23, 0x04881D05);
|
||||
P(A, B, C, D, 9, 4, 0xD9D4D039);
|
||||
P(D, A, B, C, 12, 11, 0xE6DB99E5);
|
||||
P(C, D, A, B, 15, 16, 0x1FA27CF8);
|
||||
P(B, C, D, A, 2, 23, 0xC4AC5665);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
P(A, B, C, D, 0, 6, 0xF4292244);
|
||||
P(D, A, B, C, 7, 10, 0x432AFF97);
|
||||
P(C, D, A, B, 14, 15, 0xAB9423A7);
|
||||
P(B, C, D, A, 5, 21, 0xFC93A039);
|
||||
P(A, B, C, D, 12, 6, 0x655B59C3);
|
||||
P(D, A, B, C, 3, 10, 0x8F0CCC92);
|
||||
P(C, D, A, B, 10, 15, 0xFFEFF47D);
|
||||
P(B, C, D, A, 1, 21, 0x85845DD1);
|
||||
P(A, B, C, D, 8, 6, 0x6FA87E4F);
|
||||
P(D, A, B, C, 15, 10, 0xFE2CE6E0);
|
||||
P(C, D, A, B, 6, 15, 0xA3014314);
|
||||
P(B, C, D, A, 13, 21, 0x4E0811A1);
|
||||
P(A, B, C, D, 4, 6, 0xF7537E82);
|
||||
P(D, A, B, C, 11, 10, 0xBD3AF235);
|
||||
P(C, D, A, B, 2, 15, 0x2AD7D2BB);
|
||||
P(B, C, D, A, 9, 21, 0xEB86D391);
|
||||
|
||||
#undef F
|
||||
|
||||
ctx->state[0] += A;
|
||||
ctx->state[1] += B;
|
||||
ctx->state[2] += C;
|
||||
ctx->state[3] += D;
|
||||
}
|
||||
|
||||
void md5_update(md5_context *ctx, const uint8 *input, uint32 length) {
|
||||
uint32 left, fill;
|
||||
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
left = ctx->total[0] & 0x3F;
|
||||
fill = 64 - left;
|
||||
|
||||
ctx->total[0] += length;
|
||||
ctx->total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if (ctx->total[0] < length)
|
||||
ctx->total[1]++;
|
||||
|
||||
if (left && length >= fill) {
|
||||
memcpy((void *)(ctx->buffer + left), (const void *)input, fill);
|
||||
md5_process(ctx, ctx->buffer);
|
||||
length -= fill;
|
||||
input += fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while (length >= 64) {
|
||||
md5_process(ctx, input);
|
||||
length -= 64;
|
||||
input += 64;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
memcpy((void *)(ctx->buffer + left), (const void *)input, length);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8 md5_padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
void md5_finish(md5_context *ctx, uint8 digest[16]) {
|
||||
uint32 last, padn;
|
||||
uint32 high, low;
|
||||
uint8 msglen[8];
|
||||
|
||||
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
|
||||
low = (ctx->total[0] << 3);
|
||||
|
||||
PUT_UINT32(low, msglen, 0);
|
||||
PUT_UINT32(high, msglen, 4);
|
||||
|
||||
last = ctx->total[0] & 0x3F;
|
||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
||||
|
||||
md5_update(ctx, md5_padding, padn);
|
||||
md5_update(ctx, msglen, 8);
|
||||
|
||||
PUT_UINT32(ctx->state[0], digest, 0);
|
||||
PUT_UINT32(ctx->state[1], digest, 4);
|
||||
PUT_UINT32(ctx->state[2], digest, 8);
|
||||
PUT_UINT32(ctx->state[3], digest, 12);
|
||||
}
|
||||
|
||||
bool md5_file(const FSNode &file, uint8 digest[16], uint32 length) {
|
||||
if (!file.exists()) {
|
||||
warning("md5_file: using an inexistent FSNode");
|
||||
return false;
|
||||
} else if (!file.isReadable()) {
|
||||
warning("md5_file: using an unreadable FSNode");
|
||||
return false;
|
||||
} else if (file.isDirectory()) {
|
||||
warning("md5_file: using a directory FSNode");
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadStream *stream = file.createReadStream();
|
||||
if (!stream) {
|
||||
warning("md5_file: failed to open '%s'", file.getPath().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = md5_file(*stream, digest, length);
|
||||
delete stream;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool md5_file(const char *name, uint8 digest[16], uint32 length) {
|
||||
File f;
|
||||
|
||||
f.open(name);
|
||||
if (!f.isOpen()) {
|
||||
warning("md5_file couldn't open '%s'", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return md5_file(f, digest, length);
|
||||
}
|
||||
|
||||
|
||||
bool md5_file(ReadStream &stream, uint8 digest[16], uint32 length) {
|
||||
|
||||
#ifdef DISABLE_MD5
|
||||
memset(digest, 0, 16);
|
||||
#else
|
||||
md5_context ctx;
|
||||
int i;
|
||||
unsigned char buf[1000];
|
||||
bool restricted = (length != 0);
|
||||
uint32 readlen;
|
||||
|
||||
if (!restricted || sizeof(buf) <= length)
|
||||
readlen = sizeof(buf);
|
||||
else
|
||||
readlen = length;
|
||||
|
||||
md5_starts(&ctx);
|
||||
|
||||
while ((i = stream.read(buf, readlen)) > 0) {
|
||||
md5_update(&ctx, buf, i);
|
||||
|
||||
length -= i;
|
||||
if (restricted && length == 0)
|
||||
break;
|
||||
|
||||
if (restricted && sizeof(buf) > length)
|
||||
readlen = length;
|
||||
}
|
||||
|
||||
md5_finish(&ctx, digest);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool md5_file_string(const FSNode &file, char *md5str, uint32 length) {
|
||||
uint8 digest[16];
|
||||
if (!md5_file(file, digest, length))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
snprintf(md5str + i*2, 3, "%02x", (int)digest[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool md5_file_string(const char *name, char *md5str, uint32 length) {
|
||||
uint8 digest[16];
|
||||
if (!md5_file(name, digest, length))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
snprintf(md5str + i*2, 3, "%02x", (int)digest[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool md5_file_string(ReadStream &stream, char *md5str, uint32 length) {
|
||||
uint8 digest[16];
|
||||
if (!md5_file(stream, digest, length))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
snprintf(md5str + i*2, 3, "%02x", (int)digest[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* those are the standard RFC 1321 test vectors
|
||||
*/
|
||||
|
||||
static const char *msg[] = {
|
||||
"",
|
||||
"a",
|
||||
"abc",
|
||||
"message digest",
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
||||
"12345678901234567890123456789012345678901234567890123456789012" \
|
||||
"345678901234567890"
|
||||
};
|
||||
|
||||
static const char *val[] = {
|
||||
"d41d8cd98f00b204e9800998ecf8427e",
|
||||
"0cc175b9c0f1b6a831c399e269772661",
|
||||
"900150983cd24fb0d6963f7d28e17f72",
|
||||
"f96b697d7cb7938d525a2f31aaf161d0",
|
||||
"c3fcd3d76192e4007dfb496cca67e13b",
|
||||
"d174ab98d277d9f5a5611c2c9f419d9f",
|
||||
"57edf4a22be3c955ac49da2e2107b67a"
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, j;
|
||||
char output[33];
|
||||
md5_context ctx;
|
||||
unsigned char md5sum[16];
|
||||
|
||||
if (argc < 2) {
|
||||
printf("\n MD5 Validation Tests:\n\n");
|
||||
|
||||
for (i = 0; i < 7; i++) {
|
||||
printf(" Test %d ", i + 1);
|
||||
|
||||
md5_starts(&ctx);
|
||||
md5_update(&ctx, (const uint8 *)msg[i], strlen(msg[i]));
|
||||
md5_finish(&ctx, md5sum);
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
sprintf(output + j * 2, "%02x", md5sum[j]);
|
||||
}
|
||||
|
||||
if (memcmp(output, val[i], 32)) {
|
||||
printf("failed!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("passed.\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
} else {
|
||||
for (i = 1; i < argc; i++) {
|
||||
md5_file(argv[i], md5sum);
|
||||
for (j = 0; j < 16; j++) {
|
||||
printf("%02x", md5sum[j]);
|
||||
}
|
||||
|
||||
printf(" %s\n", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
51
common/md5.h
Normal file
51
common/md5.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_MD5_H
|
||||
#define COMMON_MD5_H
|
||||
|
||||
#include "common/sys.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class FSNode;
|
||||
class ReadStream;
|
||||
|
||||
bool md5_file(const char *name, uint8 digest[16], uint32 length = 0);
|
||||
bool md5_file(const FSNode &file, uint8 digest[16], uint32 length = 0);
|
||||
bool md5_file(ReadStream &stream, uint8 digest[16], uint32 length = 0);
|
||||
|
||||
// The following two methods work similar to the above two, but
|
||||
// instead of computing the binary MD5 digest, they produce
|
||||
// a human readable lowercase hexstring representing the digest.
|
||||
// The md5str parameter must point to a buffer of 32+1 chars.
|
||||
bool md5_file_string(const char *name, char *md5str, uint32 length = 0);
|
||||
bool md5_file_string(const FSNode &file, char *md5str, uint32 length = 0);
|
||||
bool md5_file_string(ReadStream &stream, char *md5str, uint32 length = 0);
|
||||
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
@ -10,6 +10,7 @@ MODULE_OBJS := \
|
||||
hashmap.o \
|
||||
libz.o \
|
||||
memorypool.o \
|
||||
md5.o \
|
||||
mutex.o \
|
||||
str.o \
|
||||
stream.o \
|
||||
|
538
engines/advancedDetector.cpp
Normal file
538
engines/advancedDetector.cpp
Normal file
@ -0,0 +1,538 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/util.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
/**
|
||||
* A list of pointers to ADGameDescription structs (or subclasses thereof).
|
||||
*/
|
||||
typedef Common::Array<const ADGameDescription*> ADGameDescList;
|
||||
|
||||
|
||||
/**
|
||||
* Detect games in specified directory.
|
||||
* Parameters language and platform are used to pass on values
|
||||
* specified by the user. I.e. this is used to restrict search scope.
|
||||
*
|
||||
* @param fslist FSList to scan or NULL for scanning all specified
|
||||
* default directories.
|
||||
* @param params a ADParams struct containing various parameters
|
||||
* @param language restrict results to specified language only
|
||||
* @param platform restrict results to specified platform only
|
||||
* @return list of ADGameDescription (or subclass) pointers corresponding to matched games
|
||||
*/
|
||||
static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String extra);
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of targets supported by the engine.
|
||||
* Distinguishes engines with single ID
|
||||
*/
|
||||
static GameList gameIDList(const ADParams ¶ms) {
|
||||
if (params.singleid != NULL) {
|
||||
GameList gl;
|
||||
|
||||
const PlainGameDescriptor *g = params.list;
|
||||
while (g->gameid) {
|
||||
if (0 == strcasecmp(params.singleid, g->gameid)) {
|
||||
gl.push_back(GameDescriptor(g->gameid, g->description));
|
||||
|
||||
return gl;
|
||||
}
|
||||
g++;
|
||||
}
|
||||
error("Engine %s doesn't have its singleid specified in ids list", params.singleid);
|
||||
}
|
||||
|
||||
return GameList(params.list);
|
||||
}
|
||||
|
||||
static void upgradeTargetIfNecessary(const ADParams ¶ms) {
|
||||
if (params.obsoleteList == 0)
|
||||
return;
|
||||
|
||||
Common::String gameid = ConfMan.get("gameid");
|
||||
|
||||
for (const ADObsoleteGameID *o = params.obsoleteList; o->from; ++o) {
|
||||
if (gameid.equalsIgnoreCase(o->from)) {
|
||||
gameid = o->to;
|
||||
ConfMan.set("gameid", gameid);
|
||||
|
||||
if (o->platform != Common::kPlatformUnknown)
|
||||
ConfMan.set("platform", Common::getPlatformCode(o->platform));
|
||||
|
||||
warning("Target upgraded from %s to %s", o->from, o->to);
|
||||
|
||||
// WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching
|
||||
// undefined target adds launcher entry"
|
||||
if (ConfMan.hasKey("id_came_from_command_line")) {
|
||||
warning("Target came from command line. Skipping save");
|
||||
} else {
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace AdvancedDetector {
|
||||
|
||||
GameDescriptor findGameID(
|
||||
const char *gameid,
|
||||
const PlainGameDescriptor *list,
|
||||
const ADObsoleteGameID *obsoleteList
|
||||
) {
|
||||
// First search the list of supported game IDs for a match.
|
||||
const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, list);
|
||||
if (g)
|
||||
return GameDescriptor(*g);
|
||||
|
||||
// If we didn't find the gameid in the main list, check if it
|
||||
// is an obsolete game id.
|
||||
if (obsoleteList != 0) {
|
||||
const ADObsoleteGameID *o = obsoleteList;
|
||||
while (o->from) {
|
||||
if (0 == strcasecmp(gameid, o->from)) {
|
||||
g = findPlainGameDescriptor(o->to, list);
|
||||
if (g && g->description)
|
||||
return GameDescriptor(gameid, "Obsolete game ID (" + Common::String(g->description) + ")");
|
||||
else
|
||||
return GameDescriptor(gameid, "Obsolete game ID");
|
||||
}
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
// No match found
|
||||
return GameDescriptor();
|
||||
}
|
||||
|
||||
} // End of namespace AdvancedDetector
|
||||
|
||||
static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGameDescriptor *sg) {
|
||||
const char *title = 0;
|
||||
|
||||
while (sg->gameid) {
|
||||
if (!strcasecmp(g.gameid, sg->gameid))
|
||||
title = sg->description;
|
||||
sg++;
|
||||
}
|
||||
|
||||
GameDescriptor gd(g.gameid, title, g.language, g.platform);
|
||||
gd.updateDesc(g.extra);
|
||||
return gd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a preferred target value as
|
||||
* GAMEID-PLAFORM-LANG
|
||||
* or (if ADGF_DEMO has been set)
|
||||
* GAMEID-demo-PLAFORM-LANG
|
||||
*/
|
||||
static Common::String generatePreferredTarget(const Common::String &id, const ADGameDescription *desc) {
|
||||
Common::String res(id);
|
||||
|
||||
if (desc->flags & ADGF_DEMO) {
|
||||
res = res + "-demo";
|
||||
}
|
||||
|
||||
if (desc->flags & ADGF_CD) {
|
||||
res = res + "-cd";
|
||||
}
|
||||
|
||||
if (desc->platform != Common::kPlatformPC && desc->platform != Common::kPlatformUnknown) {
|
||||
res = res + "-" + getPlatformAbbrev(desc->platform);
|
||||
}
|
||||
|
||||
if (desc->language != Common::EN_ANY && desc->language != Common::UNK_LANG && !(desc->flags & ADGF_DROPLANGUAGE)) {
|
||||
res = res + "-" + getLanguageCode(desc->language);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc, const ADParams ¶ms) {
|
||||
if (params.singleid != NULL) {
|
||||
desc["preferredtarget"] = desc["gameid"];
|
||||
desc["gameid"] = params.singleid;
|
||||
}
|
||||
|
||||
if (!(params.flags & kADFlagDontAugmentPreferredTarget)) {
|
||||
if (!desc.contains("preferredtarget"))
|
||||
desc["preferredtarget"] = desc["gameid"];
|
||||
|
||||
desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc);
|
||||
}
|
||||
|
||||
if (params.flags & kADFlagUseExtraAsHint)
|
||||
desc["extra"] = realDesc->extra;
|
||||
}
|
||||
|
||||
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
|
||||
ADGameDescList matches = detectGame(fslist, params, Common::UNK_LANG, Common::kPlatformUnknown, "");
|
||||
GameList detectedGames;
|
||||
|
||||
// Use fallback detector if there were no matches by other means
|
||||
if (matches.empty()) {
|
||||
const ADGameDescription *fallbackDesc = fallbackDetect(fslist);
|
||||
if (fallbackDesc != 0) {
|
||||
GameDescriptor desc(toGameDescriptor(*fallbackDesc, params.list));
|
||||
updateGameDescriptor(desc, fallbackDesc, params);
|
||||
detectedGames.push_back(desc);
|
||||
}
|
||||
} else for (uint i = 0; i < matches.size(); i++) { // Otherwise use the found matches
|
||||
GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
|
||||
updateGameDescriptor(desc, matches[i], params);
|
||||
detectedGames.push_back(desc);
|
||||
}
|
||||
|
||||
return detectedGames;
|
||||
}
|
||||
|
||||
Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
|
||||
assert(engine);
|
||||
upgradeTargetIfNecessary(params);
|
||||
|
||||
const ADGameDescription *agdDesc = 0;
|
||||
Common::Language language = Common::UNK_LANG;
|
||||
Common::Platform platform = Common::kPlatformUnknown;
|
||||
Common::String extra;
|
||||
|
||||
if (ConfMan.hasKey("language"))
|
||||
language = Common::parseLanguage(ConfMan.get("language"));
|
||||
if (ConfMan.hasKey("platform"))
|
||||
platform = Common::parsePlatform(ConfMan.get("platform"));
|
||||
if (params.flags & kADFlagUseExtraAsHint)
|
||||
if (ConfMan.hasKey("extra"))
|
||||
extra = ConfMan.get("extra");
|
||||
|
||||
Common::String gameid = ConfMan.get("gameid");
|
||||
|
||||
Common::String path;
|
||||
if (ConfMan.hasKey("path")) {
|
||||
path = ConfMan.get("path");
|
||||
} else {
|
||||
path = ".";
|
||||
warning("No path was provided. Assuming the data files are in the current directory");
|
||||
}
|
||||
Common::FSNode dir(path);
|
||||
Common::FSList files;
|
||||
if (!dir.isDirectory() || !dir.getChildren(files, Common::FSNode::kListAll)) {
|
||||
warning("Game data path does not exist or is not a directory (%s)", path.c_str());
|
||||
return Common::kNoGameDataFoundError;
|
||||
}
|
||||
|
||||
ADGameDescList matches = detectGame(files, params, language, platform, extra);
|
||||
|
||||
if (params.singleid == NULL) {
|
||||
for (uint i = 0; i < matches.size(); i++) {
|
||||
if (matches[i]->gameid == gameid) {
|
||||
agdDesc = matches[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (matches.size() > 0) {
|
||||
agdDesc = matches[0];
|
||||
}
|
||||
|
||||
if (agdDesc == 0) {
|
||||
// Use fallback detector if there were no matches by other means
|
||||
agdDesc = fallbackDetect(files);
|
||||
if (agdDesc != 0) {
|
||||
// Seems we found a fallback match. But first perform a basic
|
||||
// sanity check: the gameid must match.
|
||||
if (params.singleid == NULL && agdDesc->gameid != gameid)
|
||||
agdDesc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (agdDesc == 0) {
|
||||
return Common::kNoGameDataFoundError;
|
||||
}
|
||||
|
||||
debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str());
|
||||
if (!createInstance(syst, engine, agdDesc)) {
|
||||
return Common::kNoGameDataFoundError;
|
||||
}
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
struct SizeMD5 {
|
||||
int size;
|
||||
char md5[32+1];
|
||||
};
|
||||
|
||||
typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map;
|
||||
typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
|
||||
|
||||
static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSizeMD5) {
|
||||
// TODO: This message should be cleaned up / made more specific.
|
||||
// For example, we should specify at least which engine triggered this.
|
||||
//
|
||||
// Might also be helpful to display the full path (for when this is used
|
||||
// from the mass detector).
|
||||
printf("The game in '%s' seems to be unknown.\n", path.getPath().c_str());
|
||||
printf("Please, report the following data to the ScummVM team along with name\n");
|
||||
printf("of the game you tried to add and its version/language/etc.:\n");
|
||||
|
||||
for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file)
|
||||
printf(" \"%s\", \"%s\", %d\n", file->_key.c_str(), file->_value.md5, file->_value.size);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams ¶ms);
|
||||
|
||||
static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String extra) {
|
||||
FileMap allFiles;
|
||||
SizeMD5Map filesSizeMD5;
|
||||
|
||||
const ADGameFileDescription *fileDesc;
|
||||
const ADGameDescription *g;
|
||||
const byte *descPtr;
|
||||
|
||||
if (fslist.empty())
|
||||
return ADGameDescList();
|
||||
Common::FSNode parent = fslist.begin()->getParent();
|
||||
debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());
|
||||
|
||||
// First we compose a hashmap of all files in fslist.
|
||||
// Includes nifty stuff like removing trailing dots and ignoring case.
|
||||
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
if (file->isDirectory())
|
||||
continue;
|
||||
|
||||
Common::String tstr = file->getName();
|
||||
|
||||
// Strip any trailing dot
|
||||
if (tstr.lastChar() == '.')
|
||||
tstr.deleteLastChar();
|
||||
|
||||
allFiles[tstr] = *file; // Record the presence of this file
|
||||
}
|
||||
|
||||
// Check which files are included in some ADGameDescription *and* present
|
||||
// in fslist. Compute MD5s and file sizes for these files.
|
||||
for (descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize) {
|
||||
g = (const ADGameDescription *)descPtr;
|
||||
|
||||
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
|
||||
Common::String fname = fileDesc->fileName;
|
||||
if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
|
||||
debug(3, "+ %s", fname.c_str());
|
||||
|
||||
SizeMD5 tmp;
|
||||
if (!md5_file_string(allFiles[fname], tmp.md5, params.md5Bytes))
|
||||
tmp.md5[0] = 0;
|
||||
|
||||
debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5);
|
||||
|
||||
Common::File testFile;
|
||||
if (testFile.open(allFiles[fname]))
|
||||
tmp.size = (int32)testFile.size();
|
||||
else
|
||||
tmp.size = -1;
|
||||
|
||||
filesSizeMD5[fname] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ADGameDescList matched;
|
||||
int maxFilesMatched = 0;
|
||||
bool gotAnyMatchesWithAllFiles = false;
|
||||
|
||||
// MD5 based matching
|
||||
uint i;
|
||||
for (i = 0, descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize, ++i) {
|
||||
g = (const ADGameDescription *)descPtr;
|
||||
bool fileMissing = false;
|
||||
|
||||
// Do not even bother to look at entries which do not have matching
|
||||
// language and platform (if specified).
|
||||
if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language) ||
|
||||
(platform != Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown && g->platform != platform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((params.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
|
||||
continue;
|
||||
|
||||
bool allFilesPresent = true;
|
||||
|
||||
// Try to match all files for this game
|
||||
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
|
||||
Common::String tstr = fileDesc->fileName;
|
||||
|
||||
if (!filesSizeMD5.contains(tstr)) {
|
||||
fileMissing = true;
|
||||
allFilesPresent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileDesc->md5 != NULL && 0 != strcmp(fileDesc->md5, filesSizeMD5[tstr].md5)) {
|
||||
debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5);
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) {
|
||||
debug(3, "Size Mismatch. Skipping");
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
debug(3, "Matched file: %s", tstr.c_str());
|
||||
}
|
||||
|
||||
// We found at least one entry with all required files present.
|
||||
// That means that we got new variant of the game.
|
||||
//
|
||||
// Wihtout this check we would have errorneous checksum display
|
||||
// where only located files will be enlisted.
|
||||
//
|
||||
// Potentially this could rule out variants where some particular file
|
||||
// is really missing, but the developers should better know about such
|
||||
// cases.
|
||||
if (allFilesPresent)
|
||||
gotAnyMatchesWithAllFiles = true;
|
||||
|
||||
if (!fileMissing) {
|
||||
debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
|
||||
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
|
||||
|
||||
// Count the number of matching files. Then, only keep those
|
||||
// entries which match a maximal amount of files.
|
||||
int curFilesMatched = 0;
|
||||
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++)
|
||||
curFilesMatched++;
|
||||
|
||||
if (curFilesMatched > maxFilesMatched) {
|
||||
debug(2, " ... new best match, removing all previous candidates");
|
||||
maxFilesMatched = curFilesMatched;
|
||||
|
||||
for (uint j = 0; j < matched.size();) {
|
||||
if (matched[j]->flags & ADGF_KEEPMATCH)
|
||||
++j;
|
||||
else
|
||||
matched.remove_at(j);
|
||||
}
|
||||
matched.push_back(g);
|
||||
} else if (curFilesMatched == maxFilesMatched) {
|
||||
matched.push_back(g);
|
||||
} else {
|
||||
debug(2, " ... skipped");
|
||||
}
|
||||
|
||||
} else {
|
||||
debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
|
||||
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find a match
|
||||
if (matched.empty()) {
|
||||
if (!filesSizeMD5.empty() && gotAnyMatchesWithAllFiles) {
|
||||
reportUnknown(parent, filesSizeMD5);
|
||||
}
|
||||
|
||||
// Filename based fallback
|
||||
if (params.fileBasedFallback != 0)
|
||||
matched = detectGameFilebased(allFiles, params);
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for each ADFileBasedFallback record whether all files listed
|
||||
* in it are present. If multiple pass this test, we pick the one with
|
||||
* the maximal number of matching files. In case of a tie, the entry
|
||||
* coming first in the list is chosen.
|
||||
*/
|
||||
static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams ¶ms) {
|
||||
const ADFileBasedFallback *ptr;
|
||||
const char* const* filenames;
|
||||
|
||||
int maxNumMatchedFiles = 0;
|
||||
const ADGameDescription *matchedDesc = 0;
|
||||
|
||||
for (ptr = params.fileBasedFallback; ptr->desc; ++ptr) {
|
||||
const ADGameDescription *agdesc = (const ADGameDescription *)ptr->desc;
|
||||
int numMatchedFiles = 0;
|
||||
bool fileMissing = false;
|
||||
|
||||
for (filenames = ptr->filenames; *filenames; ++filenames) {
|
||||
debug(3, "++ %s", *filenames);
|
||||
if (!allFiles.contains(*filenames)) {
|
||||
fileMissing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
numMatchedFiles++;
|
||||
}
|
||||
|
||||
if (!fileMissing) {
|
||||
debug(4, "Matched: %s", agdesc->gameid);
|
||||
|
||||
if (numMatchedFiles > maxNumMatchedFiles) {
|
||||
matchedDesc = agdesc;
|
||||
maxNumMatchedFiles = numMatchedFiles;
|
||||
|
||||
debug(4, "and overriden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ADGameDescList matched;
|
||||
|
||||
if (matchedDesc) { // We got a match
|
||||
matched.push_back(matchedDesc);
|
||||
if (params.flags & kADFlagPrintWarningOnFileBasedFallback) {
|
||||
printf("Your game version has been detected using filename matching as a\n");
|
||||
printf("variant of %s.\n", matchedDesc->gameid);
|
||||
printf("If this is an original and unmodified version, please report any\n");
|
||||
printf("information previously printed by ScummVM to the team.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
GameList AdvancedMetaEngine::getSupportedGames() const {
|
||||
return gameIDList(params);
|
||||
}
|
||||
GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const {
|
||||
return AdvancedDetector::findGameID(gameid, params.list, params.obsoleteList);
|
||||
}
|
218
engines/advancedDetector.h
Normal file
218
engines/advancedDetector.h
Normal file
@ -0,0 +1,218 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
#ifndef ENGINES_ADVANCED_DETECTOR_H
|
||||
#define ENGINES_ADVANCED_DETECTOR_H
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "common/error.h"
|
||||
|
||||
#include "engines/metaengine.h"
|
||||
|
||||
|
||||
struct ADGameFileDescription {
|
||||
const char *fileName;
|
||||
uint16 fileType; // Optional. Not used during detection, only by engines.
|
||||
const char *md5; // Optional. May be NULL.
|
||||
int32 fileSize; // Optional. Set to -1 to ignore.
|
||||
};
|
||||
|
||||
#define AD_ENTRY1(f, x) {{ f, 0, x, -1}, {NULL, 0, NULL, 0}}
|
||||
#define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, {NULL, 0, NULL, 0}}
|
||||
|
||||
enum ADGameFlags {
|
||||
ADGF_NO_FLAGS = 0,
|
||||
ADGF_KEEPMATCH = (1 << 27), // this entry is kept even when there are matched
|
||||
// entries with more files
|
||||
ADGF_DROPLANGUAGE = (1 << 28), // don't add language to gameid
|
||||
ADGF_CD = (1 << 29), // add "-cd" to gameid
|
||||
ADGF_DEMO = (1 << 30) // add "-demo" to gameid
|
||||
};
|
||||
|
||||
struct ADGameDescription {
|
||||
const char *gameid;
|
||||
const char *extra;
|
||||
ADGameFileDescription filesDescriptions[14];
|
||||
Common::Language language;
|
||||
Common::Platform platform;
|
||||
|
||||
/**
|
||||
* A bitmask of extra flags. The top 8 bits are reserved for generic flags
|
||||
* defined in the ADGameFlags. This leaves 24 flags to be used by client
|
||||
* code.
|
||||
*/
|
||||
uint32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* End marker for a table of ADGameDescription structs. Use this to
|
||||
* terminate a list to be passed to the AdvancedDetector API.
|
||||
*/
|
||||
#define AD_TABLE_END_MARKER \
|
||||
{ NULL, NULL, { { NULL, 0, NULL, 0 } }, Common::UNK_LANG, Common::kPlatformUnknown, ADGF_NO_FLAGS }
|
||||
|
||||
|
||||
struct ADObsoleteGameID {
|
||||
const char *from;
|
||||
const char *to;
|
||||
Common::Platform platform;
|
||||
};
|
||||
|
||||
struct ADFileBasedFallback {
|
||||
/**
|
||||
* Pointer to an ADGameDescription or subclass thereof which will get
|
||||
* returned if there's a detection match.
|
||||
*/
|
||||
const void *desc;
|
||||
|
||||
/**
|
||||
* A zero-terminated list of filenames used for matching. All files in
|
||||
* the list must be present to get a detection match.
|
||||
*/
|
||||
const char *filenames[10];
|
||||
};
|
||||
|
||||
|
||||
enum ADFlags {
|
||||
/**
|
||||
* Generate/augment preferred target with information on the language (if
|
||||
* not equal to english) and platform (if not equal to PC).
|
||||
*/
|
||||
kADFlagDontAugmentPreferredTarget = (1 << 0),
|
||||
kADFlagPrintWarningOnFileBasedFallback = (1 << 1),
|
||||
kADFlagUseExtraAsHint = (1 << 2)
|
||||
};
|
||||
|
||||
/**
|
||||
* A structure containing all parameters for the AdvancedDetector.
|
||||
* Typically, an engine will have a single instance of this which is
|
||||
* used by its AdvancedMetaEngine subclass as a parameter to the
|
||||
* primary AdvancedMetaEngine constructor.
|
||||
*/
|
||||
struct ADParams {
|
||||
/**
|
||||
* Pointer to an array of objects which are either ADGameDescription
|
||||
* or superset structures (i.e. start with an ADGameDescription member.
|
||||
* The list is terminated by an entry with a gameid equal to 0
|
||||
* (see AD_TABLE_END_MARKER).
|
||||
*/
|
||||
const byte *descs;
|
||||
|
||||
/**
|
||||
* The size of a single entry of the above descs array. Always
|
||||
* must be >= sizeof(ADGameDescription).
|
||||
*/
|
||||
uint descItemSize;
|
||||
|
||||
/**
|
||||
* The number of bytes to compute MD5 sum for. The AdvancedDetector
|
||||
* is primarily based on computing and matching MD5 checksums of files.
|
||||
* Since doing that for large files can be slow, it can be restricted
|
||||
* to a subset of all files.
|
||||
* Typically this will be set to something between 5 and 50 kilobyte,
|
||||
* but arbitrary non-zero values are possible.
|
||||
*/
|
||||
uint md5Bytes;
|
||||
|
||||
/**
|
||||
* A list of all gameids (and their corresponding descriptions) supported
|
||||
* by this engine.
|
||||
*/
|
||||
const PlainGameDescriptor *list;
|
||||
|
||||
/**
|
||||
* Structure for autoupgrading obsolete targets (optional).
|
||||
*
|
||||
* @todo Properly explain this.
|
||||
*/
|
||||
const ADObsoleteGameID *obsoleteList;
|
||||
|
||||
/**
|
||||
* Name of single gameid (optional).
|
||||
*
|
||||
* @todo Properly explain this -- what does it do?
|
||||
*/
|
||||
const char *singleid;
|
||||
|
||||
/**
|
||||
* List of files for file-based fallback detection (optional).
|
||||
* This is used if the regular MD5 based detection failed to
|
||||
* detect anything.
|
||||
* As usual this list is terminated by an all-zero entry.
|
||||
*
|
||||
* @todo Properly explain this
|
||||
*/
|
||||
const ADFileBasedFallback *fileBasedFallback;
|
||||
|
||||
/**
|
||||
* A bitmask of flags which can be used to configure the behavior
|
||||
* of the AdvancedDetector. Refer to ADFlags for a list of flags
|
||||
* that can be ORed together and passed here.
|
||||
*/
|
||||
uint32 flags;
|
||||
};
|
||||
|
||||
|
||||
namespace AdvancedDetector {
|
||||
|
||||
/**
|
||||
* Scan through the game descriptors specified in params and search for
|
||||
* 'gameid' in there. If a match is found, returns a GameDescriptor
|
||||
* with gameid and description set.
|
||||
*/
|
||||
GameDescriptor findGameID(
|
||||
const char *gameid,
|
||||
const PlainGameDescriptor *list,
|
||||
const ADObsoleteGameID *obsoleteList = 0
|
||||
);
|
||||
|
||||
} // End of namespace AdvancedDetector
|
||||
|
||||
/**
|
||||
* A MetaEngine implementation based around the advanced detector code.
|
||||
*/
|
||||
class AdvancedMetaEngine : public MetaEngine {
|
||||
const ADParams ¶ms;
|
||||
public:
|
||||
AdvancedMetaEngine(const ADParams &dp) : params(dp) {}
|
||||
|
||||
virtual GameList getSupportedGames() const;
|
||||
virtual GameDescriptor findGame(const char *gameid) const;
|
||||
virtual GameList detectGames(const Common::FSList &fslist) const;
|
||||
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
|
||||
|
||||
// To be provided by subclasses
|
||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const = 0;
|
||||
|
||||
/**
|
||||
* An (optional) generic fallback detect function which is invoked
|
||||
* if both the regular MD5 based detection as well as the file
|
||||
* based fallback failed to detect anything.
|
||||
*/
|
||||
virtual const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
146
engines/engine.cpp
Normal file
146
engines/engine.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
// winnt.h defines ARRAYSIZE, but we want our own one...
|
||||
#undef ARRAYSIZE
|
||||
#endif
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "mixer/mixer.h"
|
||||
#include "engines/metaengine.h"
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
extern bool isSmartphone(void);
|
||||
#endif
|
||||
|
||||
Engine::Engine(OSystem *syst)
|
||||
: _system(syst),
|
||||
_mixer(_system->getMixer()),
|
||||
_timer(_system->getTimerManager()),
|
||||
_eventMan(_system->getEventManager()),
|
||||
_saveFileMan(_system->getSavefileManager()),
|
||||
_targetName(ConfMan.getActiveDomainName()),
|
||||
_gameDataDir(ConfMan.get("path")),
|
||||
_pauseLevel(0) {
|
||||
|
||||
// FIXME: Get rid of the following again. It is only here temporarily.
|
||||
// We really should never run with a non-working Mixer, so ought to handle
|
||||
// this at a much earlier stage. If we *really* want to support systems
|
||||
// without a working mixer, then we need more work. E.g. we could modify the
|
||||
// Mixer to immediately drop any streams passed to it. This way, at least
|
||||
// we don't crash because heaps of (sound) memory get allocated but never
|
||||
// freed. Of course, there still would be problems with many games...
|
||||
if (!_mixer->isReady())
|
||||
warning("Sound initialization failed. This may cause severe problems in some games.");
|
||||
}
|
||||
|
||||
Engine::~Engine() {
|
||||
_mixer->stopAll();
|
||||
}
|
||||
|
||||
bool Engine::shouldPerformAutoSave(int lastSaveTime) {
|
||||
const int diff = _system->getMillis() - lastSaveTime;
|
||||
const int autosavePeriod = ConfMan.getInt("autosave_period");
|
||||
return autosavePeriod != 0 && diff > autosavePeriod * 1000;
|
||||
}
|
||||
|
||||
void Engine::pauseEngine(bool pause) {
|
||||
assert((pause && _pauseLevel >= 0) || (!pause && _pauseLevel));
|
||||
|
||||
if (pause)
|
||||
_pauseLevel++;
|
||||
else
|
||||
_pauseLevel--;
|
||||
|
||||
if (_pauseLevel == 1) {
|
||||
pauseEngineIntern(true);
|
||||
} else if (_pauseLevel == 0) {
|
||||
pauseEngineIntern(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::pauseEngineIntern(bool pause) {
|
||||
// By default, just (un)pause all digital sounds
|
||||
_mixer->pauseAll(pause);
|
||||
}
|
||||
|
||||
void Engine::syncSoundSettings() {
|
||||
|
||||
// Sync the engine with the config manager
|
||||
int soundVolumeMusic = ConfMan.getInt("music_volume");
|
||||
int soundVolumeSFX = ConfMan.getInt("sfx_volume");
|
||||
int soundVolumeSpeech = ConfMan.getInt("speech_volume");
|
||||
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
|
||||
}
|
||||
|
||||
Common::Error Engine::loadGameState(int slot) {
|
||||
// Do nothing by default
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool Engine::canLoadGameStateCurrently() {
|
||||
// Do not allow loading by default
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Error Engine::saveGameState(int slot, const char *desc) {
|
||||
// Do nothing by default
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool Engine::canSaveGameStateCurrently() {
|
||||
// Do not allow saving by default
|
||||
return false;
|
||||
}
|
||||
|
||||
void Engine::quitGame() {
|
||||
Common::Event event;
|
||||
|
||||
event.type = Common::EVENT_QUIT;
|
||||
g_system->getEventManager()->pushEvent(event);
|
||||
}
|
||||
|
||||
/*
|
||||
EnginePlugin *Engine::getMetaEnginePlugin() const {
|
||||
|
||||
const EnginePlugin *plugin = 0;
|
||||
Common::String gameid = ConfMan.get("gameid");
|
||||
gameid.toLowercase();
|
||||
EngineMan.findGame(gameid, &plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
*/
|
211
engines/engine.h
Normal file
211
engines/engine.h
Normal file
@ -0,0 +1,211 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef ENGINES_ENGINE_H
|
||||
#define ENGINES_ENGINE_H
|
||||
|
||||
#include "common/sys.h"
|
||||
#include "common/error.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/str.h"
|
||||
|
||||
class OSystem;
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
namespace Common {
|
||||
class EventManager;
|
||||
class SaveFileManager;
|
||||
class TimerManager;
|
||||
}
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
OSystem *_system;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
protected:
|
||||
Common::TimerManager *_timer;
|
||||
Common::EventManager *_eventMan;
|
||||
Common::SaveFileManager *_saveFileMan;
|
||||
|
||||
const Common::String _targetName; // target name for saves
|
||||
|
||||
const Common::FSNode _gameDataDir; // FIXME: Get rid of this
|
||||
|
||||
private:
|
||||
/**
|
||||
* The pause level, 0 means 'running', a positive value indicates
|
||||
* how often the engine has been paused (and hence how often it has
|
||||
* to be un-paused before it resumes running). This makes it possible
|
||||
* to nest code which pauses the engine.
|
||||
*/
|
||||
int _pauseLevel;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* A feature in this context means an ability of the engine which can be
|
||||
* either available or not.
|
||||
* @see Engine::hasFeature()
|
||||
*/
|
||||
enum EngineFeature {
|
||||
/**
|
||||
* Enables the subtitle speed and toggle items in the Options section
|
||||
* of the global main menu.
|
||||
*/
|
||||
kSupportsSubtitleOptions,
|
||||
|
||||
/**
|
||||
* 'Return to launcher' feature is supported, i.e., EVENT_RTL is handled
|
||||
* either directly, or indirectly (that is, the engine calls and honors
|
||||
* the result of the Engine::shouldQuit() method appropriately).
|
||||
*/
|
||||
kSupportsRTL,
|
||||
|
||||
/**
|
||||
* Loading savestates during runtime is supported, that is, this engine
|
||||
* implements loadGameState() and canLoadGameStateCurrently().
|
||||
* If this feature is supported, then the corresponding MetaEngine *must*
|
||||
* support the kSupportsListSaves feature.
|
||||
*/
|
||||
kSupportsLoadingDuringRuntime,
|
||||
|
||||
/**
|
||||
* Loading savestates during runtime is supported, that is, this engine
|
||||
* implements saveGameState() and canSaveGameStateCurrently().
|
||||
* If this feature is supported, then the corresponding MetaEngine *must*
|
||||
* support the kSupportsListSaves feature.
|
||||
*/
|
||||
kSupportsSavingDuringRuntime
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @name Overloadable methods
|
||||
*
|
||||
* All Engine subclasses should consider overloading some or all of the following methods.
|
||||
*/
|
||||
//@{
|
||||
|
||||
Engine(OSystem *syst);
|
||||
virtual ~Engine();
|
||||
|
||||
/**
|
||||
* Init the engine and start its main loop.
|
||||
* @return returns kNoError on success, else an error code.
|
||||
*/
|
||||
virtual Common::Error run() = 0;
|
||||
|
||||
/**
|
||||
* Determine whether the engine supports the specified feature.
|
||||
*/
|
||||
virtual bool hasFeature(EngineFeature f) const { return false; }
|
||||
|
||||
// virtual EnginePlugin *getMetaEnginePlugin() const;
|
||||
|
||||
/**
|
||||
* Notify the engine that the sound settings in the config manager may have
|
||||
* changed and that it hence should adjust any internal volume etc. values
|
||||
* accordingly.
|
||||
* @todo find a better name for this
|
||||
*/
|
||||
virtual void syncSoundSettings();
|
||||
|
||||
/**
|
||||
* Load a game state.
|
||||
* @param slot the slot from which a savestate should be loaded
|
||||
* @return returns kNoError on success, else an error code.
|
||||
*/
|
||||
virtual Common::Error loadGameState(int slot);
|
||||
|
||||
/**
|
||||
* Indicates whether a game state can be loaded.
|
||||
*/
|
||||
virtual bool canLoadGameStateCurrently();
|
||||
|
||||
/**
|
||||
* Save a game state.
|
||||
* @param slot the slot into which the savestate should be stored
|
||||
* @param desc a description for the savestate, entered by the user
|
||||
* @return returns kNoError on success, else an error code.
|
||||
*/
|
||||
virtual Common::Error saveGameState(int slot, const char *desc);
|
||||
|
||||
/**
|
||||
* Indicates whether a game state can be saved.
|
||||
*/
|
||||
virtual bool canSaveGameStateCurrently();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Actual implementation of pauseEngine by subclasses. See there
|
||||
* for details.
|
||||
*/
|
||||
virtual void pauseEngineIntern(bool pause);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Request the engine to quit. Sends a EVENT_QUIT event to the Event
|
||||
* Manager.
|
||||
*/
|
||||
static void quitGame();
|
||||
|
||||
/**
|
||||
* Pause or resume the engine. This should stop/resume any audio playback
|
||||
* and other stuff. Called right before the system runs a global dialog
|
||||
* (like a global pause, main menu, options or 'confirm exit' dialog).
|
||||
*
|
||||
* This is a convenience tracker which automatically keeps track on how
|
||||
* often the engine has been paused, ensuring that after pausing an engine
|
||||
* e.g. twice, it has to be unpaused twice before actuallying resuming.
|
||||
*
|
||||
* @param pause true to pause the engine, false to resume it
|
||||
*/
|
||||
void pauseEngine(bool pause);
|
||||
|
||||
/**
|
||||
* Return whether the engine is currently paused or not.
|
||||
*/
|
||||
bool isPaused() const { return _pauseLevel != 0; }
|
||||
|
||||
public:
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Indicate whether an autosave should be performed.
|
||||
*/
|
||||
bool shouldPerformAutoSave(int lastSaveTime);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
2
engines/engines.mk
Normal file
2
engines/engines.mk
Normal file
@ -0,0 +1,2 @@
|
||||
DEFINES +=
|
||||
MODULES += engines/grim
|
131
engines/game.cpp
Normal file
131
engines/game.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/game.h"
|
||||
#include "base/plugins.h"
|
||||
|
||||
|
||||
const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) {
|
||||
const PlainGameDescriptor *g = list;
|
||||
while (g->gameid) {
|
||||
if (0 == strcasecmp(gameid, g->gameid))
|
||||
return g;
|
||||
g++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GameDescriptor::GameDescriptor() {
|
||||
setVal("gameid", "");
|
||||
setVal("description", "");
|
||||
}
|
||||
|
||||
GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd) {
|
||||
setVal("gameid", pgd.gameid);
|
||||
setVal("description", pgd.description);
|
||||
}
|
||||
|
||||
GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l, Common::Platform p) {
|
||||
setVal("gameid", g);
|
||||
setVal("description", d);
|
||||
if (l != Common::UNK_LANG)
|
||||
setVal("language", Common::getLanguageCode(l));
|
||||
if (p != Common::kPlatformUnknown)
|
||||
setVal("platform", Common::getPlatformCode(p));
|
||||
}
|
||||
|
||||
void GameDescriptor::updateDesc(const char *extra) {
|
||||
// TODO: The format used here (LANG/PLATFORM/EXTRA) is not set in stone.
|
||||
// We may want to change the order (PLATFORM/EXTRA/LANG, anybody?), or
|
||||
// the seperator (instead of '/' use ', ' or ' ').
|
||||
const bool hasCustomLanguage = (language() != Common::UNK_LANG);
|
||||
const bool hasCustomPlatform = (platform() != Common::kPlatformUnknown);
|
||||
const bool hasExtraDesc = (extra && extra[0]);
|
||||
|
||||
// Adapt the description string if custom platform/language is set.
|
||||
if (hasCustomLanguage || hasCustomPlatform || hasExtraDesc) {
|
||||
Common::String descr = description();
|
||||
|
||||
descr += " (";
|
||||
if (hasExtraDesc)
|
||||
descr += extra;
|
||||
if (hasCustomPlatform) {
|
||||
if (hasExtraDesc)
|
||||
descr += "/";
|
||||
descr += Common::getPlatformDescription(platform());
|
||||
}
|
||||
if (hasCustomLanguage) {
|
||||
if (hasExtraDesc || hasCustomPlatform)
|
||||
descr += "/";
|
||||
descr += Common::getLanguageDescription(language());
|
||||
}
|
||||
descr += ")";
|
||||
setVal("description", descr);
|
||||
}
|
||||
}
|
||||
|
||||
bool SaveStateDescriptor::getBool(const Common::String &key) const {
|
||||
if (contains(key)) {
|
||||
Common::String value = getVal(key);
|
||||
if (value.equalsIgnoreCase("true") ||
|
||||
value.equalsIgnoreCase("yes") ||
|
||||
value.equals("1"))
|
||||
return true;
|
||||
if (value.equalsIgnoreCase("false") ||
|
||||
value.equalsIgnoreCase("no") ||
|
||||
value.equals("0"))
|
||||
return false;
|
||||
error("SaveStateDescriptor: %s '%s' has unknown value '%s' for boolean '%s'",
|
||||
save_slot().c_str(), description().c_str(), value.c_str(), key.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SaveStateDescriptor::setDeletableFlag(bool state) {
|
||||
setVal("is_deletable", state ? "true" : "false");
|
||||
}
|
||||
|
||||
void SaveStateDescriptor::setWriteProtectedFlag(bool state) {
|
||||
setVal("is_write_protected", state ? "true" : "false");
|
||||
}
|
||||
|
||||
void SaveStateDescriptor::setSaveDate(int year, int month, int day) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, 32, "%.2d.%.2d.%.4d", day, month, year);
|
||||
setVal("save_date", buffer);
|
||||
}
|
||||
|
||||
void SaveStateDescriptor::setSaveTime(int hour, int min) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, 32, "%.2d:%.2d", hour, min);
|
||||
setVal("save_time", buffer);
|
||||
}
|
||||
|
||||
void SaveStateDescriptor::setPlayTime(int hours, int minutes) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, 32, "%.2d:%.2d", hours, minutes);
|
||||
setVal("play_time", buffer);
|
||||
}
|
||||
|
180
engines/game.h
Normal file
180
engines/game.h
Normal file
@ -0,0 +1,180 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ENGINES_GAME_H
|
||||
#define ENGINES_GAME_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/array.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
|
||||
/**
|
||||
* A simple structure used to map gameids (like "monkey", "sword1", ...) to
|
||||
* nice human readable and descriptive game titles (like "The Secret of Monkey Island").
|
||||
* This is a plain struct to make it possible to declare NULL-terminated C arrays
|
||||
* consisting of PlainGameDescriptors.
|
||||
*/
|
||||
struct PlainGameDescriptor {
|
||||
const char *gameid;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a list of PlainGameDescriptors, returns the first PlainGameDescriptor
|
||||
* matching the given gameid. If not match is found return 0.
|
||||
* The end of the list must marked by a PlainGameDescriptor with gameid equal to 0.
|
||||
*/
|
||||
const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list);
|
||||
|
||||
/**
|
||||
* A hashmap describing details about a given game. In a sense this is a refined
|
||||
* version of PlainGameDescriptor, as it also contains a gameid and a description string.
|
||||
* But in addition, platform and language settings, as well as arbitrary other settings,
|
||||
* can be contained in a GameDescriptor.
|
||||
* This is an essential part of the glue between the game engines and the launcher code.
|
||||
*/
|
||||
class GameDescriptor : public Common::StringMap {
|
||||
public:
|
||||
GameDescriptor();
|
||||
GameDescriptor(const PlainGameDescriptor &pgd);
|
||||
GameDescriptor(const Common::String &gameid,
|
||||
const Common::String &description,
|
||||
Common::Language language = Common::UNK_LANG,
|
||||
Common::Platform platform = Common::kPlatformUnknown);
|
||||
|
||||
/**
|
||||
* Update the description string by appending (LANG/PLATFORM/EXTRA) to it.
|
||||
*/
|
||||
void updateDesc(const char *extra = 0);
|
||||
|
||||
Common::String &gameid() { return getVal("gameid"); }
|
||||
Common::String &description() { return getVal("description"); }
|
||||
const Common::String &gameid() const { return getVal("gameid"); }
|
||||
const Common::String &description() const { return getVal("description"); }
|
||||
Common::Language language() const { return contains("language") ? Common::parseLanguage(getVal("language")) : Common::UNK_LANG; }
|
||||
Common::Platform platform() const { return contains("platform") ? Common::parsePlatform(getVal("platform")) : Common::kPlatformUnknown; }
|
||||
|
||||
const Common::String &preferredtarget() const {
|
||||
return contains("preferredtarget") ? getVal("preferredtarget") : getVal("gameid");
|
||||
}
|
||||
};
|
||||
|
||||
/** List of games. */
|
||||
class GameList : public Common::Array<GameDescriptor> {
|
||||
public:
|
||||
GameList() {}
|
||||
GameList(const GameList &list) : Common::Array<GameDescriptor>(list) {}
|
||||
GameList(const PlainGameDescriptor *g) {
|
||||
while (g->gameid) {
|
||||
push_back(GameDescriptor(g->gameid, g->description));
|
||||
g++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A hashmap describing details about a given save state.
|
||||
* TODO
|
||||
* Guaranteed to contain save_slot and description values.
|
||||
* Additional ideas: Playtime, creation date, thumbnail, ...
|
||||
*/
|
||||
class SaveStateDescriptor : public Common::StringMap {
|
||||
protected:
|
||||
|
||||
public:
|
||||
SaveStateDescriptor() {
|
||||
setVal("save_slot", "-1"); // FIXME: default to 0 (first slot) or to -1 (invalid slot) ?
|
||||
setVal("description", "");
|
||||
}
|
||||
|
||||
SaveStateDescriptor(int s, const Common::String &d) {
|
||||
char buf[16];
|
||||
sprintf(buf, "%d", s);
|
||||
setVal("save_slot", buf);
|
||||
setVal("description", d);
|
||||
}
|
||||
|
||||
SaveStateDescriptor(const Common::String &s, const Common::String &d) {
|
||||
setVal("save_slot", s);
|
||||
setVal("description", d);
|
||||
}
|
||||
|
||||
/** 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"); }
|
||||
|
||||
/** Optional entries only included when querying via MetaEngine::querySaveMetaInfo */
|
||||
|
||||
/**
|
||||
* Returns the value of a given key as boolean.
|
||||
* It accepts 'true', 'yes' and '1' for true and
|
||||
* 'false', 'no' and '0' for false.
|
||||
* (FIXME:) On unknown value it errors out ScummVM.
|
||||
* On unknown key it returns false as default.
|
||||
*/
|
||||
bool getBool(const Common::String &key) const;
|
||||
|
||||
/**
|
||||
* Sets the 'is_deletable' key, which indicates if the
|
||||
* given savestate is safe for deletion.
|
||||
*/
|
||||
void setDeletableFlag(bool state);
|
||||
|
||||
/**
|
||||
* Sets the 'is_write_protected' key, which indicates if the
|
||||
* given savestate can be overwritten or not
|
||||
*/
|
||||
void setWriteProtectedFlag(bool state);
|
||||
|
||||
/**
|
||||
* Sets the 'save_date' key properly, based on the given values.
|
||||
*/
|
||||
void setSaveDate(int year, int month, int day);
|
||||
|
||||
/**
|
||||
* Sets the 'save_time' key properly, based on the given values.
|
||||
*/
|
||||
void setSaveTime(int hour, int min);
|
||||
|
||||
/**
|
||||
* Sets the 'play_time' key properly, based on the given values.
|
||||
*/
|
||||
void setPlayTime(int hours, int minutes);
|
||||
};
|
||||
|
||||
/** List of savestates. */
|
||||
typedef Common::Array<SaveStateDescriptor> SaveStateList;
|
||||
|
||||
#endif
|
240
engines/metaengine.h
Normal file
240
engines/metaengine.h
Normal file
@ -0,0 +1,240 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef ENGINES_METAENGINE_H
|
||||
#define ENGINES_METAENGINE_H
|
||||
|
||||
#include "common/sys.h"
|
||||
#include "common/error.h"
|
||||
|
||||
#include "engines/game.h"
|
||||
#include "base/plugins.h"
|
||||
|
||||
class Engine;
|
||||
class OSystem;
|
||||
|
||||
namespace Common {
|
||||
class FSList;
|
||||
class String;
|
||||
}
|
||||
|
||||
/**
|
||||
* A meta engine is essentially a factory for Engine instances with the
|
||||
* added ability of listing and detecting supported games.
|
||||
* Every engine "plugin" provides a hook to get an instance of a MetaEngine
|
||||
* subclass for that "engine plugin". E.g. SCUMM povides ScummMetaEngine.
|
||||
* This is then in turn used by the frontend code to detect games,
|
||||
* and instantiate actual Engine objects.
|
||||
*/
|
||||
class MetaEngine : public PluginObject {
|
||||
public:
|
||||
virtual ~MetaEngine() {}
|
||||
|
||||
/** Returns some copyright information about the original engine. */
|
||||
virtual const char *getOriginalCopyright() const = 0;
|
||||
|
||||
/** 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 Common::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 Common::Error describing the error which occurred, or kNoError
|
||||
*/
|
||||
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const = 0;
|
||||
|
||||
/**
|
||||
* Return a list of all save states associated with the given target.
|
||||
*
|
||||
* The caller has to ensure that this (Meta)Engine is responsible
|
||||
* for the specified target (by using findGame on it respectively
|
||||
* on the associated gameid from the relevant ConfMan entry, if present).
|
||||
*
|
||||
* The default implementation returns an empty list.
|
||||
*
|
||||
* @note MetaEngines must indicate that this function has been implemented
|
||||
* via the kSupportsListSaves feature flag.
|
||||
*
|
||||
* @param target name of a config manager target
|
||||
* @return a list of save state descriptors
|
||||
*/
|
||||
virtual SaveStateList listSaves(const char *target) const {
|
||||
return SaveStateList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum save slot that the engine supports.
|
||||
*
|
||||
* @note MetaEngines must indicate that this function has been implemented
|
||||
* via the kSupportsListSaves feature flag.
|
||||
*
|
||||
* The default implementation limits the save slots to zero (0).
|
||||
*
|
||||
* @return maximum save slot number supported
|
||||
*/
|
||||
virtual int getMaximumSaveSlot() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified save state.
|
||||
*
|
||||
* For most engines this just amounts to calling _saveFileMan->removeSaveFile().
|
||||
* Engines which keep an index file will also update it accordingly.
|
||||
*
|
||||
* @note MetaEngines must indicate that this function has been implemented
|
||||
* via the kSupportsDeleteSave feature flag.
|
||||
*
|
||||
* @param target name of a config manager target
|
||||
* @param slot slot number of the save state to be removed
|
||||
*/
|
||||
virtual void removeSaveState(const char *target, int slot) const {};
|
||||
|
||||
/**
|
||||
* Returns meta infos from the specified save state.
|
||||
*
|
||||
* Depending on the MetaEngineFeatures set this can include
|
||||
* thumbnails, save date / time, play time.
|
||||
*
|
||||
* @param target name of a config manager target
|
||||
* @param slot slot number of the save state
|
||||
*/
|
||||
virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const {
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
/** @name MetaEngineFeature flags */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* A feature in this context means an ability of the engine which can be
|
||||
* either available or not.
|
||||
*/
|
||||
enum MetaEngineFeature {
|
||||
/**
|
||||
* Listing all Save States for a given target is supported, i.e.,
|
||||
* the listSaves() and getMaximumSaveSlot methods are implemented.
|
||||
* Used for --list-saves support, as well as the GMM load dialog.
|
||||
*/
|
||||
kSupportsListSaves,
|
||||
|
||||
/**
|
||||
* Loading from the Launcher / command line (-x)
|
||||
*/
|
||||
kSupportsLoadingDuringStartup,
|
||||
|
||||
/**
|
||||
* Deleting Saves from the Launcher (i.e. implements the
|
||||
* removeSaveState() method)
|
||||
*/
|
||||
kSupportsDeleteSave,
|
||||
|
||||
/**
|
||||
* Features meta infos for savestates (i.e. implements the
|
||||
* querySaveMetaInfos method properly).
|
||||
*
|
||||
* Engines implementing meta infos always have to provide
|
||||
* the following entries in the save state descriptor queried
|
||||
* by querySaveMetaInfos:
|
||||
* - 'is_deletable', which indicates if a given save is
|
||||
* safe for deletion
|
||||
* - 'is_write_protected', which indicates if a given save
|
||||
* can be overwritten by the user.
|
||||
* (note: of course you do not have to
|
||||
* set this, since it defaults to 'false')
|
||||
*/
|
||||
kSavesSupportMetaInfo,
|
||||
|
||||
/**
|
||||
* Features a thumbnail in savegames (i.e. includes a thumbnail
|
||||
* in savestates returned via querySaveMetaInfo).
|
||||
* This flag may only be set when 'kSavesSupportMetaInfo' is set.
|
||||
*/
|
||||
kSavesSupportThumbnail,
|
||||
|
||||
/**
|
||||
* Features 'save_date' and 'save_time' entries in the
|
||||
* savestate returned by querySaveMetaInfo. Those values
|
||||
* indicate the date/time the savegame was created.
|
||||
* This flag may only be set when 'kSavesSupportMetaInfo' is set.
|
||||
*/
|
||||
kSavesSupportCreationDate,
|
||||
|
||||
/**
|
||||
* Features 'play_time' entry in the savestate returned by
|
||||
* querySaveMetaInfo. It indicates how long the user played
|
||||
* the game till the save.
|
||||
* This flag may only be set when 'kSavesSupportMetaInfo' is set.
|
||||
*/
|
||||
kSavesSupportPlayTime
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether the engine supports the specified MetaEngine feature.
|
||||
* Used by e.g. the launcher to determine whether to enable the "Load" button.
|
||||
*/
|
||||
virtual bool hasFeature(MetaEngineFeature f) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
|
||||
// Engine plugins
|
||||
|
||||
typedef PluginSubclass<MetaEngine> EnginePlugin;
|
||||
|
||||
/**
|
||||
* Singleton class which manages all Engine plugins.
|
||||
*/
|
||||
class EngineManager : public Common::Singleton<EngineManager> {
|
||||
private:
|
||||
friend class Common::Singleton<SingletonBaseType>;
|
||||
|
||||
public:
|
||||
GameDescriptor findGame(const Common::String &gameName, const EnginePlugin **plugin = NULL) const;
|
||||
GameList detectGames(const Common::FSList &fslist) const;
|
||||
const EnginePlugin::List &getPlugins() const;
|
||||
};
|
||||
|
||||
/** Convenience shortcut for accessing the engine manager. */
|
||||
#define EngineMan EngineManager::instance()
|
||||
|
||||
#endif
|
9
engines/module.mk
Normal file
9
engines/module.mk
Normal file
@ -0,0 +1,9 @@
|
||||
MODULE := engines
|
||||
|
||||
MODULE_OBJS := \
|
||||
advancedDetector.o \
|
||||
engine.o \
|
||||
game.o
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
Loading…
x
Reference in New Issue
Block a user