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/debug-channels.h"
28 #include "common/config-manager.h"
29 
30 #ifdef DYNAMIC_MODULES
31 #include "common/fs.h"
32 #endif
33 
34 #include "base/detection/detection.h"
35 
36 #include "engines/advancedDetector.h"
37 
38 // Plugin versioning
39 
40 int pluginTypeVersions[PLUGIN_TYPE_MAX] = {
41 	PLUGIN_TYPE_ENGINE_DETECTION_VERSION,
42 	PLUGIN_TYPE_ENGINE_VERSION,
43 	PLUGIN_TYPE_MUSIC_VERSION,
44 	PLUGIN_TYPE_DETECTION_VERSION,
45 	PLUGIN_TYPE_SCALER_VERSION,
46 };
47 
48 
49 // Abstract plugins
50 
getType() const51 PluginType Plugin::getType() const {
52 	return _type;
53 }
54 
getName() const55 const char *Plugin::getName() const {
56 	return _pluginObject->getName();
57 }
58 
getEngineId() const59 const char *Plugin::getEngineId() const {
60 	if (_type == PLUGIN_TYPE_ENGINE_DETECTION) {
61 		return _pluginObject->getEngineId();
62 	}
63 
64 	return nullptr;
65 }
66 
StaticPlugin(PluginObject * pluginobject,PluginType type)67 StaticPlugin::StaticPlugin(PluginObject *pluginobject, PluginType type) {
68 	assert(pluginobject);
69 	assert(type < PLUGIN_TYPE_MAX);
70 	_pluginObject = pluginobject;
71 	_type = type;
72 }
73 
~StaticPlugin()74 StaticPlugin::~StaticPlugin() {
75 	delete _pluginObject;
76 }
77 
loadPlugin()78 bool StaticPlugin::loadPlugin()		{ return true; }
unloadPlugin()79 void StaticPlugin::unloadPlugin()	{}
80 
81 class StaticPluginProvider : public PluginProvider {
82 public:
StaticPluginProvider()83 	StaticPluginProvider() {
84 	}
85 
~StaticPluginProvider()86 	~StaticPluginProvider() {
87 	}
88 
getPlugins()89 	virtual PluginList getPlugins() {
90 		PluginList pl;
91 
92 		#define LINK_PLUGIN(ID) \
93 			extern PluginType g_##ID##_type; \
94 			extern PluginObject *g_##ID##_getObject(); \
95 			pl.push_back(new StaticPlugin(g_##ID##_getObject(), g_##ID##_type));
96 
97 		// "Loader" for the static plugins.
98 		// Iterate over all registered (static) plugins and load them.
99 
100 		// Engine plugins
101 		#include "engines/plugins_table.h"
102 
103 		#ifdef DETECTION_STATIC
104 		// Engine-detection plugins are included if we don't use uncached plugins.
105 		#include "engines/detection_table.h"
106 		#endif
107 
108 		// Music plugins
109 		// TODO: Use defines to disable or enable each MIDI driver as a
110 		// static/dynamic plugin, like it's done for the engines
111 		LINK_PLUGIN(AUTO)
112 		LINK_PLUGIN(NULL)
113 		#if defined(WIN32) && !defined(__SYMBIAN32__)
114 		LINK_PLUGIN(WINDOWS)
115 		#endif
116 		#if defined(USE_ALSA)
117 		LINK_PLUGIN(ALSA)
118 		#endif
119 		#if defined(USE_SEQ_MIDI)
120 		LINK_PLUGIN(SEQ)
121 		#endif
122 		#if defined(USE_SNDIO)
123 		LINK_PLUGIN(SNDIO)
124 		#endif
125 		#if defined(__MINT__)
126 		LINK_PLUGIN(STMIDI)
127 		#endif
128 		#if defined(IRIX)
129 		LINK_PLUGIN(DMEDIA)
130 		#endif
131 		#if defined(__amigaos4__) || defined(__MORPHOS__)
132 		LINK_PLUGIN(CAMD)
133 		#endif
134 		#if defined(MACOSX)
135 		LINK_PLUGIN(COREAUDIO)
136 		LINK_PLUGIN(COREMIDI)
137 		#endif
138 		#ifdef USE_FLUIDSYNTH
139 		LINK_PLUGIN(FLUIDSYNTH)
140 		#endif
141 		#ifdef USE_MT32EMU
142 		LINK_PLUGIN(MT32)
143 		#endif
144 		#if defined(__ANDROID__)
145 		LINK_PLUGIN(EAS)
146 		#endif
147 		LINK_PLUGIN(ADLIB)
148 		LINK_PLUGIN(PCSPK)
149 		LINK_PLUGIN(PCJR)
150 		LINK_PLUGIN(CMS)
151 		#ifndef DISABLE_SID
152 		LINK_PLUGIN(C64)
153 		#endif
154 		LINK_PLUGIN(AMIGA)
155 		LINK_PLUGIN(APPLEIIGS)
156 		LINK_PLUGIN(TOWNS)
157 		LINK_PLUGIN(PC98)
158 		LINK_PLUGIN(SEGACD)
159 		#if defined(USE_TIMIDITY)
160 		LINK_PLUGIN(TIMIDITY)
161 		#endif
162 
163 		// Scaler plugins
164 		LINK_PLUGIN(NORMAL)
165 #ifdef USE_SCALERS
166 #ifdef USE_HQ_SCALERS
167 		LINK_PLUGIN(HQ)
168 #endif
169 #ifdef USE_EDGE_SCALERS
170 		LINK_PLUGIN(EDGE)
171 #endif
172 		LINK_PLUGIN(ADVMAME)
173 		LINK_PLUGIN(SAI)
174 		LINK_PLUGIN(SUPERSAI)
175 		LINK_PLUGIN(SUPEREAGLE)
176 		LINK_PLUGIN(PM)
177 		LINK_PLUGIN(DOTMATRIX)
178 		LINK_PLUGIN(TV)
179 #endif
180 
181 		return pl;
182 	}
183 };
184 
185 #ifdef DYNAMIC_MODULES
186 
getPlugins()187 PluginList FilePluginProvider::getPlugins() {
188 	PluginList pl;
189 
190 	// Prepare the list of directories to search
191 	Common::FSList pluginDirs;
192 
193 	// Add the default directories
194 	#ifndef WIN32
195 	pluginDirs.push_back(Common::FSNode("."));
196 	#endif
197 	pluginDirs.push_back(Common::FSNode("plugins"));
198 
199 	// Add the provider's custom directories
200 	addCustomDirectories(pluginDirs);
201 
202 	// Add the user specified directory
203 	Common::String pluginsPath(ConfMan.get("pluginspath"));
204 	if (!pluginsPath.empty())
205 		pluginDirs.push_back(Common::FSNode(pluginsPath));
206 
207 	Common::FSList::const_iterator dir;
208 	for (dir = pluginDirs.begin(); dir != pluginDirs.end(); ++dir) {
209 		// Load all plugins.
210 		// Scan for all plugins in this directory
211 		Common::FSList files;
212 		if (!dir->getChildren(files, Common::FSNode::kListFilesOnly)) {
213 			debug(1, "Couldn't open plugin directory '%s'", dir->getPath().c_str());
214 			continue;
215 		} else {
216 			debug(1, "Reading plugins from plugin directory '%s'", dir->getPath().c_str());
217 		}
218 
219 		for (Common::FSList::const_iterator i = files.begin(); i != files.end(); ++i) {
220 			if (isPluginFilename(*i)) {
221 				pl.push_back(createPlugin(*i));
222 			}
223 		}
224 	}
225 
226 	return pl;
227 }
228 
isPluginFilename(const Common::FSNode & node) const229 bool FilePluginProvider::isPluginFilename(const Common::FSNode &node) const {
230 	Common::String filename = node.getName();
231 
232 #ifdef PLUGIN_PREFIX
233 	// Check the plugin prefix
234 	if (!filename.hasPrefix(PLUGIN_PREFIX))
235 		return false;
236 #endif
237 
238 #ifdef PLUGIN_SUFFIX
239 	// Check the plugin suffix
240 	if (!filename.hasSuffix(PLUGIN_SUFFIX))
241 		return false;
242 #endif
243 
244 	return true;
245 }
246 
addCustomDirectories(Common::FSList & dirs) const247 void FilePluginProvider::addCustomDirectories(Common::FSList &dirs) const {
248 #ifdef PLUGIN_DIRECTORY
249 	dirs.push_back(Common::FSNode(PLUGIN_DIRECTORY));
250 #endif
251 }
252 
253 #endif // DYNAMIC_MODULES
254 
255 #pragma mark -
256 
257 PluginManager *PluginManager::_instance = NULL;
258 
instance()259 PluginManager &PluginManager::instance() {
260 	if (_instance)
261 		return *_instance;
262 
263 #if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES)
264 		_instance = new PluginManagerUncached();
265 #else
266 		_instance = new PluginManager();
267 #endif
268 	return *_instance;
269 }
270 
PluginManager()271 PluginManager::PluginManager() {
272 	// Always add the static plugin provider.
273 	addPluginProvider(new StaticPluginProvider());
274 }
275 
~PluginManager()276 PluginManager::~PluginManager() {
277 	// Explicitly unload all loaded plugins
278 	unloadAllPlugins();
279 
280 	// Delete the plugin providers
281 	for (ProviderList::iterator pp = _providers.begin();
282 	                            pp != _providers.end();
283 	                            ++pp) {
284 		delete *pp;
285 	}
286 }
287 
addPluginProvider(PluginProvider * pp)288 void PluginManager::addPluginProvider(PluginProvider *pp) {
289 	_providers.push_back(pp);
290 }
291 
getEngineFromMetaEngine(const Plugin * plugin)292 const Plugin *PluginManager::getEngineFromMetaEngine(const Plugin *plugin) {
293 	assert(plugin->getType() == PLUGIN_TYPE_ENGINE_DETECTION);
294 
295 	const Plugin *enginePlugin = nullptr;
296 
297 	// Use the engineID from MetaEngine for comparison.
298 	Common::String metaEnginePluginName = plugin->getEngineId();
299 
300 	enginePlugin = PluginMan.findEnginePlugin(metaEnginePluginName);
301 
302 	if (enginePlugin) {
303 		debug(9, "MetaEngine: %s \t matched to \t Engine: %s", plugin->getName(), enginePlugin->getFileName());
304 		return enginePlugin;
305 	}
306 
307 	debug(9, "MetaEngine: %s couldn't find a match for an engine plugin.", plugin->getName());
308 	return nullptr;
309 }
310 
getMetaEngineFromEngine(const Plugin * plugin)311 const Plugin *PluginManager::getMetaEngineFromEngine(const Plugin *plugin) {
312 	assert(plugin->getType() == PLUGIN_TYPE_ENGINE);
313 
314 	const Plugin *metaEngine = nullptr;
315 
316 	PluginList pl = PluginMan.getPlugins(PLUGIN_TYPE_ENGINE_DETECTION);
317 
318 	// This will return a name of the Engine plugin, which will be identical to
319 	// a getEngineID from a relevant MetaEngine.
320 	Common::String enginePluginName(plugin->getName());
321 
322 	for (PluginList::const_iterator itr = pl.begin(); itr != pl.end(); itr++) {
323 		Common::String metaEngineName = (*itr)->getEngineId();
324 
325 		if (metaEngineName.equalsIgnoreCase(enginePluginName)) {
326 			metaEngine = (*itr);
327 			break;
328 		}
329 	}
330 
331 	if (metaEngine) {
332 		debug(9, "Engine: %s matched to MetaEngine: %s", plugin->getFileName(), metaEngine->getName());
333 		return metaEngine;
334 	}
335 
336 	debug(9, "Engine: %s couldn't find a match for an MetaEngine plugin.", plugin->getFileName());
337 	return nullptr;
338 }
339 
340 /**
341  * This should only be called once by main()
342  **/
init()343 void PluginManagerUncached::init() {
344 	unloadAllPlugins();
345 	_allEnginePlugins.clear();
346 	ConfMan.setBool("always_run_fallback_detection_extern", false);
347 
348 	unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // empty the engine plugins
349 
350 #ifndef DETECTION_STATIC
351 	Common::String detectPluginName = "detection";
352 #ifdef PLUGIN_SUFFIX
353 	detectPluginName += PLUGIN_SUFFIX;
354 #endif
355 
356 	bool foundDetectPlugin = false;
357 #endif
358 
359 	for (ProviderList::iterator pp = _providers.begin();
360 	                            pp != _providers.end();
361 	                            ++pp) {
362 		PluginList pl((*pp)->getPlugins());
363 
364 		for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) {
365 			// This is a 'hack' based on the assumption that we have no sound
366 			// file plugins. Currently this is the case. If it changes, we
367 			// should find a fast way of detecting whether a plugin is a
368 			// music or an engine plugin.
369 #ifndef DETECTION_STATIC
370 			if (!foundDetectPlugin && (*pp)->isFilePluginProvider()) {
371 				Common::String pName = (*p)->getFileName();
372 				if (pName.hasSuffixIgnoreCase(detectPluginName)) {
373 					_detectionPlugin = (*p);
374 					foundDetectPlugin = true;
375 					debug(9, "Detection plugin found!");
376 					continue;
377 				}
378 			}
379 #endif
380 
381 			if ((*pp)->isFilePluginProvider()) {
382 				_allEnginePlugins.push_back(*p);
383 			} else if ((*p)->loadPlugin()) { // and this is the proper method
384 				if ((*p)->getType() == PLUGIN_TYPE_ENGINE) {
385 					(*p)->unloadPlugin();
386 					_allEnginePlugins.push_back(*p);
387 				} else {	// add non-engine plugins to the 'in-memory' list
388 							// these won't ever get unloaded
389 					addToPluginsInMemList(*p);
390 				}
391 			}
392  		}
393  	}
394 }
395 
396 /**
397  * Try to load the plugin by searching in the ConfigManager for a matching
398  * engine ID under the domain 'engine_plugin_files'.
399  **/
loadPluginFromEngineId(const Common::String & engineId)400 bool PluginManagerUncached::loadPluginFromEngineId(const Common::String &engineId) {
401 	Common::ConfigManager::Domain *domain = ConfMan.getDomain("engine_plugin_files");
402 
403 	if (domain) {
404 		if (domain->contains(engineId)) {
405 			Common::String filename = (*domain)[engineId];
406 
407 			if (loadPluginByFileName(filename)) {
408 				return true;
409 			}
410 		}
411 	}
412 	// Check for a plugin with the same name as the engine before starting
413 	// to scan all plugins
414 	Common::String tentativeEnginePluginFilename = engineId;
415 #ifdef PLUGIN_SUFFIX
416 	tentativeEnginePluginFilename += PLUGIN_SUFFIX;
417 #endif
418 	for (PluginList::iterator p = _allEnginePlugins.begin(); p != _allEnginePlugins.end(); ++p) {
419 		Common::String filename = (*p)->getFileName();
420 		if (filename.hasSuffixIgnoreCase(tentativeEnginePluginFilename)) {
421 			if (loadPluginByFileName(filename)) {
422 				return true;
423 			}
424 		}
425 	}
426 	return false;
427 }
428 
429 /**
430  * Load a plugin with a filename taken from ConfigManager.
431  **/
loadPluginByFileName(const Common::String & filename)432 bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) {
433 	if (filename.empty())
434 		return false;
435 
436 	unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
437 
438 	PluginList::iterator i;
439 	for (i = _allEnginePlugins.begin(); i != _allEnginePlugins.end(); ++i) {
440 		if (Common::String((*i)->getFileName()) == filename && (*i)->loadPlugin()) {
441 			addToPluginsInMemList(*i);
442 			_currentPlugin = i;
443 			return true;
444 		}
445 	}
446 	return false;
447 }
448 
449 /**
450  * Update the config manager with a plugin file name that we found can handle
451  * the engine.
452  **/
updateConfigWithFileName(const Common::String & engineId)453 void PluginManagerUncached::updateConfigWithFileName(const Common::String &engineId) {
454 	// Check if we have a filename for the current plugin
455 	if ((*_currentPlugin)->getFileName()) {
456 		if (!ConfMan.hasMiscDomain("engine_plugin_files"))
457 			ConfMan.addMiscDomain("engine_plugin_files");
458 
459 		Common::ConfigManager::Domain *domain = ConfMan.getDomain("engine_plugin_files");
460 		assert(domain);
461 		(*domain).setVal(engineId, (*_currentPlugin)->getFileName());
462 
463 		ConfMan.flushToDisk();
464 	}
465 }
466 
467 #ifndef DETECTION_STATIC
loadDetectionPlugin()468 void PluginManagerUncached::loadDetectionPlugin() {
469 	bool linkMetaEngines = false;
470 
471 	if (_isDetectionLoaded) {
472 		debug(9, "Detection plugin is already loaded. Adding each available engines to the memory.");
473 		linkMetaEngines = true;
474 	} else {
475 		if (_detectionPlugin) {
476 			if (_detectionPlugin->loadPlugin()) {
477 				assert((_detectionPlugin)->getType() == PLUGIN_TYPE_DETECTION);
478 
479 				linkMetaEngines = true;
480 				_isDetectionLoaded = true;
481 			} else {
482 				debug(9, "Detection plugin was not loaded correctly.");
483 				return;
484 			}
485 		} else {
486 			debug(9, "Detection plugin not found.");
487 			return;
488 		}
489 	}
490 
491 	if (linkMetaEngines) {
492 		_pluginsInMem[PLUGIN_TYPE_ENGINE_DETECTION].clear();
493 		const Detection &detectionConnect = _detectionPlugin->get<Detection>();
494 		const PluginList &pl = detectionConnect.getPlugins();
495 		Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManagerUncached::tryLoadPlugin), this));
496 	}
497 
498 }
499 
unloadDetectionPlugin()500 void PluginManagerUncached::unloadDetectionPlugin() {
501 	if (_isDetectionLoaded) {
502 		_pluginsInMem[PLUGIN_TYPE_ENGINE_DETECTION].clear();
503 		_detectionPlugin->unloadPlugin();
504 		_isDetectionLoaded = false;
505 	} else {
506 		debug(9, "Detection plugin is already unloaded.");
507 	}
508 }
509 #endif
510 
loadFirstPlugin()511 void PluginManagerUncached::loadFirstPlugin() {
512 	unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
513 
514 	// let's try to find one we can load
515 	for (_currentPlugin = _allEnginePlugins.begin(); _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
516 		if ((*_currentPlugin)->loadPlugin()) {
517 			addToPluginsInMemList(*_currentPlugin);
518 			break;
519 		}
520 	}
521 }
522 
loadNextPlugin()523 bool PluginManagerUncached::loadNextPlugin() {
524 	unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false);
525 
526 	if (!_currentPlugin || _currentPlugin == _allEnginePlugins.end())
527 		return false;
528 
529 	for (++_currentPlugin; _currentPlugin != _allEnginePlugins.end(); ++_currentPlugin) {
530 		if ((*_currentPlugin)->loadPlugin()) {
531 			addToPluginsInMemList(*_currentPlugin);
532 			return true;
533 		}
534 	}
535 	return false; // no more in list
536 }
537 
538 /**
539  * Used by only the cached plugin manager. The uncached manager can only have
540  * one plugin in memory at a time.
541  **/
loadAllPlugins()542 void PluginManager::loadAllPlugins() {
543 	for (ProviderList::iterator pp = _providers.begin();
544 	                            pp != _providers.end();
545 	                            ++pp) {
546 		PluginList pl((*pp)->getPlugins());
547 		Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
548 	}
549 
550 #ifndef DETECTION_STATIC
551 	/*
552 	 * When detection is dynamic, loading above only gets us a PLUGIN_TYPE_DETECTION plugin
553 	 * We must register all plugins linked in it in order to use them
554 	 */
555 	PluginList dpl = getPlugins(PLUGIN_TYPE_DETECTION);
556 	_pluginsInMem[PLUGIN_TYPE_ENGINE_DETECTION].clear();
557 	for (PluginList::iterator it = dpl.begin();
558 	                            it != dpl.end();
559 	                            ++it) {
560 		const Detection &detectionConnect = (*it)->get<Detection>();
561 		const PluginList &pl = detectionConnect.getPlugins();
562 		Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this));
563 	}
564 #endif
565 }
566 
loadAllPluginsOfType(PluginType type)567 void PluginManager::loadAllPluginsOfType(PluginType type) {
568 	for (ProviderList::iterator pp = _providers.begin();
569 	                            pp != _providers.end();
570 	                            ++pp) {
571 		PluginList pl((*pp)->getPlugins());
572 		for (PluginList::iterator p = pl.begin();
573 				                  p != pl.end();
574 								  ++p) {
575 			if ((*p)->loadPlugin()) {
576 				if ((*p)->getType() == type) {
577 					addToPluginsInMemList((*p));
578 				} else {
579 					// Plugin is wrong type
580 					(*p)->unloadPlugin();
581 					delete (*p);
582 				}
583 			} else {
584 				// Plugin did not load
585 				delete (*p);
586 			}
587 		}
588 	}
589 }
590 
unloadAllPlugins()591 void PluginManager::unloadAllPlugins() {
592 	for (int i = 0; i < PLUGIN_TYPE_MAX; i++)
593 		unloadPluginsExcept((PluginType)i, NULL);
594 }
595 
unloadPluginsExcept(PluginType type,const Plugin * plugin,bool deletePlugin)596 void PluginManager::unloadPluginsExcept(PluginType type, const Plugin *plugin, bool deletePlugin /*=true*/) {
597 	Plugin *found = NULL;
598 	for (PluginList::iterator p = _pluginsInMem[type].begin(); p != _pluginsInMem[type].end(); ++p) {
599 		if (*p == plugin) {
600 			found = *p;
601 		} else {
602 			(*p)->unloadPlugin();
603 			if (deletePlugin) {
604 				delete *p;
605 			}
606 		}
607 	}
608 	_pluginsInMem[type].clear();
609 	if (found != NULL) {
610 		_pluginsInMem[type].push_back(found);
611 	}
612 }
613 
614 /*
615  * Used only by the cached plugin manager since it deletes the plugin.
616  */
tryLoadPlugin(Plugin * plugin)617 bool PluginManager::tryLoadPlugin(Plugin *plugin) {
618 	assert(plugin);
619 	// Try to load the plugin
620 	if (plugin->loadPlugin()) {
621 		addToPluginsInMemList(plugin);
622 		return true;
623 	} else {
624 		// Failed to load the plugin
625 		delete plugin;
626 		return false;
627 	}
628 }
629 
630 /**
631  * Add to the list of plugins loaded in memory.
632  */
addToPluginsInMemList(Plugin * plugin)633 void PluginManager::addToPluginsInMemList(Plugin *plugin) {
634 	bool found = false;
635 	// The plugin is valid, see if it provides the same module as an
636 	// already loaded one and should replace it.
637 
638 	PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin();
639 	while (!found && pl != _pluginsInMem[plugin->getType()].end()) {
640 		if (!strcmp(plugin->getName(), (*pl)->getName())) {
641 			// Found a duplicated module. Replace the old one.
642 			found = true;
643 			(*pl)->unloadPlugin();
644 			delete *pl;
645 			*pl = plugin;
646 			debug(1, "Replaced the duplicated plugin: '%s'", plugin->getName());
647 		}
648 		pl++;
649 	}
650 
651 	if (!found) {
652 		// If it provides a new module, just add it to the list of known plugins in memory.
653 		_pluginsInMem[plugin->getType()].push_back(plugin);
654 	}
655 }
656 
657 // Engine plugins
658 
659 #include "engines/metaengine.h"
660 
661 namespace Common {
662 DECLARE_SINGLETON(EngineManager);
663 }
664 
665 /**
666  * This function works for both cached and uncached PluginManagers.
667  * For the cached version, most of the logic here will short circuit.
668  *
669  * For the uncached version, we first try to find the plugin using the engineId
670  * and only if we can't find it there, we loop through the plugins.
671  **/
findGamesMatching(const Common::String & engineId,const Common::String & gameId) const672 QualifiedGameList EngineManager::findGamesMatching(const Common::String &engineId, const Common::String &gameId) const {
673 	QualifiedGameList results;
674 
675 	if (!engineId.empty()) {
676 		// If we got an engine name, look for THE game only in that engine
677 		const Plugin *p = EngineMan.findPlugin(engineId);
678 		if (p) {
679 			const MetaEngineDetection &engine = p->get<MetaEngineDetection>();
680 			DebugMan.addAllDebugChannels(engine.getDebugChannels());
681 
682 			PlainGameDescriptor pluginResult = engine.findGame(gameId.c_str());
683 			if (pluginResult.gameId) {
684 				results.push_back(QualifiedGameDescriptor(engine.getEngineId(), pluginResult));
685 			}
686 		}
687 	} else {
688 		// This is a slow path, we have to scan the list of plugins
689 		PluginMan.loadFirstPlugin();
690 		do {
691 			results.push_back(findGameInLoadedPlugins(gameId));
692 		} while (PluginMan.loadNextPlugin());
693 	}
694 
695 	return results;
696 }
697 
698 /**
699  * Find the game within the plugins loaded in memory
700  **/
findGameInLoadedPlugins(const Common::String & gameId) const701 QualifiedGameList EngineManager::findGameInLoadedPlugins(const Common::String &gameId) const {
702 	// Find the GameDescriptor for this target
703 	const PluginList &plugins = getPlugins();
704 
705 	QualifiedGameList results;
706 	PluginList::const_iterator iter;
707 
708 	for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
709 		const MetaEngineDetection &engine = (*iter)->get<MetaEngineDetection>();
710 		DebugMan.addAllDebugChannels(engine.getDebugChannels());
711 		PlainGameDescriptor pluginResult = engine.findGame(gameId.c_str());
712 
713 		if (pluginResult.gameId) {
714 			results.push_back(QualifiedGameDescriptor(engine.getEngineId(), pluginResult));
715 		}
716 	}
717 
718 	return results;
719 }
720 
detectGames(const Common::FSList & fslist) const721 DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
722 	DetectedGames candidates;
723 	PluginList plugins;
724 	PluginList::const_iterator iter;
725 
726 	// MetaEngines are always loaded into memory, so, get them and
727 	// run detection for all of them.
728 	plugins = getPlugins(PLUGIN_TYPE_ENGINE_DETECTION);
729 
730 	// Clear md5 cache before each detection starts, just in case.
731 	MD5Man.clear();
732 
733 	// Iterate over all known games and for each check if it might be
734 	// the game in the presented directory.
735 	for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
736 		const MetaEngineDetection &metaEngine = (*iter)->get<MetaEngineDetection>();
737 		// set the debug flags
738 		DebugMan.addAllDebugChannels(metaEngine.getDebugChannels());
739 		DetectedGames engineCandidates = metaEngine.detectGames(fslist);
740 
741 		for (uint i = 0; i < engineCandidates.size(); i++) {
742 			engineCandidates[i].path = fslist.begin()->getParent().getPath();
743 			engineCandidates[i].shortPath = fslist.begin()->getParent().getDisplayName();
744 			candidates.push_back(engineCandidates[i]);
745 		}
746 	}
747 
748 	return DetectionResults(candidates);
749 }
750 
getPlugins(const PluginType fetchPluginType) const751 const PluginList &EngineManager::getPlugins(const PluginType fetchPluginType) const {
752 	return PluginManager::instance().getPlugins(fetchPluginType);
753 }
754 
755 namespace {
756 
addStringToConf(const Common::String & key,const Common::String & value,const Common::String & domain)757 void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
758 	if (!value.empty())
759 		ConfMan.set(key, value, domain);
760 }
761 
762 } // End of anonymous namespace
763 
createTargetForGame(const DetectedGame & game)764 Common::String EngineManager::createTargetForGame(const DetectedGame &game) {
765 	// The auto detector or the user made a choice.
766 	// Pick a domain name which does not yet exist (after all, we
767 	// are *adding* a game to the config, not replacing).
768 	Common::String domain = game.preferredTarget;
769 
770 	assert(!domain.empty());
771 	if (ConfMan.hasGameDomain(domain)) {
772 		int suffixN = 1;
773 		Common::String gameid(domain);
774 
775 		while (ConfMan.hasGameDomain(domain)) {
776 			domain = gameid + Common::String::format("-%d", suffixN);
777 			suffixN++;
778 		}
779 	}
780 
781 	// Add the name domain
782 	ConfMan.addGameDomain(domain);
783 
784 	// Copy all non-empty relevant values into the new domain
785 	addStringToConf("engineid", game.engineId, domain);
786 	addStringToConf("gameid", game.gameId, domain);
787 	addStringToConf("description", game.description, domain);
788 	addStringToConf("language", Common::getLanguageCode(game.language), domain);
789 	addStringToConf("platform", Common::getPlatformCode(game.platform), domain);
790 	addStringToConf("path", game.path, domain);
791 	addStringToConf("extra", game.extra, domain);
792 	addStringToConf("guioptions", game.getGUIOptions(), domain);
793 
794 	// Add any extra configuration keys
795 	for (Common::StringMap::iterator i = game._extraConfigEntries.begin();
796 			i != game._extraConfigEntries.end(); ++i)
797 		addStringToConf((*i)._key, (*i)._value, domain);
798 
799 	// TODO: Setting the description field here has the drawback
800 	// that the user does never notice when we upgrade our descriptions.
801 	// It might be nice to leave this field empty, and only set it to
802 	// a value when the user edits the description string.
803 	// However, at this point, that's impractical. Once we have a method
804 	// to query all backends for the proper & full description of a given
805 	// game target, we can change this (currently, you can only query
806 	// for the generic gameid description; it's not possible to obtain
807 	// a description which contains extended information like language, etc.).
808 
809 	return domain;
810 }
811 
findPlugin(const Common::String & engineId) const812 const Plugin *EngineManager::findPlugin(const Common::String &engineId) const {
813 	const PluginList &plugins = getPlugins();
814 
815 	for (PluginList::const_iterator iter = plugins.begin(); iter != plugins.end(); iter++)
816 		if (engineId == (*iter)->get<MetaEngineDetection>().getEngineId())
817 			return *iter;
818 
819 	return 0;
820 }
821 
findLoadedPlugin(const Common::String & engineId)822 const Plugin *PluginManager::findLoadedPlugin(const Common::String &engineId) {
823 	const PluginList &plugins = getPlugins(PLUGIN_TYPE_ENGINE);
824 
825 	for (PluginList::const_iterator iter = plugins.begin(); iter != plugins.end(); iter++)
826 		if (engineId == (*iter)->get<MetaEngine>().getName())
827 			return *iter;
828 
829 	return 0;
830 }
831 
findEnginePlugin(const Common::String & engineId)832 const Plugin *PluginManager::findEnginePlugin(const Common::String &engineId) {
833 	// First look for the game using the plugins in memory. This is critical
834 	// for calls coming from inside games
835 	const Plugin *plugin = findLoadedPlugin(engineId);
836 	if (plugin)
837 		return plugin;
838 
839 	// Now look for the plugin using the engine ID. This is much faster than scanning plugin
840 	// by plugin
841 	if (loadPluginFromEngineId(engineId))  {
842 		plugin = findLoadedPlugin(engineId);
843 		if (plugin)
844 			return plugin;
845 	}
846 
847 	// We failed to find it using the engine ID. Scan the list of plugins
848 	PluginMan.loadFirstPlugin();
849 	do {
850 		plugin = findLoadedPlugin(engineId);
851 		if (plugin) {
852 			// Update with new plugin file name
853 			PluginMan.updateConfigWithFileName(engineId);
854 			return plugin;
855 		}
856 	} while (PluginMan.loadNextPlugin());
857 
858 	return 0;
859 }
860 
findTarget(const Common::String & target,const Plugin ** plugin) const861 QualifiedGameDescriptor EngineManager::findTarget(const Common::String &target, const Plugin **plugin) const {
862 	// Ignore empty targets
863 	if (target.empty())
864 		return QualifiedGameDescriptor();
865 
866 	// Lookup the domain. If we have no domain, fallback on the old function [ultra-deprecated].
867 	const Common::ConfigManager::Domain *domain = ConfMan.getDomain(target);
868 	if (!domain || !domain->contains("gameid") || !domain->contains("engineid"))
869 		return QualifiedGameDescriptor();
870 
871 	// Look for the engine ID
872 	const Plugin *foundPlugin = findPlugin(domain->getVal("engineid"));
873 	if (!foundPlugin) {
874 		return QualifiedGameDescriptor();
875 	}
876 
877 	// Make sure it does support the game ID
878 	const MetaEngineDetection &engine = foundPlugin->get<MetaEngineDetection>();
879 	DebugMan.addAllDebugChannels(engine.getDebugChannels());
880 	PlainGameDescriptor desc = engine.findGame(domain->getVal("gameid").c_str());
881 	if (!desc.gameId) {
882 		return QualifiedGameDescriptor();
883 	}
884 
885 	if (plugin)
886 		*plugin = foundPlugin;
887 
888 	return QualifiedGameDescriptor(engine.getEngineId(), desc);
889 }
890 
upgradeTargetIfNecessary(const Common::String & target) const891 void EngineManager::upgradeTargetIfNecessary(const Common::String &target) const {
892 	Common::ConfigManager::Domain *domain = ConfMan.getDomain(target);
893 	assert(domain);
894 
895 	if (!domain->contains("engineid")) {
896 		upgradeTargetForEngineId(target);
897 	} else {
898 		if (domain->getVal("engineid").equals("fullpipe")) {
899 			domain->setVal("engineid", "ngi");
900 
901 			debug("Upgrading engineid from 'fullpipe' to 'ngi'");
902 
903 			ConfMan.flushToDisk();
904 		}
905 	}
906 }
907 
upgradeTargetForEngineId(const Common::String & target) const908 void EngineManager::upgradeTargetForEngineId(const Common::String &target) const {
909 	Common::ConfigManager::Domain *domain = ConfMan.getDomain(target);
910 	assert(domain);
911 
912 	debug("Target '%s' lacks an engine ID, upgrading...", target.c_str());
913 
914 	Common::String oldGameId = domain->getVal("gameid");
915 	Common::String path = domain->getVal("path");
916 
917 	// At this point the game ID and game path must be known
918 	if (oldGameId.empty()) {
919 		warning("The game ID is required to upgrade target '%s'", target.c_str());
920 		return;
921 	}
922 	if (path.empty()) {
923 		warning("The game path is required to upgrade target '%s'", target.c_str());
924 		return;
925 	}
926 
927 	// Game descriptor for the upgraded target
928 	Common::String engineId;
929 	Common::String newGameId;
930 
931 	// First, try to update entries for engines that previously used the "single id" system
932 	// Search for an engine whose ID is the game ID
933 	const Plugin *plugin = findPlugin(oldGameId);
934 	if (plugin) {
935 		// Run detection on the game path
936 		Common::FSNode dir(path);
937 		Common::FSList files;
938 		if (!dir.getChildren(files, Common::FSNode::kListAll)) {
939 			warning("Failed to access path '%s' when upgrading target '%s'", path.c_str(), target.c_str());
940 			return;
941 		}
942 
943 		// Take the first detection entry
944 		const MetaEngineDetection &metaEngine = plugin->get<MetaEngineDetection>();
945 		// set debug flags before call detectGames
946 		DebugMan.addAllDebugChannels(metaEngine.getDebugChannels());
947 		DetectedGames candidates = metaEngine.detectGames(files);
948 		if (candidates.empty()) {
949 			warning("No games supported by the engine '%s' were found in path '%s' when upgrading target '%s'",
950 			        metaEngine.getEngineId(), path.c_str(), target.c_str());
951 			return;
952 		}
953 
954 		engineId = candidates[0].engineId;
955 		newGameId = candidates[0].gameId;
956 	}
957 
958 	// Next, try to find an engine with the game ID in its supported games list
959 	if (engineId.empty()) {
960 		QualifiedGameList candidates = findGamesMatching("", oldGameId);
961 		if (candidates.size() > 1) {
962 			warning("Multiple matching engines were found when upgrading target '%s'", target.c_str());
963 			return;
964 		} else if (!candidates.empty()) {
965 			engineId = candidates[0].engineId;
966 			newGameId = candidates[0].gameId;
967 		}
968 	}
969 
970 	if (engineId.empty() || newGameId.empty()) {
971 		warning("No matching engine was found when upgrading target '%s'", target.c_str());
972 		return;
973 	}
974 
975 	domain->setVal("engineid", engineId);
976 	domain->setVal("gameid", newGameId);
977 
978 	// Save a backup of the pre-upgrade gameId to the config file
979 	if (newGameId != oldGameId) {
980 		domain->setVal("oldgameid", oldGameId);
981 	}
982 
983 	debug("Upgrade complete (engine ID '%s', game ID '%s')", engineId.c_str(), newGameId.c_str());
984 
985 	ConfMan.flushToDisk();
986 }
987 
988 // Music plugins
989 
990 #include "audio/musicplugin.h"
991 
992 namespace Common {
993 DECLARE_SINGLETON(MusicManager);
994 }
995 
getPlugins() const996 const PluginList &MusicManager::getPlugins() const {
997 	return PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
998 }
999 
1000 // Scaler plugins
1001 
1002 #include "graphics/scalerplugin.h"
1003 
1004 namespace Common {
1005 DECLARE_SINGLETON(ScalerManager);
1006 }
1007 
getPlugins() const1008 const PluginList &ScalerManager::getPlugins() const {
1009 	return PluginManager::instance().getPlugins(PLUGIN_TYPE_SCALER);
1010 }
1011 
getMaxExtraPixels() const1012 uint ScalerManager::getMaxExtraPixels() const {
1013 	uint maxPixels = 0;
1014 	PluginList plugins = getPlugins();
1015 	PluginList::iterator i = plugins.begin();
1016 	for (; i != plugins.end(); ++i) {
1017 		uint n = (*i)->get<ScalerPluginObject>().extraPixels();
1018 		if (n > maxPixels) {
1019 			maxPixels = n;
1020 		}
1021 	}
1022 	return maxPixels;
1023 }
1024 
findScalerPlugin(const char * name) const1025 Plugin *ScalerManager::findScalerPlugin(const char *name) const {
1026 	const PluginList &plugins = getPlugins();
1027 	for (PluginList::const_iterator i = plugins.begin(); i != plugins.end(); ++i) {
1028 		if (!strcmp((*i)->get<ScalerPluginObject>().getName(), name)) {
1029 			return *i;
1030 		}
1031 	}
1032 
1033 	return 0;
1034 }
1035 
findScalerPluginIndex(const char * name) const1036 uint ScalerManager::findScalerPluginIndex(const char *name) const {
1037 	const PluginList &plugins = getPlugins();
1038 	uint index = 0;
1039 
1040 	for (PluginList::const_iterator i = plugins.begin(); i != plugins.end(); ++i) {
1041 		if (!strcmp((*i)->get<ScalerPluginObject>().getName(), name)) {
1042 			return index;
1043 		}
1044 		index++;
1045 	}
1046 
1047 	return 0;
1048 }
1049 
1050 struct LegacyGraphicsMode {
1051 	const char *oldName;
1052 	const char *newName;
1053 	uint factor;
1054 };
1055 
1056 // Table for using old names for scalers in the configuration
1057 // to keep compatibiblity with old config files.
1058 static const LegacyGraphicsMode s_legacyGraphicsModes[] = {
1059 	{ "1x", "normal", 1 },
1060 	{ "2x", "normal", 2 },
1061 	{ "3x", "normal", 3 },
1062 	{ "normal1x", "normal", 1 },
1063 	{ "normal2x", "normal", 2 },
1064 	{ "normal3x", "normal", 3 },
1065 	{ "normal4x", "normal", 4 },
1066 	{ "hq2x", "hq", 2 },
1067 	{ "hq3x", "hq", 3 },
1068 	{ "edge2x", "edge", 2 },
1069 	{ "edge3x", "edge", 3 },
1070 	{ "advmame2x", "advmame", 2 },
1071 	{ "advmame3x", "advmame", 3 },
1072 	{ "advmame4x", "advmame", 4 },
1073 	{ "2xsai", "sai", 2 },
1074 	{ "sai2x", "sai", 2 },
1075 	{ "super2xsai", "supersai", 2 },
1076 	{ "supersai2x", "supersai", 2 },
1077 	{ "supereagle", "supereagle", 2 },
1078 	{ "supereagle2x", "supereagle", 2 },
1079 	{ "pm2x", "pm", 2 },
1080 	{ "dotmatrix", "dotmatrix", 2 },
1081 	{ "dotmatrix2x", "dotmatrix", 2 },
1082 	{ "tv2x", "tv", 2 }
1083 };
1084 
updateOldSettings()1085 void ScalerManager::updateOldSettings() {
1086 	// Search for legacy gfx_mode and replace it
1087 	if (ConfMan.hasKey("gfx_mode")) {
1088 		Common::String gfxMode(ConfMan.get("gfx_mode"));
1089 		for (uint i = 0; i < ARRAYSIZE(s_legacyGraphicsModes); ++i) {
1090 			if (gfxMode == s_legacyGraphicsModes[i].oldName) {
1091 				ConfMan.set("scaler", s_legacyGraphicsModes[i].newName);
1092 				ConfMan.setInt("scale_factor", s_legacyGraphicsModes[i].factor);
1093 				break;
1094 			}
1095 		}
1096 	}
1097 
1098 	// Look in all game domains as well
1099 	for (Common::ConfigManager::DomainMap::iterator domain = ConfMan.beginGameDomains(); domain != ConfMan.endGameDomains(); ++domain) {
1100 		if (domain->_value.contains("gfx_mode")) {
1101 			Common::String gfxMode(domain->_value.getVal("gfx_mode"));
1102 			for (uint i = 0; i < ARRAYSIZE(s_legacyGraphicsModes); ++i) {
1103 				if (gfxMode == s_legacyGraphicsModes[i].oldName) {
1104 					warning("%s: %s -> %s@%dx", domain->_value.getDomainComment().c_str(), s_legacyGraphicsModes[i].oldName, s_legacyGraphicsModes[i].newName, s_legacyGraphicsModes[i].factor);
1105 					domain->_value.setVal("scaler", s_legacyGraphicsModes[i].newName);
1106 					domain->_value.setVal("scale_factor", Common::String::format("%i", s_legacyGraphicsModes[i].factor));
1107 					domain->_value.erase("gfx_mode");
1108 					break;
1109 				}
1110 			}
1111 		}
1112 	}
1113 }
1114