diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/plugins.cpp | 170 | ||||
-rw-r--r-- | base/plugins.h | 29 |
2 files changed, 182 insertions, 17 deletions
diff --git a/base/plugins.cpp b/base/plugins.cpp index f3c16ac845..7887b2663a 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -23,8 +23,19 @@ #include "base/gameDetector.h" #include "base/plugins.h" #include "base/engine.h" +#include "common/util.h" +#ifdef DYNAMIC_MODULES + +#ifdef UNIX +#include <dlfcn.h> +#else +#error No support for loading plugins on non-unix systems at this point! +#endif + +#else + // Factory functions => no need to include the specific classes // in this header. This serves two purposes: // 1) Clean seperation from the game modules (scumm, simon) and the generic code @@ -49,6 +60,8 @@ extern const TargetSettings *Engine_SWORD2_targetList(); extern Engine *Engine_SWORD2_create(GameDetector *detector, OSystem *syst); #endif +#endif + #pragma mark - @@ -96,7 +109,6 @@ public: } const char *getName() const { return _name; } - int getVersion() const { return 0; } int countTargets() const { return _targetCount; } const TargetSettings *getTargets() const { return _targets; } @@ -110,6 +122,98 @@ public: #pragma mark - +#ifdef DYNAMIC_MODULES + +class DynamicPlugin : public Plugin { + void *_dlHandle; + ScummVM::String _filename; + + ScummVM::String _name; + const TargetSettings *_targets; + int _targetCount; + EngineFactory _ef; + + void *findSymbol(const char *symbol); + +public: + DynamicPlugin(const char *filename) + : _dlHandle(0), _filename(filename), _targets(0), _targetCount(0), _ef(0) {} + + const char *getName() const { return _name.c_str(); } + + int countTargets() const { return _targetCount; } + const TargetSettings *getTargets() const { return _targets; } + + Engine *createInstance(GameDetector *detector, OSystem *syst) const { + assert(_ef); + return (*_ef)(detector, syst); + } + + bool loadPlugin(); + void unloadPlugin(); +}; + +void *DynamicPlugin::findSymbol(const char *symbol) { +#ifdef UNIX + void *func = dlsym(_dlHandle, symbol); + if (!func) + warning("Failed loading symbold '%s' from plugin '%s' (%s)\n", symbol, _filename.c_str(), dlerror()); + return func; +#else +#error TODO +#endif +} + +typedef const char *(*NameFunc)(); +typedef const TargetSettings *(*TargetListFunc)(); + +bool DynamicPlugin::loadPlugin() { + assert(!_dlHandle); + _dlHandle = dlopen(_filename.c_str(), RTLD_LAZY); + + if (!_dlHandle) { + warning("Failed loading plugin '%s' (%s)\n", _filename.c_str(), dlerror()); + return false; + } + + // Query the plugin's name + NameFunc nameFunc = (NameFunc)findSymbol("PLUGIN_name"); + if (!nameFunc) { + unloadPlugin(); + return false; + } + _name = nameFunc(); + + // Query the plugin for the targets it supports + TargetListFunc targetListFunc = (TargetListFunc)findSymbol("PLUGIN_getTargetList"); + if (!targetListFunc) { + unloadPlugin(); + return false; + } + _targets = targetListFunc(); + + // Finally, retrieve the factory function + _ef = (EngineFactory)findSymbol("PLUGIN_createEngine"); + if (!_ef) { + unloadPlugin(); + return false; + } + + return true; +} + +void DynamicPlugin::unloadPlugin() { + if (_dlHandle) { + if (dlclose(_dlHandle) != 0) + warning("Failed unloading plugin '%s' (%s)\n", _filename.c_str(), dlerror()); + } +} + +#endif // DYNAMIC_MODULES + +#pragma mark - + + PluginManager::PluginManager() { } @@ -119,20 +223,41 @@ PluginManager::~PluginManager() { } void PluginManager::loadPlugins() { -#ifndef DISABLE_SCUMM - _plugins.push_back(new StaticPlugin("scumm", Engine_SCUMM_targetList(), Engine_SCUMM_create)); -#endif - -#ifndef DISABLE_SIMON - _plugins.push_back(new StaticPlugin("simon", Engine_SIMON_targetList(), Engine_SIMON_create)); -#endif - -#ifndef DISABLE_SKY - _plugins.push_back(new StaticPlugin("sky", Engine_SKY_targetList(), Engine_SKY_create)); -#endif - -#ifndef DISABLE_SWORD2 - _plugins.push_back(new StaticPlugin("sword2", Engine_SWORD2_targetList(), Engine_SWORD2_create)); +#ifndef DYNAMIC_MODULES + // "Load" the static plugins + #ifndef DISABLE_SCUMM + tryLoadPlugin(new StaticPlugin("scumm", Engine_SCUMM_targetList(), Engine_SCUMM_create)); + #endif + + #ifndef DISABLE_SIMON + tryLoadPlugin(new StaticPlugin("simon", Engine_SIMON_targetList(), Engine_SIMON_create)); + #endif + + #ifndef DISABLE_SKY + tryLoadPlugin(new StaticPlugin("sky", Engine_SKY_targetList(), Engine_SKY_create)); + #endif + + #ifndef DISABLE_SWORD2 + tryLoadPlugin(new StaticPlugin("sword2", Engine_SWORD2_targetList(), Engine_SWORD2_create)); + #endif +#else + // Load dynamic plugins + // TODO... this is right now just a nasty hack. + #ifndef DISABLE_SCUMM + tryLoadPlugin(new DynamicPlugin("scumm/libscumm.so")); + #endif + + #ifndef DISABLE_SIMON + tryLoadPlugin(new DynamicPlugin("simon/libsimon.so")); + #endif + + #ifndef DISABLE_SKY + tryLoadPlugin(new DynamicPlugin("sky/libsky.so")); + #endif + + #ifndef DISABLE_SWORD2 + tryLoadPlugin(new DynamicPlugin("bs2/libbs2.so")); + #endif #endif } @@ -144,3 +269,18 @@ void PluginManager::unloadPlugins() { } _plugins.clear(); } + +bool PluginManager::tryLoadPlugin(Plugin *plugin) { + assert(plugin); + // Try to load the plugin + if (plugin->loadPlugin()) { + // If succesful, add it to the list of known plugins and return. + _plugins.push_back(plugin); + return true; + } else { + // Failed to load the plugin + delete plugin; + return false; + } +} + diff --git a/base/plugins.h b/base/plugins.h index 8257bf1a53..d79ff0c5d9 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -37,11 +37,13 @@ struct TargetSettings; */ class Plugin { public: - virtual void loadPlugin() {} + virtual ~Plugin() {} + + virtual bool loadPlugin() { return true; } virtual void unloadPlugin() {} virtual const char *getName() const = 0; - virtual int getVersion() const = 0; + virtual int getVersion() const { return 0; } // TODO! virtual int countTargets() const; virtual const TargetSettings *getTargets() const = 0; @@ -51,6 +53,27 @@ public: }; +/** + * The 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. + * + * @todo add some means to query the plugin API version etc. + * @todo on Windows, we might need __declspec(dllexport) ? + */ +#ifndef DYNAMIC_MODULES +#define REGISTER_PLUGIN(name,targetListFactory,engineFactory) +#else +#define REGISTER_PLUGIN(name,targetListFactory,engineFactory) \ + extern "C" { \ + const char *PLUGIN_name() { return name; } \ + const TargetSettings *PLUGIN_getTargetList() { return targetListFactory(); } \ + Engine *PLUGIN_createEngine(GameDetector *detector, OSystem *syst) { return engineFactory(detector, syst); } \ + } +#endif + + /** List of plugins. */ typedef ScummVM::List<Plugin *> PluginList; @@ -65,6 +88,8 @@ class PluginManager { protected: PluginList _plugins; + bool tryLoadPlugin(Plugin *plugin); + public: PluginManager(); ~PluginManager(); |