1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "base/plugins.h"
24
25 #include "common/func.h"
26 #include "common/debug.h"
27 #include "common/config-manager.h"
28
29 #ifdef DYNAMIC_MODULES
30 #include "common/fs.h"
31 #endif
32
33 // Plugin versioning
34
35 int pluginTypeVersions[PLUGIN_TYPE_MAX] = {
36 PLUGIN_TYPE_ENGINE_VERSION,
37 PLUGIN_TYPE_MUSIC_VERSION,
38 };
39
40
41 // Abstract plugins
42
getType() const43 PluginType Plugin::getType() const {
44 return _type;
45 }
46
getName() const47 const char *Plugin::getName() const {
48 return _pluginObject->getName();
49 }
50
51 class StaticPlugin : public Plugin {
52 public:
StaticPlugin(PluginObject * pluginobject,PluginType type)53 StaticPlugin(PluginObject *pluginobject, PluginType type) {
54 assert(pluginobject);
55 assert(type < PLUGIN_TYPE_MAX);
56 _pluginObject = pluginobject;
57 _type = type;
58 }
59
~StaticPlugin()60 ~StaticPlugin() {
61 delete _pluginObject;
62 }
63
loadPlugin()64 virtual bool loadPlugin() { return true; }
unloadPlugin()65 virtual void unloadPlugin() {}
66 };
67
68 class StaticPluginProvider : public PluginProvider {
69 public:
StaticPluginProvider()70 StaticPluginProvider() {
71 }
72
~StaticPluginProvider()73 ~StaticPluginProvider() {
74 }
75
getPlugins()76 virtual PluginList getPlugins() {
77 PluginList pl;
78
79 #define LINK_PLUGIN(ID) \
80 extern PluginType g_##ID##_type; \
81 extern PluginObject *g_##ID##_getObject(); \
82 pl.push_back(new StaticPlugin(g_##ID##_getObject(), g_##ID##_type));
83
84 // "Loader" for the static plugins.
85 // Iterate over all registered (static) plugins and load them.
86
87 // Engine plugins
88 #include "engines/plugins_table.h"
89
90 // Music plugins
91 // TODO: Use defines to disable or enable each MIDI driver as a
92 // static/dynamic plugin, like it's done for the engines
93 LINK_PLUGIN(AUTO)
94 LINK_PLUGIN(NULL)
95 #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
96 LINK_PLUGIN(WINDOWS)
97 #endif
98 #if defined(USE_ALSA)
99 LINK_PLUGIN(ALSA)
100 #endif
101 #if defined(USE_SEQ_MIDI)
102 LINK_PLUGIN(SEQ)
103 #endif
104 #if defined(USE_SNDIO)
105 LINK_PLUGIN(SNDIO)
106 #endif
107 #if defined(__MINT__)
108 LINK_PLUGIN(STMIDI)
109 #endif
110 #if defined(IRIX)
111 LINK_PLUGIN(DMEDIA)
112 #endif
113 #if defined(__amigaos4__)
114 LINK_PLUGIN(CAMD)
115 #endif
116 #if defined(MACOSX)
117 LINK_PLUGIN(COREAUDIO)
118 LINK_PLUGIN(COREMIDI)
119 #endif
120 #ifdef USE_FLUIDSYNTH
121 LINK_PLUGIN(FLUIDSYNTH)
122 #endif
123 #ifdef USE_MT32EMU
124 LINK_PLUGIN(MT32)
125 #endif
126 LINK_PLUGIN(ADLIB)
127 //ResidualVM: disabled belows:
128 // LINK_PLUGIN(PCSPK)
129 // LINK_PLUGIN(PCJR)
130 // LINK_PLUGIN(CMS)
131 #if defined(__ANDROID__)
132 // LINK_PLUGIN(EAS)
133 #endif
134 #ifndef DISABLE_SID
135 // LINK_PLUGIN(C64)
136 #endif
137 // LINK_PLUGIN(AMIGA)
138 // LINK_PLUGIN(APPLEIIGS)
139 // LINK_PLUGIN(TOWNS)
140 // LINK_PLUGIN(PC98)
141 #if defined(USE_TIMIDITY)
142 LINK_PLUGIN(TIMIDITY)
143 #endif
144
145 return pl;
146 }
147 };
148
149 #ifdef DYNAMIC_MODULES
150
getPlugins()151 PluginList FilePluginProvider::getPlugins() {
152 PluginList pl;
153
154 // Prepare the list of directories to search
155 Common::FSList pluginDirs;
156
157 // Add the default directories
158 pluginDirs.push_back(Common::FSNode("."));
159 pluginDirs.push_back(Common::FSNode("plugins"));
160
161 // Add the provider's custom directories
162 addCustomDirectories(pluginDirs);
163
164 // Add the user specified directory
165 Common::String pluginsPath(ConfMan.get("pluginspath"));
166 if (!pluginsPath.empty())
167 pluginDirs.push_back(Common::FSNode(pluginsPath));
168
169 Common::FSList::const_iterator dir;
170 for (dir = pluginDirs.begin(); dir != pluginDirs.end(); ++dir) {
171 // Load all plugins.
172 // Scan for all plugins in this directory
173 Common::FSList files;
174 if (!dir->getChildren(files, Common::FSNode::kListFilesOnly)) {
175 debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str());
176 continue;
177 } else {
178 debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str());
179 }
180
181 for (Common::FSList::const_iterator i = files.begin(); i != files.end(); ++i) {
182 if (isPluginFilename(*i)) {
183 pl.push_back(createPlugin(*i));
184 }
185 }
186 }
187
188 return pl;
189 }
190
isPluginFilename(const Common::FSNode & node) const191 bool FilePluginProvider::isPluginFilename(const Common::FSNode &node) const {
192 Common::String filename = node.getName();
193
194 #ifdef PLUGIN_PREFIX
195 // Check the plugin prefix
196 if (!filename.hasPrefix(PLUGIN_PREFIX))
197 return false;
198 #endif
199
200 #ifdef PLUGIN_SUFFIX
201 // Check the plugin suffix
202 if (!filename.hasSuffix(PLUGIN_SUFFIX))
203 return false;
204 #endif
205
206 return true;
207 }
208
addCustomDirectories(Common::FSList & dirs) const209 void FilePluginProvider::addCustomDirectories(Common::FSList &dirs) const {
210 #ifdef PLUGIN_DIRECTORY
211 dirs.push_back(Common::FSNode(PLUGIN_DIRECTORY));
212 #endif
213 }
214
215 #endif // DYNAMIC_MODULES
216
217 #pragma mark -
218
219 PluginManager *PluginManager::_instance = NULL;
220
instance()221 PluginManager &PluginManager::instance() {
222 if (_instance)
223 return *_instance;
224
225 #if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES)
226 _instance = new PluginManagerUncached();
227 #else
228 _instance = new PluginManager();
229 #endif
230 return *_instance;
231 }
232
PluginManager()233 PluginManager::PluginManager() {
234 // Always add the static plugin provider.
235 addPluginProvider(new StaticPluginProvider());
236 }
237
~PluginManager()238 PluginManager::~PluginManager() {
239 // Explicitly unload all loaded plugins
240 unloadAllPlugins();
241
242 // Delete the plugin providers
243 for (ProviderList::iterator pp = _providers.begin();
244 pp != _providers.end();
245 ++pp) {
246 delete *pp;
247 }
248 }
249
addPluginProvider(PluginProvider * pp)250 void PluginManager::addPluginProvider(PluginProvider *pp) {
251 _providers.push_back(pp);
252 }
253
254 /**
255 * This should only be called once by main()
256 **/
init()257 void PluginManagerUncached::init() {
258 unloadAllPlugins();
259 _allEnginePlugins.clear();
260
261 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // empty the engine plugins
262
263 for (ProviderList::iterator pp = _providers.begin();
264 pp != _providers.end();
265 ++pp) {
266 PluginList pl((*pp)->getPlugins());
267
268 for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) {
269 // This is a 'hack' based on the assumption that we have no sound
270 // file plugins. Currently this is the case. If it changes, we
271 // should find a fast way of detecting whether a plugin is a
272 // music or an engine plugin.
273 if ((*pp)->isFilePluginProvider()) {
274 _allEnginePlugins.push_back(*p);
275 } else if ((*p)->loadPlugin()) { // and this is the proper method
276 if ((*p)->getType() == PLUGIN_TYPE_ENGINE) {
277 (*p)->unloadPlugin();
278 _allEnginePlugins.push_back(*p);
279 } else { // add non-engine plugins to the 'in-memory' list
280 // these won't ever get unloaded
281 addToPluginsInMemList(*p);
282 }
283 }
284 }
285 }
286 }
287
288 /**
289 * Try to load the plugin by searching in the ConfigManager for a matching
290 * gameId under the domain 'plugin_files'.
291 **/
loadPluginFromGameId(const Common::String & gameId)292 bool PluginManagerUncached::loadPluginFromGameId(const Common::String &gameId) {
293 Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
294
295 if (domain) {
296 if (domain->contains(gameId)) {
297 Common::String filename = (*domain)[gameId];
298
299 if (loadPluginByFileName(filename)) {
300 return true;
301 }
302 }
303 }
304 return false;
305 }
306
307 /**
308 * Load a plugin with a filename taken from ConfigManager.
309 **/
loadPluginByFileName(const Common::String & filename)310 bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) {
311 if (filename.empty())
312 return false;
313
314 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
315
316 PluginList::iterator i;
317 for (i = _allEnginePlugins.begin(); i != _allEnginePlugins.end(); ++i) {
318 if (Common::String((*i)->getFileName()) == filename && (*i)->loadPlugin()) {
319 addToPluginsInMemList(*i);
320 _currentPlugin = i;
321 return true;
322 }
323 }
324 return false;
325 }
326
327 /**
328 * Update the config manager with a plugin file name that we found can handle
329 * the game.
330 **/
updateConfigWithFileName(const Common::String & gameId)331 void PluginManagerUncached::updateConfigWithFileName(const Common::String &gameId) {
332 // Check if we have a filename for the current plugin
333 if ((*_currentPlugin)->getFileName()) {
334 if (!ConfMan.hasMiscDomain("plugin_files"))
335 ConfMan.addMiscDomain("plugin_files");
336
337 Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
338 assert(domain);
339 (*domain)[gameId] = (*_currentPlugin)->getFileName();
340
341 ConfMan.flushToDisk();
342 }
343 }
344
loadFirstPlugin()345 void PluginManagerUncached::loadFirstPlugin() {
346 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
347
348 // let's try to find one we can load
349 for (_currentPlugin = _allEnginePlugins.begin(); _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
350 if ((*_currentPlugin)->loadPlugin()) {
351 addToPluginsInMemList(*_currentPlugin);
352 break;
353 }
354 }
355 }
356
loadNextPlugin()357 bool PluginManagerUncached::loadNextPlugin() {
358 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
359
360 for (++_currentPlugin; _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
361 if ((*_currentPlugin)->loadPlugin()) {
362 addToPluginsInMemList(*_currentPlugin);
363 return true;
364 }
365 }
366 return false; // no more in list
367 }
368
369 /**
370 * Used by only the cached plugin manager. The uncached manager can only have
371 * one plugin in memory at a time.
372 **/
loadAllPlugins()373 void PluginManager::loadAllPlugins() {
374 for (ProviderList::iterator pp = _providers.begin();
375 pp != _providers.end();
376 ++pp) {
377 PluginList pl((*pp)->getPlugins());
378 Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
379 }
380 }
381
unloadAllPlugins()382 void PluginManager::unloadAllPlugins() {
383 for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
384 unloadPluginsExcept((PluginType)i, NULL);
385 }
386
unloadPluginsExcept(PluginType type,const Plugin * plugin,bool deletePlugin)387 void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin, bool deletePlugin /*=true*/) {
388 Plugin *found = NULL;
389 for (PluginList::iterator p = _pluginsInMem[type].begin(); p != _pluginsInMem[type].end(); ++p) {
390 if (*p == plugin) {
391 found = *p;
392 } else {
393 (*p)->unloadPlugin();
394 if (deletePlugin)
395 delete *p;
396 }
397 }
398 _pluginsInMem[type].clear();
399 if (found != NULL) {
400 _pluginsInMem[type].push_back(found);
401 }
402 }
403
404 /*
405 * Used only by the cached plugin manager since it deletes the plugin.
406 */
tryLoadPlugin(Plugin * plugin)407 bool PluginManager::tryLoadPlugin(Plugin *plugin) {
408 assert(plugin);
409 // Try to load the plugin
410 if (plugin->loadPlugin()) {
411 addToPluginsInMemList(plugin);
412 return true;
413 } else {
414 // Failed to load the plugin
415 delete plugin;
416 return false;
417 }
418 }
419
420 /**
421 * Add to the list of plugins loaded in memory.
422 */
addToPluginsInMemList(Plugin * plugin)423 void PluginManager::addToPluginsInMemList(Plugin *plugin) {
424 bool found = false;
425 // The plugin is valid, see if it provides the same module as an
426 // already loaded one and should replace it.
427
428 PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin();
429 while (!found && pl != _pluginsInMem[plugin->getType()].end()) {
430 if (!strcmp(plugin->getName(), (*pl)->getName())) {
431 // Found a duplicated module. Replace the old one.
432 found = true;
433 delete *pl;
434 *pl = plugin;
435 debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
436 }
437 pl++;
438 }
439
440 if (!found) {
441 // If it provides a new module, just add it to the list of known plugins in memory.
442 _pluginsInMem[plugin->getType()].push_back(plugin);
443 }
444 }
445
446 // Engine plugins
447
448 #include "engines/metaengine.h"
449
450 namespace Common {
451 DECLARE_SINGLETON(EngineManager);
452 }
453
454 /**
455 * This function works for both cached and uncached PluginManagers.
456 * For the cached version, most of the logic here will short circuit.
457 *
458 * For the uncached version, we first try to find the plugin using the gameId
459 * and only if we can't find it there, we loop through the plugins.
460 **/
findGame(const Common::String & gameName,const EnginePlugin ** plugin) const461 GameDescriptor EngineManager::findGame(const Common::String &gameName, const EnginePlugin **plugin) const {
462 GameDescriptor result;
463
464 // First look for the game using the plugins in memory. This is critical
465 // for calls coming from inside games
466 result = findGameInLoadedPlugins(gameName, plugin);
467 if (!result.gameid().empty()) {
468 return result;
469 }
470
471 // Now look for the game using the gameId. This is much faster than scanning plugin
472 // by plugin
473 if (PluginMan.loadPluginFromGameId(gameName)) {
474 result = findGameInLoadedPlugins(gameName, plugin);
475 if (!result.gameid().empty()) {
476 return result;
477 }
478 }
479
480 // We failed to find it using the gameid. Scan the list of plugins
481 PluginMan.loadFirstPlugin();
482 do {
483 result = findGameInLoadedPlugins(gameName, plugin);
484 if (!result.gameid().empty()) {
485 // Update with new plugin file name
486 PluginMan.updateConfigWithFileName(gameName);
487 break;
488 }
489 } while (PluginMan.loadNextPlugin());
490
491 return result;
492 }
493
494 /**
495 * Find the game within the plugins loaded in memory
496 **/
findGameInLoadedPlugins(const Common::String & gameName,const EnginePlugin ** plugin) const497 GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const EnginePlugin **plugin) const {
498 // Find the GameDescriptor for this target
499 const EnginePlugin::List &plugins = getPlugins();
500 GameDescriptor result;
501
502 if (plugin)
503 *plugin = 0;
504
505 EnginePlugin::List::const_iterator iter;
506
507 for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
508 result = (**iter)->findGame(gameName.c_str());
509 if (!result.gameid().empty()) {
510 if (plugin)
511 *plugin = *iter;
512 return result;
513 }
514 }
515 return result;
516 }
517
detectGames(const Common::FSList & fslist) const518 GameList EngineManager::detectGames(const Common::FSList &fslist) const {
519 GameList candidates;
520 EnginePlugin::List plugins;
521 EnginePlugin::List::const_iterator iter;
522 PluginManager::instance().loadFirstPlugin();
523 do {
524 plugins = getPlugins();
525 // Iterate over all known games and for each check if it might be
526 // the game in the presented directory.
527 for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
528 candidates.push_back((**iter)->detectGames(fslist));
529 }
530 } while (PluginManager::instance().loadNextPlugin());
531 return candidates;
532 }
533
getPlugins() const534 const EnginePlugin::List &EngineManager::getPlugins() const {
535 return (const EnginePlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
536 }
537
538
539 // Music plugins
540
541 #include "audio/musicplugin.h"
542
543 namespace Common {
544 DECLARE_SINGLETON(MusicManager);
545 }
546
getPlugins() const547 const MusicPlugin::List &MusicManager::getPlugins() const {
548 return (const MusicPlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
549 }
550