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