diff options
author | Vicent Marti | 2008-06-14 14:44:29 +0000 |
---|---|---|
committer | Vicent Marti | 2008-06-14 14:44:29 +0000 |
commit | d0b27cf9c66b9281899acf826cb205e19dcb7260 (patch) | |
tree | 74e813b1d1f081f35f41ca7a95da5d048951b9e7 /base | |
parent | d51a0cab3fe494698f001d81d5d86cea7cd0395b (diff) | |
parent | 91d3ea31359950b59ee46af8355cc0f5790257e5 (diff) | |
download | scummvm-rg350-d0b27cf9c66b9281899acf826cb205e19dcb7260.tar.gz scummvm-rg350-d0b27cf9c66b9281899acf826cb205e19dcb7260.tar.bz2 scummvm-rg350-d0b27cf9c66b9281899acf826cb205e19dcb7260.zip |
Merged trunk into the GUI branch.
Fixed MSVS9 project files.
svn-id: r32702
Diffstat (limited to 'base')
-rw-r--r-- | base/commandLine.cpp | 27 | ||||
-rw-r--r-- | base/game.cpp | 26 | ||||
-rw-r--r-- | base/game.h | 11 | ||||
-rw-r--r-- | base/main.cpp | 28 | ||||
-rw-r--r-- | base/plugins.cpp | 231 | ||||
-rw-r--r-- | base/plugins.h | 240 |
6 files changed, 359 insertions, 204 deletions
diff --git a/base/commandLine.cpp b/base/commandLine.cpp index a4c867edee..b9fd4ecfb7 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -23,7 +23,7 @@ * */ -#include "engines/engine.h" +#include "engines/metaengine.h" #include "base/commandLine.h" #include "base/plugins.h" #include "base/version.h" @@ -46,6 +46,8 @@ #endif #elif defined(__SYMBIAN32__) #define DEFAULT_SAVE_PATH "Savegames" +#elif defined(PALMOS_MODE) +#define DEFAULT_SAVE_PATH "/PALM/Programs/ScummVM/Saved" #endif #define DETECTOR_TESTING_HACK @@ -146,7 +148,7 @@ static void usage(const char *s, ...) { vsnprintf(buf, STRINGBUFLEN, s, va); va_end(va); -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__) || defined (__SYMBIAN32__)) +#if !(defined(__GP32__) || defined (__SYMBIAN32__)) printf(USAGE_STRING, s_appName, buf, s_appName, s_appName); #endif exit(1); @@ -229,6 +231,9 @@ void registerDefaults() { ConfMan.registerDefault("savepath", savePath); #elif defined (IPHONE) ConfMan.registerDefault("savepath", OSystem_IPHONE::getSavePath()); + +#elif defined(PALMOS_MODE) + ConfMan.registerDefault("savepath", DEFAULT_SAVE_PATH); #endif #endif // #ifdef DEFAULT_SAVE_PATH @@ -559,10 +564,10 @@ static void listGames() { printf("Game ID Full Title \n" "-------------------- ------------------------------------------------------\n"); - const PluginList &plugins = PluginManager::instance().getPlugins(); - PluginList::const_iterator iter = plugins.begin(); + 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(); + 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()); } @@ -586,7 +591,7 @@ static void listTargets() { // to find the proper desc. In fact, the platform probably should // be taken into account, too. Common::String gameid(name); - GameDescriptor g = Base::findGame(gameid); + GameDescriptor g = EngineMan.findGame(gameid); if (g.description().size() > 0) description = g.description(); } @@ -613,8 +618,8 @@ static void listSaves(const char *target) { gameid.toLowercase(); // Normalize it to lower case // Find the plugin that will handle the specified gameid - const Plugin *plugin = 0; - GameDescriptor game = Base::findGame(gameid, &plugin); + const EnginePlugin *plugin = 0; + GameDescriptor game = EngineMan.findGame(gameid, &plugin); if (!plugin) { error("Could not find any plugin to handle gameid '%s' (target '%s')", gameid.c_str(), target); @@ -622,7 +627,7 @@ static void listSaves(const char *target) { } // Query the plugin for a list of savegames - SaveStateList saveList = plugin->listSaves(target); + SaveStateList saveList = (*plugin)->listSaves(target); // TODO: Include more info about the target (desc, engine name, ...) ??? printf("Saves for target '%s':\n", target); @@ -667,7 +672,7 @@ static void runDetectorTest() { continue; } - GameList candidates(PluginManager::instance().detectGames(files)); + GameList candidates(EngineMan.detectGames(files)); bool gameidDiffers = false; GameList::iterator x; for (x = candidates.begin(); x != candidates.end(); ++x) { @@ -740,7 +745,7 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { // domain (i.e. a target) matching this argument, or alternatively // whether there is a gameid matching that name. if (!command.empty()) { - GameDescriptor gd = Base::findGame(command); + GameDescriptor gd = EngineMan.findGame(command); if (ConfMan.hasGameDomain(command) || !gd.gameid().empty()) { bool idCameFromCommandLine = false; diff --git a/base/game.cpp b/base/game.cpp index a79cfddec9..9628543672 100644 --- a/base/game.cpp +++ b/base/game.cpp @@ -75,29 +75,3 @@ void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) { } _thumbnail = t; } - - -namespace Base { - -// TODO: Find a better name & place for this function. -GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin) { - // Find the GameDescriptor for this target - const PluginList &plugins = PluginManager::instance().getPlugins(); - GameDescriptor result; - - if (plugin) - *plugin = 0; - - PluginList::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; -} - -} // End of namespace Base diff --git a/base/game.h b/base/game.h index 08d18247ef..18d7967388 100644 --- a/base/game.h +++ b/base/game.h @@ -179,15 +179,4 @@ public: /** List of savestates. */ typedef Common::Array<SaveStateDescriptor> SaveStateList; - -class Plugin; - -namespace Base { - -// TODO: Find a better name & place for this function. -GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL); - -} // End of namespace Base - - #endif diff --git a/base/main.cpp b/base/main.cpp index d0da338e2a..20775f24db 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -32,6 +32,7 @@ */ #include "engines/engine.h" +#include "engines/metaengine.h" #include "base/commandLine.h" #include "base/plugins.h" #include "base/version.h" @@ -90,8 +91,8 @@ static bool launcherDialog(OSystem &system) { #endif // vector renderer debug } -static const Plugin *detectPlugin() { - const Plugin *plugin = 0; +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()); @@ -102,23 +103,26 @@ static const Plugin *detectPlugin() { ConfMan.set("gameid", gameid); // Query the plugins and find one that will handle the specified gameid - printf("Looking for %s\n", gameid.c_str()); - GameDescriptor game = Base::findGame(gameid, &plugin); + 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 game detection\n"); + 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("Trying to start game '%s'\n", game.description().c_str()); + printf(" Starting '%s'\n", game.description().c_str()); return plugin; } // TODO: specify the possible return values here -static int runGame(const Plugin *plugin, OSystem &system, const Common::String &edebuglevels) { +static int runGame(const EnginePlugin *plugin, OSystem &system, const Common::String &edebuglevels) { Common::String gameDataPath(ConfMan.get("path")); if (gameDataPath.empty()) { } else if (gameDataPath.lastChar() != '/' @@ -151,7 +155,7 @@ static int runGame(const Plugin *plugin, OSystem &system, const Common::String & // Create the game engine Engine *engine = 0; - PluginError err = plugin->createInstance(&system, &engine); + PluginError err = (*plugin)->createInstance(&system, &engine); if (!engine || err != kNoError) { // TODO: Show an error dialog or so? // TODO: Also take 'err' into consideration... @@ -181,7 +185,7 @@ static int runGame(const Plugin *plugin, OSystem &system, const Common::String & // Set the window caption to the game name Common::String caption(ConfMan.get("description")); - Common::String desc = Base::findGame(ConfMan.get("gameid")).description(); + Common::String desc = EngineMan.findGame(ConfMan.get("gameid")).description(); if (caption.empty() && !desc.empty()) caption = desc; if (caption.empty()) @@ -311,11 +315,11 @@ extern "C" int scummvm_main(int argc, char *argv[]) { // cleanly, so this is now enabled to encourage people to fix bits :) while (0 != ConfMan.getActiveDomain()) { // Try to find a plugin which feels responsible for the specified game. - const Plugin *plugin = detectPlugin(); + const EnginePlugin *plugin = detectPlugin(); if (plugin) { // Unload all plugins not needed for this game, // to save memory - PluginManager::instance().unloadPluginsExcept(plugin); + PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, plugin); // Try to run the game int result = runGame(plugin, system, specialDebug); @@ -342,7 +346,7 @@ extern "C" int scummvm_main(int argc, char *argv[]) { launcherDialog(system); } - PluginManager::instance().unloadPluginsExcept(NULL); + PluginManager::instance().unloadPlugins(); PluginManager::destroy(); Common::ConfigManager::destroy(); GUI::NewGui::destroy(); diff --git a/base/plugins.cpp b/base/plugins.cpp index 9e2c2599ce..dcd394495f 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -24,12 +24,23 @@ */ #include "base/plugins.h" -#include "common/util.h" + +#include "common/func.h" + +#ifdef DYNAMIC_MODULES +#include "common/config-manager.h" +#endif + +// Plugin versioning int pluginTypeVersions[PLUGIN_TYPE_MAX] = { PLUGIN_TYPE_ENGINE_VERSION, + PLUGIN_TYPE_MUSIC_VERSION, }; + +// Abstract plugins + PluginType Plugin::getType() const { return _type; } @@ -38,31 +49,6 @@ const char *Plugin::getName() const { return _pluginObject->getName(); } -const char *Plugin::getCopyright() const { - return ((MetaEngine*)_pluginObject)->getCopyright(); -} - -PluginError Plugin::createInstance(OSystem *syst, Engine **engine) const { - return ((MetaEngine*)_pluginObject)->createInstance(syst, engine); -} - -GameList Plugin::getSupportedGames() const { - return ((MetaEngine*)_pluginObject)->getSupportedGames(); -} - -GameDescriptor Plugin::findGame(const char *gameid) const { - return ((MetaEngine*)_pluginObject)->findGame(gameid); -} - -GameList Plugin::detectGames(const FSList &fslist) const { - return ((MetaEngine*)_pluginObject)->detectGames(fslist); -} - -SaveStateList Plugin::listSaves(const char *target) const { - return ((MetaEngine*)_pluginObject)->listSaves(target); -} - - class StaticPlugin : public Plugin { public: StaticPlugin(PluginObject *pluginobject, PluginType type) { @@ -99,6 +85,7 @@ public: // "Loader" for the static plugins. // Iterate over all registered (static) plugins and load them. + // Engine plugins #if PLUGIN_ENABLED_STATIC(SCUMM) LINK_PLUGIN(SCUMM) #endif @@ -157,6 +144,49 @@ public: LINK_PLUGIN(TOUCHE) #endif + // Music plugins + // TODO: Use defines to disable or enable each MIDI driver as a + // static/dynamic plugin, like it's done for the engines + LINK_PLUGIN(NULL) + #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) + LINK_PLUGIN(WINDOWS) + #endif + #if defined(UNIX) && defined(USE_ALSA) + LINK_PLUGIN(ALSA) + #endif + #if defined(UNIX) && !defined(__BEOS__) && !defined(__MAEMO__) + LINK_PLUGIN(SEQ) + #endif + #if defined(IRIX) + LINK_PLUGIN(DMEDIA) + #endif + #if defined(__amigaos4__) + LINK_PLUGIN(CAMD) + #endif + #if defined(MACOSX) + LINK_PLUGIN(COREAUDIO) + LINK_PLUGIN(COREMIDI) + LINK_PLUGIN(QUICKTIME) + #endif + #if defined(PALMOS_MODE) + # if defined(COMPILE_CLIE) + LINK_PLUGIN(YPA1) + # elif defined(COMPILE_ZODIAC) && (!defined(ENABLE_SCUMM) || !defined(PALMOS_ARM)) + LINK_PLUGIN(ZODIAC) + # endif + #endif + #ifdef USE_FLUIDSYNTH + LINK_PLUGIN(FLUIDSYNTH) + #endif + #ifdef USE_MT32EMU + LINK_PLUGIN(MT32) + #endif + LINK_PLUGIN(ADLIB) + LINK_PLUGIN(TOWNS) + #if defined (UNIX) + LINK_PLUGIN(TIMIDITY) + #endif + return pl; } }; @@ -167,30 +197,34 @@ PluginList FilePluginProvider::getPlugins() { PluginList pl; // Prepare the list of directories to search - Common::StringList pluginDirs; - // TODO: Add the user specified directory (via config file) - pluginDirs.push_back("."); - pluginDirs.push_back("plugins"); + FSList pluginDirs; + + // Add the default directories + pluginDirs.push_back(FilesystemNode(".")); + pluginDirs.push_back(FilesystemNode("plugins")); // Add the provider's custom directories addCustomDirectories(pluginDirs); - Common::StringList::const_iterator d; - for (d = pluginDirs.begin(); d != pluginDirs.end(); d++) { + // Add the user specified directory + Common::String pluginsPath(ConfMan.get("pluginspath")); + if (!pluginsPath.empty()) + pluginDirs.push_back(FilesystemNode(pluginsPath)); + + FSList::const_iterator dir; + for (dir = pluginDirs.begin(); dir != pluginDirs.end(); dir++) { // Load all plugins. // Scan for all plugins in this directory - FilesystemNode dir(*d); FSList files; - if (!dir.getChildren(files, FilesystemNode::kListFilesOnly)) { - debug(1, "Couldn't open plugin directory '%s'", d->c_str()); + if (!dir->getChildren(files, FilesystemNode::kListFilesOnly)) { + debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str()); continue; } else { - debug(1, "Reading plugins from plugin directory '%s'", d->c_str()); + debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str()); } for (FSList::const_iterator i = files.begin(); i != files.end(); ++i) { - Common::String name(i->getName()); - if (name.hasPrefix(getPrefix()) && name.hasSuffix(getSuffix())) { + if (isPluginFilename(i->getName())) { pl.push_back(createPlugin(i->getPath())); } } @@ -199,25 +233,25 @@ PluginList FilePluginProvider::getPlugins() { return pl; } -const char* FilePluginProvider::getPrefix() const { +bool FilePluginProvider::isPluginFilename(const Common::String &filename) const { #ifdef PLUGIN_PREFIX - return PLUGIN_PREFIX; -#else - return ""; + // Check the plugin prefix + if (!filename.hasPrefix(PLUGIN_PREFIX)) + return false; #endif -} -const char* FilePluginProvider::getSuffix() const { #ifdef PLUGIN_SUFFIX - return PLUGIN_SUFFIX; -#else - return ""; + // Check the plugin suffix + if (!filename.hasSuffix(PLUGIN_SUFFIX)) + return false; #endif + + return true; } -void FilePluginProvider::addCustomDirectories(Common::StringList &dirs) const { +void FilePluginProvider::addCustomDirectories(FSList &dirs) const { #ifdef PLUGIN_DIRECTORY - dirs.push_back(PLUGIN_DIRECTORY); + dirs.push_back(FilesystemNode(PLUGIN_DIRECTORY)); #endif } @@ -252,31 +286,30 @@ void PluginManager::loadPlugins() { for (ProviderList::iterator pp = _providers.begin(); pp != _providers.end(); ++pp) { - PluginList pl((**pp).getPlugins()); - for (PluginList::iterator plugin = pl.begin(); plugin != pl.end(); ++plugin) { - tryLoadPlugin(*plugin); - } + PluginList pl((*pp)->getPlugins()); + Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this)); } } void PluginManager::unloadPlugins() { - unloadPluginsExcept(NULL); + for (int i = 0; i < PLUGIN_TYPE_MAX; i++) + unloadPluginsExcept((PluginType)i, NULL); } -void PluginManager::unloadPluginsExcept(const Plugin *plugin) { +void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin) { Plugin *found = NULL; - for (PluginList::iterator p = _plugins.begin(); p != _plugins.end(); ++p) { + for (PluginList::iterator p = _plugins[type].begin(); p != _plugins[type].end(); ++p) { if (*p == plugin) { found = *p; } else { - (**p).unloadPlugin(); + (*p)->unloadPlugin(); delete *p; } } - _plugins.clear(); + _plugins[type].clear(); if (found != NULL) { - _plugins.push_back(found); + _plugins[type].push_back(found); } } @@ -284,16 +317,26 @@ bool PluginManager::tryLoadPlugin(Plugin *plugin) { assert(plugin); // Try to load the plugin if (plugin->loadPlugin()) { - // If successful, add it to the list of known plugins and return. - _plugins.push_back(plugin); + // 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++; + } - // TODO/FIXME: We should perform some additional checks here: - // * Check for some kind of "API version" (possibly derived from the - // SVN tree revision?) - // * If two plugins provide the same engine, we should only load one. - // To detect this situation, we could just compare the plugin name. - // To handle it, simply prefer modules loaded earlier to those coming. - // Or vice versa... to be determined... :-) + 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 { @@ -303,15 +346,59 @@ bool PluginManager::tryLoadPlugin(Plugin *plugin) { } } -GameList PluginManager::detectGames(const FSList &fslist) const { + +// 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 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. - PluginList::const_iterator iter; - for (iter = _plugins.begin(); iter != _plugins.end(); ++iter) { - candidates.push_back((*iter)->detectGames(fslist)); + 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); +} + + +// Music plugins + +#include "sound/musicplugin.h" + +DECLARE_SINGLETON(MusicManager); + +const MusicPlugin::List &MusicManager::getPlugins() const { + return (const MusicPlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC); +} diff --git a/base/plugins.h b/base/plugins.h index ca08a0c628..9d3ce97c3b 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -26,98 +26,82 @@ #ifndef BASE_PLUGINS_H #define BASE_PLUGINS_H -#include "common/array.h" #include "common/error.h" -#include "common/list.h" #include "common/singleton.h" #include "common/util.h" -#include "base/game.h" + +#ifdef DYNAMIC_MODULES +#include "common/fs.h" +#endif /** - * 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. + * @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 */ -class PluginObject { -public: - virtual ~PluginObject() {} - /** Returns the name of the plugin. */ - virtual const char *getName() const = 0; -}; -#include "engines/metaengine.h" -// Global Plugin API version +// Plugin versioning + +/** Global Plugin API version */ #define PLUGIN_VERSION 1 enum PluginType { PLUGIN_TYPE_ENGINE = 0, + PLUGIN_TYPE_MUSIC, + /* PLUGIN_TYPE_SCALER, */ // TODO: Add graphics scaler plugins PLUGIN_TYPE_MAX }; // TODO: Make the engine API version depend on ScummVM's version -// because of the backlinking +// because of the backlinking (posibly from the SVN revision) #define PLUGIN_TYPE_ENGINE_VERSION 1 +#define PLUGIN_TYPE_MUSIC_VERSION 1 extern int pluginTypeVersions[PLUGIN_TYPE_MAX]; -class Engine; -class FSList; -class OSystem; - -/** - * Abstract base class for the plugin system. - * Subclasses for this can be used to wrap both static and dynamic - * plugins. - */ -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; - const char *getCopyright() const; - - PluginError createInstance(OSystem *syst, Engine **engine) const; - GameList getSupportedGames() const; - GameDescriptor findGame(const char *gameid) const; - GameList detectGames(const FSList &fslist) const; - SaveStateList listSaves(const char *target) const; -}; +// Plugin linking #define STATIC_PLUGIN 1 #define DYNAMIC_PLUGIN 2 +// Note: The spaces around ENABLE_##ID have been added on purpose for +// MSVC. For some reason, MSVC tries to add the parenthesis after +// ENABLE_##ID to the check, thus making it false all the time. +// Please do NOT remove them, otherwise no engine plugins will be +// registered under MSVC + #define PLUGIN_ENABLED_STATIC(ID) \ - (defined(ENABLE_##ID) && !PLUGIN_ENABLED_DYNAMIC(ID)) + (defined( ENABLE_##ID ) && !PLUGIN_ENABLED_DYNAMIC(ID)) #define PLUGIN_ENABLED_DYNAMIC(ID) \ - (defined(ENABLE_##ID) && (ENABLE_##ID == DYNAMIC_PLUGIN) && defined(DYNAMIC_MODULES)) + (defined( ENABLE_##ID ) && (ENABLE_##ID == DYNAMIC_PLUGIN) && defined(DYNAMIC_MODULES)) /** - * REGISTER_PLUGIN is a convenience macro meant to ease writing - * the plugin interface for our modules. In particular, using it - * makes it possible to compile the very same code in a module - * both as a static and a dynamic plugin. + * 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. * - * @todo add some means to query the plugin API version etc. + * @see REGISTER_PLUGIN_DYNAMIC */ - #define REGISTER_PLUGIN_STATIC(ID,TYPE,PLUGINCLASS) \ PluginType g_##ID##_type = TYPE; \ PluginObject *g_##ID##_getObject() { \ @@ -127,6 +111,15 @@ public: #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; } \ @@ -141,44 +134,149 @@ public: #endif // DYNAMIC_MODULES -/** List of plugins. */ +// 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 methods). + * 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: - virtual Plugin* createPlugin(const Common::String &filename) const = 0; + /** + * 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 filename the name of the loadable code module + * @return a pointer to a Plugin instance, or 0 if an error occurred. + */ + virtual Plugin *createPlugin(const Common::String &filename) const = 0; - virtual const char* getPrefix() const; - virtual const char* getSuffix() const; + /** + * Check if the supplied filename corresponds to a loadable plugin file in + * the current platform. + * + * @param filename the name of the file to check + * @return true if the filename corresponds to a plugin, false otherwise + */ + virtual bool isPluginFilename(const Common::String &filename) const; - virtual void addCustomDirectories(Common::StringList &dirs) 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(FSList &dirs) const; }; +#endif // DYNAMIC_MODULES + /** - * Instances of this class manage all plugins, including loading them, - * making wrapper objects of class Plugin available, and unloading them. + * 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::List<PluginProvider *> ProviderList; + typedef Common::Array<PluginProvider *> ProviderList; private: - PluginList _plugins; + PluginList _plugins[PLUGIN_TYPE_MAX]; ProviderList _providers; bool tryLoadPlugin(Plugin *plugin); @@ -193,11 +291,9 @@ public: void loadPlugins(); void unloadPlugins(); - void unloadPluginsExcept(const Plugin *plugin); - - const PluginList &getPlugins() { return _plugins; } + void unloadPluginsExcept(PluginType type, const Plugin *plugin); - GameList detectGames(const FSList &fslist) const; + const PluginList &getPlugins(PluginType t) { return _plugins[t]; } }; #endif |