ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

plugins.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "base/plugins.h"
00024 
00025 #include "common/translation.h"
00026 #include "common/func.h"
00027 #include "common/debug.h"
00028 #include "common/config-manager.h"
00029 
00030 #ifdef DYNAMIC_MODULES
00031 #include "common/fs.h"
00032 #endif
00033 
00034 // Plugin versioning
00035 
00036 int pluginTypeVersions[PLUGIN_TYPE_MAX] = {
00037     PLUGIN_TYPE_ENGINE_VERSION,
00038     PLUGIN_TYPE_MUSIC_VERSION,
00039 };
00040 
00041 
00042 // Abstract plugins
00043 
00044 PluginType Plugin::getType() const {
00045     return _type;
00046 }
00047 
00048 const char *Plugin::getName() const {
00049     return _pluginObject->getName();
00050 }
00051 
00052 class StaticPlugin : public Plugin {
00053 public:
00054     StaticPlugin(PluginObject *pluginobject, PluginType type) {
00055         assert(pluginobject);
00056         assert(type < PLUGIN_TYPE_MAX);
00057         _pluginObject = pluginobject;
00058         _type = type;
00059     }
00060 
00061     ~StaticPlugin() {
00062         delete _pluginObject;
00063     }
00064 
00065     virtual bool loadPlugin()       { return true; }
00066     virtual void unloadPlugin()     {}
00067 };
00068 
00069 class StaticPluginProvider : public PluginProvider {
00070 public:
00071     StaticPluginProvider() {
00072     }
00073 
00074     ~StaticPluginProvider() {
00075     }
00076 
00077     virtual PluginList getPlugins() {
00078         PluginList pl;
00079 
00080         #define LINK_PLUGIN(ID) \
00081             extern PluginType g_##ID##_type; \
00082             extern PluginObject *g_##ID##_getObject(); \
00083             pl.push_back(new StaticPlugin(g_##ID##_getObject(), g_##ID##_type));
00084 
00085         // "Loader" for the static plugins.
00086         // Iterate over all registered (static) plugins and load them.
00087 
00088         // Engine plugins
00089         #include "engines/plugins_table.h"
00090 
00091         // Music plugins
00092         // TODO: Use defines to disable or enable each MIDI driver as a
00093         // static/dynamic plugin, like it's done for the engines
00094         LINK_PLUGIN(AUTO)
00095         LINK_PLUGIN(NULL)
00096         #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
00097         LINK_PLUGIN(WINDOWS)
00098         #endif
00099         #if defined(USE_ALSA)
00100         LINK_PLUGIN(ALSA)
00101         #endif
00102         #if defined(USE_SEQ_MIDI)
00103         LINK_PLUGIN(SEQ)
00104         #endif
00105         #if defined(USE_SNDIO)
00106         LINK_PLUGIN(SNDIO)
00107         #endif
00108         #if defined(__MINT__)
00109         LINK_PLUGIN(STMIDI)
00110         #endif
00111         #if defined(IRIX)
00112         LINK_PLUGIN(DMEDIA)
00113         #endif
00114         #if defined(__amigaos4__)
00115         LINK_PLUGIN(CAMD)
00116         #endif
00117         #if defined(MACOSX)
00118         LINK_PLUGIN(COREAUDIO)
00119         LINK_PLUGIN(COREMIDI)
00120         #endif
00121         #ifdef USE_FLUIDSYNTH
00122         LINK_PLUGIN(FLUIDSYNTH)
00123         #endif
00124         #ifdef USE_MT32EMU
00125         LINK_PLUGIN(MT32)
00126         #endif
00127         LINK_PLUGIN(ADLIB)
00128 //ResidualVM: disabled belows:
00129 //      LINK_PLUGIN(PCSPK)
00130 //      LINK_PLUGIN(PCJR)
00131 //      LINK_PLUGIN(CMS)
00132         #if defined(__ANDROID__)
00133 //      LINK_PLUGIN(EAS)
00134         #endif
00135         #ifndef DISABLE_SID
00136 //      LINK_PLUGIN(C64)
00137         #endif
00138 //      LINK_PLUGIN(AMIGA)
00139 //      LINK_PLUGIN(APPLEIIGS)
00140 //      LINK_PLUGIN(TOWNS)
00141 //      LINK_PLUGIN(PC98)
00142         #if defined(USE_TIMIDITY)
00143         LINK_PLUGIN(TIMIDITY)
00144         #endif
00145 
00146         return pl;
00147     }
00148 };
00149 
00150 #ifdef DYNAMIC_MODULES
00151 
00152 PluginList FilePluginProvider::getPlugins() {
00153     PluginList pl;
00154 
00155     // Prepare the list of directories to search
00156     Common::FSList pluginDirs;
00157 
00158     // Add the default directories
00159     pluginDirs.push_back(Common::FSNode("."));
00160     pluginDirs.push_back(Common::FSNode("plugins"));
00161 
00162     // Add the provider's custom directories
00163     addCustomDirectories(pluginDirs);
00164 
00165     // Add the user specified directory
00166     Common::String pluginsPath(ConfMan.get("pluginspath"));
00167     if (!pluginsPath.empty())
00168         pluginDirs.push_back(Common::FSNode(pluginsPath));
00169 
00170     Common::FSList::const_iterator dir;
00171     for (dir = pluginDirs.begin(); dir != pluginDirs.end(); ++dir) {
00172         // Load all plugins.
00173         // Scan for all plugins in this directory
00174         Common::FSList files;
00175         if (!dir->getChildren(files, Common::FSNode::kListFilesOnly)) {
00176             debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str());
00177             continue;
00178         } else {
00179             debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str());
00180         }
00181 
00182         for (Common::FSList::const_iterator i = files.begin(); i != files.end(); ++i) {
00183             if (isPluginFilename(*i)) {
00184                 pl.push_back(createPlugin(*i));
00185             }
00186         }
00187     }
00188 
00189     return pl;
00190 }
00191 
00192 bool FilePluginProvider::isPluginFilename(const Common::FSNode &node) const {
00193     Common::String filename = node.getName();
00194 
00195 #ifdef PLUGIN_PREFIX
00196     // Check the plugin prefix
00197     if (!filename.hasPrefix(PLUGIN_PREFIX))
00198         return false;
00199 #endif
00200 
00201 #ifdef PLUGIN_SUFFIX
00202     // Check the plugin suffix
00203     if (!filename.hasSuffix(PLUGIN_SUFFIX))
00204         return false;
00205 #endif
00206 
00207     return true;
00208 }
00209 
00210 void FilePluginProvider::addCustomDirectories(Common::FSList &dirs) const {
00211 #ifdef PLUGIN_DIRECTORY
00212     dirs.push_back(Common::FSNode(PLUGIN_DIRECTORY));
00213 #endif
00214 }
00215 
00216 #endif // DYNAMIC_MODULES
00217 
00218 #pragma mark -
00219 
00220 PluginManager *PluginManager::_instance = NULL;
00221 
00222 PluginManager &PluginManager::instance() {
00223     if (_instance)
00224         return *_instance;
00225 
00226 #if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES)
00227         _instance = new PluginManagerUncached();
00228 #else
00229         _instance = new PluginManager();
00230 #endif
00231     return *_instance;
00232 }
00233 
00234 PluginManager::PluginManager() {
00235     // Always add the static plugin provider.
00236     addPluginProvider(new StaticPluginProvider());
00237 }
00238 
00239 PluginManager::~PluginManager() {
00240     // Explicitly unload all loaded plugins
00241     unloadAllPlugins();
00242 
00243     // Delete the plugin providers
00244     for (ProviderList::iterator pp = _providers.begin();
00245                                 pp != _providers.end();
00246                                 ++pp) {
00247         delete *pp;
00248     }
00249 }
00250 
00251 void PluginManager::addPluginProvider(PluginProvider *pp) {
00252     _providers.push_back(pp);
00253 }
00254 
00258 void PluginManagerUncached::init() {
00259     unloadAllPlugins();
00260     _allEnginePlugins.clear();
00261 
00262     unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // empty the engine plugins
00263 
00264     for (ProviderList::iterator pp = _providers.begin();
00265                                 pp != _providers.end();
00266                                 ++pp) {
00267         PluginList pl((*pp)->getPlugins());
00268 
00269         for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) {
00270             // This is a 'hack' based on the assumption that we have no sound
00271             // file plugins. Currently this is the case. If it changes, we
00272             // should find a fast way of detecting whether a plugin is a
00273             // music or an engine plugin.
00274             if ((*pp)->isFilePluginProvider()) {
00275                 _allEnginePlugins.push_back(*p);
00276             } else if ((*p)->loadPlugin()) { // and this is the proper method
00277                 if ((*p)->getType() == PLUGIN_TYPE_ENGINE) {
00278                     (*p)->unloadPlugin();
00279                     _allEnginePlugins.push_back(*p);
00280                 } else {    // add non-engine plugins to the 'in-memory' list
00281                             // these won't ever get unloaded
00282                     addToPluginsInMemList(*p);
00283                 }
00284             }
00285         }
00286     }
00287 }
00288 
00293 bool PluginManagerUncached::loadPluginFromGameId(const Common::String &gameId) {
00294     Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
00295 
00296     if (domain) {
00297         if (domain->contains(gameId)) {
00298             Common::String filename = (*domain)[gameId];
00299 
00300             if (loadPluginByFileName(filename)) {
00301                 return true;
00302             }
00303         }
00304     }
00305     return false;
00306 }
00307 
00311 bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) {
00312     if (filename.empty())
00313         return false;
00314 
00315     unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
00316 
00317     PluginList::iterator i;
00318     for (i = _allEnginePlugins.begin(); i != _allEnginePlugins.end(); ++i) {
00319         if (Common::String((*i)->getFileName()) == filename && (*i)->loadPlugin()) {
00320             addToPluginsInMemList(*i);
00321             _currentPlugin = i;
00322             return true;
00323         }
00324     }
00325     return false;
00326 }
00327 
00332 void PluginManagerUncached::updateConfigWithFileName(const Common::String &gameId) {
00333     // Check if we have a filename for the current plugin
00334     if ((*_currentPlugin)->getFileName()) {
00335         if (!ConfMan.hasMiscDomain("plugin_files"))
00336             ConfMan.addMiscDomain("plugin_files");
00337 
00338         Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
00339         assert(domain);
00340         (*domain)[gameId] = (*_currentPlugin)->getFileName();
00341 
00342         ConfMan.flushToDisk();
00343     }
00344 }
00345 
00346 void PluginManagerUncached::loadFirstPlugin() {
00347     unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
00348 
00349     // let's try to find one we can load
00350     for (_currentPlugin = _allEnginePlugins.begin(); _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
00351         if ((*_currentPlugin)->loadPlugin()) {
00352             addToPluginsInMemList(*_currentPlugin);
00353             break;
00354         }
00355     }
00356 }
00357 
00358 bool PluginManagerUncached::loadNextPlugin() {
00359     unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
00360 
00361     for (++_currentPlugin; _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
00362         if ((*_currentPlugin)->loadPlugin()) {
00363             addToPluginsInMemList(*_currentPlugin);
00364             return true;
00365         }
00366     }
00367     return false; // no more in list
00368 }
00369 
00374 void PluginManager::loadAllPlugins() {
00375     for (ProviderList::iterator pp = _providers.begin();
00376                                 pp != _providers.end();
00377                                 ++pp) {
00378         PluginList pl((*pp)->getPlugins());
00379         Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
00380     }
00381 }
00382 
00383 void PluginManager::unloadAllPlugins() {
00384     for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
00385         unloadPluginsExcept((PluginType)i, NULL);
00386 }
00387 
00388 void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin, bool deletePlugin /*=true*/) {
00389     Plugin *found = NULL;
00390     for (PluginList::iterator p = _pluginsInMem[type].begin(); p != _pluginsInMem[type].end(); ++p) {
00391         if (*p == plugin) {
00392             found = *p;
00393         } else {
00394             (*p)->unloadPlugin();
00395             if (deletePlugin)
00396                 delete *p;
00397         }
00398     }
00399     _pluginsInMem[type].clear();
00400     if (found != NULL) {
00401         _pluginsInMem[type].push_back(found);
00402     }
00403 }
00404 
00405 /*
00406  * Used only by the cached plugin manager since it deletes the plugin.
00407  */
00408 bool PluginManager::tryLoadPlugin(Plugin *plugin) {
00409     assert(plugin);
00410     // Try to load the plugin
00411     if (plugin->loadPlugin()) {
00412         addToPluginsInMemList(plugin);
00413         return true;
00414     } else {
00415         // Failed to load the plugin
00416         delete plugin;
00417         return false;
00418     }
00419 }
00420 
00424 void PluginManager::addToPluginsInMemList(Plugin *plugin) {
00425     bool found = false;
00426     // The plugin is valid, see if it provides the same module as an
00427     // already loaded one and should replace it.
00428 
00429     PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin();
00430     while (!found && pl != _pluginsInMem[plugin->getType()].end()) {
00431         if (!strcmp(plugin->getName(), (*pl)->getName())) {
00432             // Found a duplicated module. Replace the old one.
00433             found = true;
00434             delete *pl;
00435             *pl = plugin;
00436             debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
00437         }
00438         pl++;
00439     }
00440 
00441     if (!found) {
00442         // If it provides a new module, just add it to the list of known plugins in memory.
00443         _pluginsInMem[plugin->getType()].push_back(plugin);
00444     }
00445 }
00446 
00447 // Engine plugins
00448 
00449 #include "engines/metaengine.h"
00450 
00451 namespace Common {
00452 DECLARE_SINGLETON(EngineManager);
00453 }
00454 
00462 PlainGameDescriptor EngineManager::findGame(const Common::String &gameName, const Plugin **plugin) const {
00463     // First look for the game using the plugins in memory. This is critical
00464     // for calls coming from inside games
00465     PlainGameDescriptor result = findGameInLoadedPlugins(gameName, plugin);
00466     if (result.gameId) {
00467         return result;
00468     }
00469 
00470     // Now look for the game using the gameId. This is much faster than scanning plugin
00471     // by plugin
00472     if (PluginMan.loadPluginFromGameId(gameName))  {
00473         result = findGameInLoadedPlugins(gameName, plugin);
00474         if (result.gameId) {
00475             return result;
00476         }
00477     }
00478 
00479     // We failed to find it using the gameid. Scan the list of plugins
00480     PluginMan.loadFirstPlugin();
00481     do {
00482         result = findGameInLoadedPlugins(gameName, plugin);
00483         if (result.gameId) {
00484             // Update with new plugin file name
00485             PluginMan.updateConfigWithFileName(gameName);
00486             break;
00487         }
00488     } while (PluginMan.loadNextPlugin());
00489 
00490     return result;
00491 }
00492 
00496 PlainGameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
00497     // Find the GameDescriptor for this target
00498     const PluginList &plugins = getPlugins();
00499 
00500     if (plugin)
00501         *plugin = 0;
00502 
00503     PluginList::const_iterator iter;
00504 
00505     for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
00506         PlainGameDescriptor pgd = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
00507         if (pgd.gameId) {
00508             if (plugin)
00509                 *plugin = *iter;
00510             return pgd;
00511         }
00512     }
00513 
00514     return PlainGameDescriptor::empty();
00515 }
00516 
00517 DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
00518     DetectedGames candidates;
00519     PluginList plugins;
00520     PluginList::const_iterator iter;
00521     PluginManager::instance().loadFirstPlugin();
00522     do {
00523         plugins = getPlugins();
00524         // Iterate over all known games and for each check if it might be
00525         // the game in the presented directory.
00526         for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
00527             const MetaEngine &metaEngine = (*iter)->get<MetaEngine>();
00528             DetectedGames engineCandidates = metaEngine.detectGames(fslist);
00529 
00530             for (uint i = 0; i < engineCandidates.size(); i++) {
00531                 engineCandidates[i].engineName = metaEngine.getName();
00532                 engineCandidates[i].path = fslist.begin()->getParent().getPath();
00533                 candidates.push_back(engineCandidates[i]);
00534             }
00535 
00536         }
00537     } while (PluginManager::instance().loadNextPlugin());
00538 
00539     return DetectionResults(candidates);
00540 }
00541 
00542 const PluginList &EngineManager::getPlugins() const {
00543     return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
00544 }
00545 
00546 namespace {
00547 
00548 void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
00549     if (!value.empty())
00550         ConfMan.set(key, value, domain);
00551 }
00552 
00553 } // End of anonymous namespace
00554 
00555 Common::String EngineManager::createTargetForGame(const DetectedGame &game) {
00556     // The auto detector or the user made a choice.
00557     // Pick a domain name which does not yet exist (after all, we
00558     // are *adding* a game to the config, not replacing).
00559     Common::String domain = game.preferredTarget;
00560 
00561     assert(!domain.empty());
00562     if (ConfMan.hasGameDomain(domain)) {
00563         int suffixN = 1;
00564         Common::String gameid(domain);
00565 
00566         while (ConfMan.hasGameDomain(domain)) {
00567             domain = gameid + Common::String::format("-%d", suffixN);
00568             suffixN++;
00569         }
00570     }
00571 
00572     // Add the name domain
00573     ConfMan.addGameDomain(domain);
00574 
00575     // Copy all non-empty relevant values into the new domain
00576     addStringToConf("gameid", game.gameId, domain);
00577     addStringToConf("description", game.description, domain);
00578     addStringToConf("language", Common::getLanguageCode(game.language), domain);
00579     addStringToConf("platform", Common::getPlatformCode(game.platform), domain);
00580     addStringToConf("path", game.path, domain);
00581     addStringToConf("extra", game.extra, domain);
00582     addStringToConf("guioptions", game.getGUIOptions(), domain);
00583 
00584     // Add any extra configuration keys
00585     for (Common::StringMap::iterator i = game._extraConfigEntries.begin();
00586             i != game._extraConfigEntries.end(); ++i)
00587         addStringToConf((*i)._key, (*i)._value, domain);
00588 
00589     // TODO: Setting the description field here has the drawback
00590     // that the user does never notice when we upgrade our descriptions.
00591     // It might be nice to leave this field empty, and only set it to
00592     // a value when the user edits the description string.
00593     // However, at this point, that's impractical. Once we have a method
00594     // to query all backends for the proper & full description of a given
00595     // game target, we can change this (currently, you can only query
00596     // for the generic gameid description; it's not possible to obtain
00597     // a description which contains extended information like language, etc.).
00598 
00599     return domain;
00600 }
00601 
00602 // Music plugins
00603 
00604 #include "audio/musicplugin.h"
00605 
00606 namespace Common {
00607 DECLARE_SINGLETON(MusicManager);
00608 }
00609 
00610 const PluginList &MusicManager::getPlugins() const {
00611     return PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
00612 }


Generated on Sat Feb 16 2019 05:01:01 for ResidualVM by doxygen 1.7.1
curved edge   curved edge