Ticket #9410: plugins.cpp

File plugins.cpp, 16.3 KB (added by SF/mrperphekt, 13 years ago)

Commented out SCI plugin

Line 
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
35int pluginTypeVersions[PLUGIN_TYPE_MAX] = {
36 PLUGIN_TYPE_ENGINE_VERSION,
37 PLUGIN_TYPE_MUSIC_VERSION,
38};
39
40
41// Abstract plugins
42
43PluginType Plugin::getType() const {
44 return _type;
45}
46
47const char *Plugin::getName() const {
48 return _pluginObject->getName();
49}
50
51class StaticPlugin : public Plugin {
52public:
53 StaticPlugin(PluginObject *pluginobject, PluginType type) {
54 assert(pluginobject);
55 assert(type < PLUGIN_TYPE_MAX);
56 _pluginObject = pluginobject;
57 _type = type;
58 }
59
60 ~StaticPlugin() {
61 delete _pluginObject;
62 }
63
64 virtual bool loadPlugin() { return true; }
65 virtual void unloadPlugin() {}
66};
67
68class StaticPluginProvider : public PluginProvider {
69public:
70 StaticPluginProvider() {
71 }
72
73 ~StaticPluginProvider() {
74 }
75
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 #if PLUGIN_ENABLED_STATIC(SCUMM)
89 LINK_PLUGIN(SCUMM)
90 #endif
91 #if PLUGIN_ENABLED_STATIC(AGI)
92 LINK_PLUGIN(AGI)
93 #endif
94 #if PLUGIN_ENABLED_STATIC(AGOS)
95 LINK_PLUGIN(AGOS)
96 #endif
97 #if PLUGIN_ENABLED_STATIC(CGE)
98 LINK_PLUGIN(CGE)
99 #endif
100 #if PLUGIN_ENABLED_STATIC(CINE)
101 LINK_PLUGIN(CINE)
102 #endif
103 #if PLUGIN_ENABLED_STATIC(COMPOSER)
104 LINK_PLUGIN(COMPOSER)
105 #endif
106 #if PLUGIN_ENABLED_STATIC(CRUISE)
107 LINK_PLUGIN(CRUISE)
108 #endif
109 #if PLUGIN_ENABLED_STATIC(DRACI)
110 LINK_PLUGIN(DRACI)
111 #endif
112 #if PLUGIN_ENABLED_STATIC(DRASCULA)
113 LINK_PLUGIN(DRASCULA)
114 #endif
115 #if PLUGIN_ENABLED_STATIC(DREAMWEB)
116 LINK_PLUGIN(DREAMWEB)
117 #endif
118 #if PLUGIN_ENABLED_STATIC(GOB)
119 LINK_PLUGIN(GOB)
120 #endif
121 #if PLUGIN_ENABLED_STATIC(GROOVIE)
122 LINK_PLUGIN(GROOVIE)
123 #endif
124 #if PLUGIN_ENABLED_STATIC(HUGO)
125 LINK_PLUGIN(HUGO)
126 #endif
127 #if PLUGIN_ENABLED_STATIC(KYRA)
128 LINK_PLUGIN(KYRA)
129 #endif
130 #if PLUGIN_ENABLED_STATIC(LASTEXPRESS)
131 LINK_PLUGIN(LASTEXPRESS)
132 #endif
133 #if PLUGIN_ENABLED_STATIC(LURE)
134 LINK_PLUGIN(LURE)
135 #endif
136 #if PLUGIN_ENABLED_STATIC(MADE)
137 LINK_PLUGIN(MADE)
138 #endif
139 #if PLUGIN_ENABLED_STATIC(MOHAWK)
140 LINK_PLUGIN(MOHAWK)
141 #endif
142 #if PLUGIN_ENABLED_STATIC(PARALLACTION)
143 LINK_PLUGIN(PARALLACTION)
144 #endif
145 #if PLUGIN_ENABLED_STATIC(QUEEN)
146 LINK_PLUGIN(QUEEN)
147 #endif
148 #if PLUGIN_ENABLED_STATIC(SAGA)
149 LINK_PLUGIN(SAGA)
150 #endif
151 /*
152 #if PLUGIN_ENABLED_STATIC(SCI)
153 LINK_PLUGIN(SCI)
154 #endif
155 */
156 #if PLUGIN_ENABLED_STATIC(SKY)
157 LINK_PLUGIN(SKY)
158 #endif
159 #if PLUGIN_ENABLED_STATIC(SWORD1)
160 LINK_PLUGIN(SWORD1)
161 #endif
162 #if PLUGIN_ENABLED_STATIC(SWORD2)
163 LINK_PLUGIN(SWORD2)
164 #endif
165 #if PLUGIN_ENABLED_STATIC(SWORD25)
166 LINK_PLUGIN(SWORD25)
167 #endif
168 #if PLUGIN_ENABLED_STATIC(TEENAGENT)
169 LINK_PLUGIN(TEENAGENT)
170 #endif
171 #if PLUGIN_ENABLED_STATIC(TESTBED)
172 LINK_PLUGIN(TESTBED)
173 #endif
174 #if PLUGIN_ENABLED_STATIC(TINSEL)
175 LINK_PLUGIN(TINSEL)
176 #endif
177 #if PLUGIN_ENABLED_STATIC(TOLTECS)
178 LINK_PLUGIN(TOLTECS)
179 #endif
180 #if PLUGIN_ENABLED_STATIC(TOON)
181 LINK_PLUGIN(TOON)
182 #endif
183 #if PLUGIN_ENABLED_STATIC(TSAGE)
184 LINK_PLUGIN(TSAGE)
185 #endif
186 #if PLUGIN_ENABLED_STATIC(TOUCHE)
187 LINK_PLUGIN(TOUCHE)
188 #endif
189 #if PLUGIN_ENABLED_STATIC(TUCKER)
190 LINK_PLUGIN(TUCKER)
191 #endif
192
193 // Music plugins
194 // TODO: Use defines to disable or enable each MIDI driver as a
195 // static/dynamic plugin, like it's done for the engines
196 LINK_PLUGIN(AUTO)
197 LINK_PLUGIN(NULL)
198 #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
199 LINK_PLUGIN(WINDOWS)
200 #endif
201 #if defined(USE_ALSA)
202 LINK_PLUGIN(ALSA)
203 #endif
204 #if defined(USE_SEQ_MIDI)
205 LINK_PLUGIN(SEQ)
206 #endif
207 #if defined(__MINT__)
208 LINK_PLUGIN(STMIDI)
209 #endif
210 #if defined(IRIX)
211 LINK_PLUGIN(DMEDIA)
212 #endif
213 #if defined(__amigaos4__)
214 LINK_PLUGIN(CAMD)
215 #endif
216 #if defined(MACOSX)
217 LINK_PLUGIN(COREAUDIO)
218 LINK_PLUGIN(COREMIDI)
219 #endif
220 #ifdef USE_FLUIDSYNTH
221 LINK_PLUGIN(FLUIDSYNTH)
222 #endif
223 #ifdef USE_MT32EMU
224 LINK_PLUGIN(MT32)
225 #endif
226 #if defined(__ANDROID__)
227 LINK_PLUGIN(EAS)
228 #endif
229 LINK_PLUGIN(ADLIB)
230 LINK_PLUGIN(PCSPK)
231 LINK_PLUGIN(PCJR)
232 LINK_PLUGIN(CMS)
233 #ifndef DISABLE_SID
234 LINK_PLUGIN(C64)
235 #endif
236 LINK_PLUGIN(AMIGA)
237 LINK_PLUGIN(APPLEIIGS)
238 LINK_PLUGIN(TOWNS)
239 LINK_PLUGIN(PC98)
240 #if defined(USE_TIMIDITY)
241 LINK_PLUGIN(TIMIDITY)
242 #endif
243
244 return pl;
245 }
246};
247
248#ifdef DYNAMIC_MODULES
249
250PluginList FilePluginProvider::getPlugins() {
251 PluginList pl;
252
253 // Prepare the list of directories to search
254 Common::FSList pluginDirs;
255
256 // Add the default directories
257 pluginDirs.push_back(Common::FSNode("."));
258 pluginDirs.push_back(Common::FSNode("plugins"));
259
260 // Add the provider's custom directories
261 addCustomDirectories(pluginDirs);
262
263 // Add the user specified directory
264 Common::String pluginsPath(ConfMan.get("pluginspath"));
265 if (!pluginsPath.empty())
266 pluginDirs.push_back(Common::FSNode(pluginsPath));
267
268 Common::FSList::const_iterator dir;
269 for (dir = pluginDirs.begin(); dir != pluginDirs.end(); ++dir) {
270 // Load all plugins.
271 // Scan for all plugins in this directory
272 Common::FSList files;
273 if (!dir->getChildren(files, Common::FSNode::kListFilesOnly)) {
274 debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str());
275 continue;
276 } else {
277 debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str());
278 }
279
280 for (Common::FSList::const_iterator i = files.begin(); i != files.end(); ++i) {
281 if (isPluginFilename(*i)) {
282 pl.push_back(createPlugin(*i));
283 }
284 }
285 }
286
287 return pl;
288}
289
290bool FilePluginProvider::isPluginFilename(const Common::FSNode &node) const {
291 Common::String filename = node.getName();
292
293#ifdef PLUGIN_PREFIX
294 // Check the plugin prefix
295 if (!filename.hasPrefix(PLUGIN_PREFIX))
296 return false;
297#endif
298
299#ifdef PLUGIN_SUFFIX
300 // Check the plugin suffix
301 if (!filename.hasSuffix(PLUGIN_SUFFIX))
302 return false;
303#endif
304
305 return true;
306}
307
308void FilePluginProvider::addCustomDirectories(Common::FSList &dirs) const {
309#ifdef PLUGIN_DIRECTORY
310 dirs.push_back(Common::FSNode(PLUGIN_DIRECTORY));
311#endif
312}
313
314#endif // DYNAMIC_MODULES
315
316#pragma mark -
317
318PluginManager *PluginManager::_instance = NULL;
319
320PluginManager &PluginManager::instance() {
321 if (_instance)
322 return *_instance;
323
324#if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES)
325 _instance = new PluginManagerUncached();
326#else
327 _instance = new PluginManager();
328#endif
329 return *_instance;
330}
331
332PluginManager::PluginManager() {
333 // Always add the static plugin provider.
334 addPluginProvider(new StaticPluginProvider());
335}
336
337PluginManager::~PluginManager() {
338 // Explicitly unload all loaded plugins
339 unloadAllPlugins();
340
341 // Delete the plugin providers
342 for (ProviderList::iterator pp = _providers.begin();
343 pp != _providers.end();
344 ++pp) {
345 delete *pp;
346 }
347}
348
349void PluginManager::addPluginProvider(PluginProvider *pp) {
350 _providers.push_back(pp);
351}
352
353/**
354 * This should only be called once by main()
355 **/
356void PluginManagerUncached::init() {
357 unloadAllPlugins();
358 _allEnginePlugins.clear();
359
360 // Resize our pluginsInMem list to prevent fragmentation
361 _pluginsInMem[PLUGIN_TYPE_ENGINE].resize(2);
362 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // empty the engine plugins
363
364 for (ProviderList::iterator pp = _providers.begin();
365 pp != _providers.end();
366 ++pp) {
367 PluginList pl((*pp)->getPlugins());
368
369 for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) {
370 // This is a 'hack' based on the assumption that we have no sound
371 // file plugins. Currently this is the case. If it changes, we
372 // should find a fast way of detecting whether a plugin is a
373 // music or an engine plugin.
374 if ((*pp)->isFilePluginProvider()) {
375 _allEnginePlugins.push_back(*p);
376 } else if ((*p)->loadPlugin()) { // and this is the proper method
377 if ((*p)->getType() == PLUGIN_TYPE_ENGINE) {
378 (*p)->unloadPlugin();
379 _allEnginePlugins.push_back(*p);
380 } else { // add non-engine plugins to the 'in-memory' list
381 // these won't ever get unloaded
382 addToPluginsInMemList(*p);
383 }
384 }
385 }
386 }
387}
388
389/**
390 * Try to load the plugin by searching in the ConfigManager for a matching
391 * gameId under the domain 'plugin_files'.
392 **/
393bool PluginManagerUncached::loadPluginFromGameId(const Common::String &gameId) {
394 Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
395
396 if (domain) {
397 if (domain->contains(gameId)) {
398 Common::String filename = (*domain)[gameId];
399
400 if (loadPluginByFileName(filename)) {
401 return true;
402 }
403 }
404 }
405 return false;
406}
407
408/**
409 * Load a plugin with a filename taken from ConfigManager.
410 **/
411bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) {
412 if (filename.empty())
413 return false;
414
415 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
416
417 PluginList::iterator i;
418 for (i = _allEnginePlugins.begin(); i != _allEnginePlugins.end(); ++i) {
419 if (Common::String((*i)->getFileName()) == filename && (*i)->loadPlugin()) {
420 addToPluginsInMemList(*i);
421 _currentPlugin = i;
422 return true;
423 }
424 }
425 return false;
426}
427
428/**
429 * Update the config manager with a plugin file name that we found can handle
430 * the game.
431 **/
432void PluginManagerUncached::updateConfigWithFileName(const Common::String &gameId) {
433 // Check if we have a filename for the current plugin
434 if ((*_currentPlugin)->getFileName()) {
435 if (!ConfMan.hasMiscDomain("plugin_files"))
436 ConfMan.addMiscDomain("plugin_files");
437
438 Common::ConfigManager::Domain *domain = ConfMan.getDomain("plugin_files");
439 assert(domain);
440 (*domain)[gameId] = (*_currentPlugin)->getFileName();
441
442 ConfMan.flushToDisk();
443 }
444}
445
446void PluginManagerUncached::loadFirstPlugin() {
447 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
448
449 // let's try to find one we can load
450 for (_currentPlugin = _allEnginePlugins.begin(); _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
451 if ((*_currentPlugin)->loadPlugin()) {
452 addToPluginsInMemList(*_currentPlugin);
453 break;
454 }
455 }
456}
457
458bool PluginManagerUncached::loadNextPlugin() {
459 unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
460
461 for (++_currentPlugin; _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
462 if ((*_currentPlugin)->loadPlugin()) {
463 addToPluginsInMemList(*_currentPlugin);
464 return true;
465 }
466 }
467 return false; // no more in list
468}
469
470/**
471 * Used by only the cached plugin manager. The uncached manager can only have
472 * one plugin in memory at a time.
473 **/
474void PluginManager::loadAllPlugins() {
475 for (ProviderList::iterator pp = _providers.begin();
476 pp != _providers.end();
477 ++pp) {
478 PluginList pl((*pp)->getPlugins());
479 Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
480 }
481}
482
483void PluginManager::unloadAllPlugins() {
484 for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
485 unloadPluginsExcept((PluginType)i, NULL);
486}
487
488void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin, bool deletePlugin /*=true*/) {
489 Plugin *found = NULL;
490 for (PluginList::iterator p = _pluginsInMem[type].begin(); p != _pluginsInMem[type].end(); ++p) {
491 if (*p == plugin) {
492 found = *p;
493 } else {
494 (*p)->unloadPlugin();
495 if (deletePlugin)
496 delete *p;
497 }
498 }
499 _pluginsInMem[type].clear();
500 if (found != NULL) {
501 _pluginsInMem[type].push_back(found);
502 }
503}
504
505/*
506 * Used only by the cached plugin manager since it deletes the plugin.
507 */
508bool PluginManager::tryLoadPlugin(Plugin *plugin) {
509 assert(plugin);
510 // Try to load the plugin
511 if (plugin->loadPlugin()) {
512 addToPluginsInMemList(plugin);
513 return true;
514 } else {
515 // Failed to load the plugin
516 delete plugin;
517 return false;
518 }
519}
520
521/**
522 * Add to the list of plugins loaded in memory.
523 */
524void PluginManager::addToPluginsInMemList(Plugin *plugin) {
525 bool found = false;
526 // The plugin is valid, see if it provides the same module as an
527 // already loaded one and should replace it.
528
529 PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin();
530 while (!found && pl != _pluginsInMem[plugin->getType()].end()) {
531 if (!strcmp(plugin->getName(), (*pl)->getName())) {
532 // Found a duplicated module. Replace the old one.
533 found = true;
534 delete *pl;
535 *pl = plugin;
536 debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
537 }
538 pl++;
539 }
540
541 if (!found) {
542 // If it provides a new module, just add it to the list of known plugins in memory.
543 _pluginsInMem[plugin->getType()].push_back(plugin);
544 }
545}
546
547// Engine plugins
548
549#include "engines/metaengine.h"
550
551namespace Common {
552DECLARE_SINGLETON(EngineManager);
553}
554
555/**
556 * This function works for both cached and uncached PluginManagers.
557 * For the cached version, most of the logic here will short circuit.
558 *
559 * For the uncached version, we first try to find the plugin using the gameId
560 * and only if we can't find it there, we loop through the plugins.
561 **/
562GameDescriptor EngineManager::findGame(const Common::String &gameName, const EnginePlugin **plugin) const {
563 GameDescriptor result;
564
565 // First look for the game using the plugins in memory. This is critical
566 // for calls coming from inside games
567 result = findGameInLoadedPlugins(gameName, plugin);
568 if (!result.gameid().empty()) {
569 return result;
570 }
571
572 // Now look for the game using the gameId. This is much faster than scanning plugin
573 // by plugin
574 if (PluginMan.loadPluginFromGameId(gameName)) {
575 result = findGameInLoadedPlugins(gameName, plugin);
576 if (!result.gameid().empty()) {
577 return result;
578 }
579 }
580
581 // We failed to find it using the gameid. Scan the list of plugins
582 PluginMan.loadFirstPlugin();
583 do {
584 result = findGameInLoadedPlugins(gameName, plugin);
585 if (!result.gameid().empty()) {
586 // Update with new plugin file name
587 PluginMan.updateConfigWithFileName(gameName);
588 break;
589 }
590 } while (PluginMan.loadNextPlugin());
591
592 return result;
593}
594
595/**
596 * Find the game within the plugins loaded in memory
597 **/
598GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const EnginePlugin **plugin) const {
599 // Find the GameDescriptor for this target
600 const EnginePlugin::List &plugins = getPlugins();
601 GameDescriptor result;
602
603 if (plugin)
604 *plugin = 0;
605
606 EnginePlugin::List::const_iterator iter;
607
608 for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
609 result = (**iter)->findGame(gameName.c_str());
610 if (!result.gameid().empty()) {
611 if (plugin)
612 *plugin = *iter;
613 return result;
614 }
615 }
616 return result;
617}
618
619GameList EngineManager::detectGames(const Common::FSList &fslist) const {
620 GameList candidates;
621 EnginePlugin::List plugins;
622 EnginePlugin::List::const_iterator iter;
623 PluginManager::instance().loadFirstPlugin();
624 do {
625 plugins = getPlugins();
626 // Iterate over all known games and for each check if it might be
627 // the game in the presented directory.
628 for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
629 candidates.push_back((**iter)->detectGames(fslist));
630 }
631 } while (PluginManager::instance().loadNextPlugin());
632 return candidates;
633}
634
635const EnginePlugin::List &EngineManager::getPlugins() const {
636 return (const EnginePlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
637}
638
639
640// Music plugins
641
642#include "audio/musicplugin.h"
643
644namespace Common {
645DECLARE_SINGLETON(MusicManager);
646}
647
648const MusicPlugin::List &MusicManager::getPlugins() const {
649 return (const MusicPlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
650}