aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Horn2003-09-18 18:23:53 +0000
committerMax Horn2003-09-18 18:23:53 +0000
commit6a4663824e573777c512e0ca21a5c5f61865b83c (patch)
tree78061db44cc3d3731decd51a84865225dd53840f
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
-rw-r--r--Makefile.common8
-rw-r--r--base/plugins.cpp170
-rw-r--r--base/plugins.h29
-rw-r--r--scumm/scummvm.cpp6
-rw-r--r--simon/simon.cpp22
-rw-r--r--sky/sky.cpp11
-rw-r--r--sword2/sword2.cpp52
7 files changed, 244 insertions, 54 deletions
diff --git a/Makefile.common b/Makefile.common
index 5da0612341..2fef3f31f0 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -41,10 +41,10 @@ PLUGIN_PREFIX := lib
PLUGIN_SUFFIX := .so
ifdef BUILD_PLUGINS
-# FIXME/TODO: The following is OS X specific (and the '-m' is an evil hack
-# to work around the conflict between our operators new/delete and the
-# ones provided by libstdc++.a)
-LDFLAGS += -all_load -m
+# FIXME/TODO: The following is OS X specific
+LDFLAGS += -all_load
+CXXFLAGS += -DDYNAMIC_MODULES
+LIBS += -ldl
endif
######################################################################
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();
diff --git a/scumm/scummvm.cpp b/scumm/scummvm.cpp
index 1f5f2ab1a3..495b7e671b 100644
--- a/scumm/scummvm.cpp
+++ b/scumm/scummvm.cpp
@@ -22,8 +22,10 @@
#include "stdafx.h"
-#include "common/config-file.h"
#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-file.h"
#include "gui/console.h"
#include "gui/message.h"
@@ -294,6 +296,8 @@ Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
return engine;
}
+REGISTER_PLUGIN("Scumm Engine", Engine_SCUMM_targetList, Engine_SCUMM_create);
+
Scumm::Scumm (GameDetector *detector, OSystem *syst)
: Engine(detector, syst), _pauseDialog(0), _optionsDialog(0), _saveLoadDialog(0) {
OSystem::Property prop;
diff --git a/simon/simon.cpp b/simon/simon.cpp
index 3025112718..959cbfd9af 100644
--- a/simon/simon.cpp
+++ b/simon/simon.cpp
@@ -20,13 +20,19 @@
*/
#include "stdafx.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-file.h"
+#include "common/file.h"
+
#include "simon/simon.h"
#include "simon/intern.h"
#include "simon/vga.h"
+
#include "sound/mididrv.h"
-#include "common/config-file.h"
-#include "common/file.h"
-#include "base/gameDetector.h"
+
#include <errno.h>
#include <time.h>
@@ -59,6 +65,12 @@ const TargetSettings *Engine_SIMON_targetList() {
return simon_settings;
}
+Engine *Engine_SIMON_create(GameDetector *detector, OSystem *syst) {
+ return new SimonEngine(detector, syst);
+}
+
+REGISTER_PLUGIN("Simon the Sorcerer", Engine_SIMON_targetList, Engine_SIMON_create);
+
static const GameSpecificSettings simon1_settings = {
1, // VGA_DELAY_BASE
1576 / 4, // TABLE_INDEX_BASE
@@ -168,10 +180,6 @@ static const GameSpecificSettings simon2dos_settings = {
};
-Engine *Engine_SIMON_create(GameDetector *detector, OSystem *syst) {
- return new SimonEngine(detector, syst);
-}
-
SimonEngine::SimonEngine(GameDetector *detector, OSystem *syst)
: Engine(detector, syst), midi (syst) {
OSystem::Property prop;
diff --git a/sky/sky.cpp b/sky/sky.cpp
index c2c0708d23..84d21b495b 100644
--- a/sky/sky.cpp
+++ b/sky/sky.cpp
@@ -20,14 +20,19 @@
*/
#include "stdafx.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/file.h"
+
#include "sky/sky.h"
#include "sky/skydefs.h" //game specific defines
#include "sky/compact.h"
#include "sky/logic.h"
#include "sky/debug.h"
#include "sky/mouse.h"
-#include "base/gameDetector.h"
-#include "common/file.h"
+
#include <errno.h>
#include <time.h>
@@ -73,6 +78,8 @@ Engine *Engine_SKY_create(GameDetector *detector, OSystem *syst) {
return new SkyState(detector, syst);
}
+REGISTER_PLUGIN("Beneath a Steel Sky", Engine_SKY_targetList, Engine_SKY_create);
+
void **SkyState::_itemList[300];
SystemVars SkyState::_systemVars = {0, 0, 0, 0, 4316, 0, 0, false, false };
diff --git a/sword2/sword2.cpp b/sword2/sword2.cpp
index 0ba862dcc8..d0dd408796 100644
--- a/sword2/sword2.cpp
+++ b/sword2/sword2.cpp
@@ -19,31 +19,35 @@
//------------------------------------------------------------------------------------
#include "stdafx.h"
-#include "driver/driver96.h"
-#include "driver/palette.h"
+
#include "base/gameDetector.h"
+#include "base/plugins.h"
+
#include "common/config-file.h"
-#include "build_display.h"
-#include "console.h"
-#include "controls.h"
-#include "debug.h"
-#include "events.h"
-#include "header.h"
-#include "interpreter.h"
-#include "layers.h"
-#include "logic.h"
-#include "maketext.h"
-#include "memory.h"
-#include "mouse.h"
-#include "protocol.h"
-#include "resman.h"
-#include "scroll.h"
-#include "sound.h"
-#include "speech.h"
-#include "startup.h"
-#include "sword2.h"
-#include "sync.h"
-#include "save_rest.h"
+
+#include "bs2/build_display.h"
+#include "bs2/console.h"
+#include "bs2/controls.h"
+#include "bs2/debug.h"
+#include "bs2/events.h"
+#include "bs2/header.h"
+#include "bs2/interpreter.h"
+#include "bs2/layers.h"
+#include "bs2/logic.h"
+#include "bs2/maketext.h"
+#include "bs2/memory.h"
+#include "bs2/mouse.h"
+#include "bs2/protocol.h"
+#include "bs2/resman.h"
+#include "bs2/save_rest.h"
+#include "bs2/scroll.h"
+#include "bs2/sound.h"
+#include "bs2/speech.h"
+#include "bs2/startup.h"
+#include "bs2/sword2.h"
+#include "bs2/sync.h"
+#include "bs2/driver/driver96.h"
+#include "bs2/driver/palette.h"
#define MAX_PATH 260
@@ -97,6 +101,8 @@ Engine *Engine_SWORD2_create(GameDetector *detector, OSystem *syst) {
return new Sword2State(detector, syst);
}
+REGISTER_PLUGIN("Broken Sword II", Engine_SWORD2_targetList, Engine_SWORD2_create);
+
Sword2State::Sword2State(GameDetector *detector, OSystem *syst)
: Engine(detector, syst) {