aboutsummaryrefslogtreecommitdiff
path: root/base
diff options
context:
space:
mode:
authorMax Horn2003-09-18 18:23:53 +0000
committerMax Horn2003-09-18 18:23:53 +0000
commit6a4663824e573777c512e0ca21a5c5f61865b83c (patch)
tree78061db44cc3d3731decd51a84865225dd53840f /base
parent209413ed07e931277cf569d260fbfad71406088e (diff)
downloadscummvm-rg350-6a4663824e573777c512e0ca21a5c5f61865b83c.tar.gz
scummvm-rg350-6a4663824e573777c512e0ca21a5c5f61865b83c.tar.bz2
scummvm-rg350-6a4663824e573777c512e0ca21a5c5f61865b83c.zip
added initial support for building our 4 adventure engines as loadable modules; right now only work on OS X; once we add more build rules, other systems with dlopen() should work, too (e.g. Linux); Windows support may come later. This is still very much WIP
svn-id: r10304
Diffstat (limited to 'base')
-rw-r--r--base/plugins.cpp170
-rw-r--r--base/plugins.h29
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();