From 6a4663824e573777c512e0ca21a5c5f61865b83c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 18 Sep 2003 18:23:53 +0000 Subject: 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 --- Makefile.common | 8 +-- base/plugins.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++++++----- base/plugins.h | 29 +++++++++- scumm/scummvm.cpp | 6 +- simon/simon.cpp | 22 ++++--- sky/sky.cpp | 11 +++- sword2/sword2.cpp | 52 +++++++++-------- 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 +#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 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 #include @@ -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 #include @@ -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) { -- cgit v1.2.3