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 //ResidualVM: disabled belows:
00095 //      LINK_PLUGIN(AUTO)
00096 //      LINK_PLUGIN(NULL)
00097 //      #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
00098 //      LINK_PLUGIN(WINDOWS)
00099 //      #endif
00100 //      #if defined(USE_ALSA)
00101 //      LINK_PLUGIN(ALSA)
00102 //      #endif
00103 //      #if defined(USE_SEQ_MIDI)
00104 //      LINK_PLUGIN(SEQ)
00105 //      #endif
00106 //      #if defined(USE_SNDIO)
00107 //      LINK_PLUGIN(SNDIO)
00108 //      #endif
00109 //      #if defined(__MINT__)
00110 //      LINK_PLUGIN(STMIDI)
00111 //      #endif
00112 //      #if defined(IRIX)
00113 //      LINK_PLUGIN(DMEDIA)
00114 //      #endif
00115 //      #if defined(__amigaos4__)
00116 //      LINK_PLUGIN(CAMD)
00117 //      #endif
00118 //      #if defined(MACOSX)
00119 //      LINK_PLUGIN(COREAUDIO)
00120 //      LINK_PLUGIN(COREMIDI)
00121 //      #endif
00122 //      #ifdef USE_FLUIDSYNTH
00123 //      LINK_PLUGIN(FLUIDSYNTH)
00124 //      #endif
00125 //      #ifdef USE_MT32EMU
00126 //      LINK_PLUGIN(MT32)
00127 //      #endif
00128 //      LINK_PLUGIN(ADLIB)
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::loadAllPluginsOfType(PluginType type) {
00384     for (ProviderList::iterator pp = _providers.begin();
00385                                 pp != _providers.end();
00386                                 ++pp) {
00387         PluginList pl((*pp)->getPlugins());
00388         for (PluginList::iterator p = pl.begin();
00389                                   p != pl.end();
00390                                   ++p) {
00391             if ((*p)->loadPlugin()) {
00392                 if ((*p)->getType() == type) {
00393                     addToPluginsInMemList((*p));
00394                 } else {
00395                     // Plugin is wrong type
00396                     (*p)->unloadPlugin();
00397                     delete (*p);
00398                 }
00399             } else {
00400                 // Plugin did not load
00401                 delete (*p);
00402             }
00403         }
00404     }
00405 }
00406 
00407 void PluginManager::unloadAllPlugins() {
00408     for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
00409         unloadPluginsExcept((PluginType)i, NULL);
00410 }
00411 
00412 void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin, bool deletePlugin /*=true*/) {
00413     Plugin *found = NULL;
00414     for (PluginList::iterator p = _pluginsInMem[type].begin(); p != _pluginsInMem[type].end(); ++p) {
00415         if (*p == plugin) {
00416             found = *p;
00417         } else {
00418             (*p)->unloadPlugin();
00419             if (deletePlugin)
00420                 delete *p;
00421         }
00422     }
00423     _pluginsInMem[type].clear();
00424     if (found != NULL) {
00425         _pluginsInMem[type].push_back(found);
00426     }
00427 }
00428 
00429 /*
00430  * Used only by the cached plugin manager since it deletes the plugin.
00431  */
00432 bool PluginManager::tryLoadPlugin(Plugin *plugin) {
00433     assert(plugin);
00434     // Try to load the plugin
00435     if (plugin->loadPlugin()) {
00436         addToPluginsInMemList(plugin);
00437         return true;
00438     } else {
00439         // Failed to load the plugin
00440         delete plugin;
00441         return false;
00442     }
00443 }
00444 
00448 void PluginManager::addToPluginsInMemList(Plugin *plugin) {
00449     bool found = false;
00450     // The plugin is valid, see if it provides the same module as an
00451     // already loaded one and should replace it.
00452 
00453     PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin();
00454     while (!found && pl != _pluginsInMem[plugin->getType()].end()) {
00455         if (!strcmp(plugin->getName(), (*pl)->getName())) {
00456             // Found a duplicated module. Replace the old one.
00457             found = true;
00458             delete *pl;
00459             *pl = plugin;
00460             debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
00461         }
00462         pl++;
00463     }
00464 
00465     if (!found) {
00466         // If it provides a new module, just add it to the list of known plugins in memory.
00467         _pluginsInMem[plugin->getType()].push_back(plugin);
00468     }
00469 }
00470 
00471 // Engine plugins
00472 
00473 #include "engines/metaengine.h"
00474 
00475 namespace Common {
00476 DECLARE_SINGLETON(EngineManager);
00477 }
00478 
00486 PlainGameDescriptor EngineManager::findGame(const Common::String &gameName, const Plugin **plugin) const {
00487     // First look for the game using the plugins in memory. This is critical
00488     // for calls coming from inside games
00489     PlainGameDescriptor result = findGameInLoadedPlugins(gameName, plugin);
00490     if (result.gameId) {
00491         return result;
00492     }
00493 
00494     // Now look for the game using the gameId. This is much faster than scanning plugin
00495     // by plugin
00496     if (PluginMan.loadPluginFromGameId(gameName))  {
00497         result = findGameInLoadedPlugins(gameName, plugin);
00498         if (result.gameId) {
00499             return result;
00500         }
00501     }
00502 
00503     // We failed to find it using the gameid. Scan the list of plugins
00504     PluginMan.loadFirstPlugin();
00505     do {
00506         result = findGameInLoadedPlugins(gameName, plugin);
00507         if (result.gameId) {
00508             // Update with new plugin file name
00509             PluginMan.updateConfigWithFileName(gameName);
00510             break;
00511         }
00512     } while (PluginMan.loadNextPlugin());
00513 
00514     return result;
00515 }
00516 
00520 PlainGameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
00521     // Find the GameDescriptor for this target
00522     const PluginList &plugins = getPlugins();
00523 
00524     if (plugin)
00525         *plugin = 0;
00526 
00527     PluginList::const_iterator iter;
00528 
00529     for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
00530         PlainGameDescriptor pgd = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
00531         if (pgd.gameId) {
00532             if (plugin)
00533                 *plugin = *iter;
00534             return pgd;
00535         }
00536     }
00537 
00538     return PlainGameDescriptor::empty();
00539 }
00540 
00541 DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
00542     DetectedGames candidates;
00543     PluginList plugins;
00544     PluginList::const_iterator iter;
00545     PluginManager::instance().loadFirstPlugin();
00546     do {
00547         plugins = getPlugins();
00548         // Iterate over all known games and for each check if it might be
00549         // the game in the presented directory.
00550         for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
00551             const MetaEngine &metaEngine = (*iter)->get<MetaEngine>();
00552             DetectedGames engineCandidates = metaEngine.detectGames(fslist);
00553 
00554             for (uint i = 0; i < engineCandidates.size(); i++) {
00555                 engineCandidates[i].engineName = metaEngine.getName();
00556                 engineCandidates[i].path = fslist.begin()->getParent().getPath();
00557                 engineCandidates[i].shortPath = fslist.begin()->getParent().getDisplayName();
00558                 candidates.push_back(engineCandidates[i]);
00559             }
00560 
00561         }
00562     } while (PluginManager::instance().loadNextPlugin());
00563 
00564     return DetectionResults(candidates);
00565 }
00566 
00567 const PluginList &EngineManager::getPlugins() const {
00568     return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
00569 }
00570 
00571 namespace {
00572 
00573 void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
00574     if (!value.empty())
00575         ConfMan.set(key, value, domain);
00576 }
00577 
00578 } // End of anonymous namespace
00579 
00580 Common::String EngineManager::createTargetForGame(const DetectedGame &game) {
00581     // The auto detector or the user made a choice.
00582     // Pick a domain name which does not yet exist (after all, we
00583     // are *adding* a game to the config, not replacing).
00584     Common::String domain = game.preferredTarget;
00585 
00586     assert(!domain.empty());
00587     if (ConfMan.hasGameDomain(domain)) {
00588         int suffixN = 1;
00589         Common::String gameid(domain);
00590 
00591         while (ConfMan.hasGameDomain(domain)) {
00592             domain = gameid + Common::String::format("-%d", suffixN);
00593             suffixN++;
00594         }
00595     }
00596 
00597     // Add the name domain
00598     ConfMan.addGameDomain(domain);
00599 
00600     // Copy all non-empty relevant values into the new domain
00601     addStringToConf("gameid", game.gameId, domain);
00602     addStringToConf("description", game.description, domain);
00603     addStringToConf("language", Common::getLanguageCode(game.language), domain);
00604     addStringToConf("platform", Common::getPlatformCode(game.platform), domain);
00605     addStringToConf("path", game.path, domain);
00606     addStringToConf("extra", game.extra, domain);
00607     addStringToConf("guioptions", game.getGUIOptions(), domain);
00608 
00609     // Add any extra configuration keys
00610     for (Common::StringMap::iterator i = game._extraConfigEntries.begin();
00611             i != game._extraConfigEntries.end(); ++i)
00612         addStringToConf((*i)._key, (*i)._value, domain);
00613 
00614     // TODO: Setting the description field here has the drawback
00615     // that the user does never notice when we upgrade our descriptions.
00616     // It might be nice to leave this field empty, and only set it to
00617     // a value when the user edits the description string.
00618     // However, at this point, that's impractical. Once we have a method
00619     // to query all backends for the proper & full description of a given
00620     // game target, we can change this (currently, you can only query
00621     // for the generic gameid description; it's not possible to obtain
00622     // a description which contains extended information like language, etc.).
00623 
00624     return domain;
00625 }
00626 
00627 // Music plugins
00628 
00629 #include "audio/musicplugin.h"
00630 
00631 namespace Common {
00632 DECLARE_SINGLETON(MusicManager);
00633 }
00634 
00635 const PluginList &MusicManager::getPlugins() const {
00636     return PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
00637 }


Generated on Sat Aug 24 2019 05:00:52 for ResidualVM by doxygen 1.7.1
curved edge   curved edge