Permalink
Browse files

BASE: Remove bad casts between incompatible Plugin types

Previously, a C-style cast was used to convert a
Common::Array<Plugin *>, populated with pointers to StaticPlugin
and DynamicPlugin instances, to a
Common::Array<PluginSubclass<T> *>, but PluginSubclass<T> is a
*sibling* class to StaticPlugin/DynamicPlugin, so this cast was
invalid and the results undefined. The methods for retrieving
subclasses of plugins can't be easily changed to just generate an
array of temporary wrapper objects that expose an identical API
which dereferences to the preferred PluginObject subclass because
pointers to these objects are retained by other parts of ScummVM,
so the wrappers would needed to be persisted or they would need to
just re-expose the underlying Plugin object again. This indicated
that a way to solve this problem is to have the callers receive
Plugin objects and get the PluginObject from the Plugin by
explicitly stating their desired type, in a similar manner to
std::get(std::variant), so that the pattern used by this patch to
solve the problem.

Closes gh-1051.
  • Loading branch information...
csnover committed Nov 11, 2017
1 parent 55855ca commit d087c9605ffffdc9053c5efdc83f53658afbe9a6
View
@@ -92,9 +92,9 @@ MusicType MidiDriver::getMusicType(MidiDriver::DeviceHandle handle) {
return MT_MT32;
if (handle) {
const MusicPlugin::List p = MusicMan.getPlugins();
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (**m)->getDevices();
const PluginList p = MusicMan.getPlugins();
for (PluginList::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); d++) {
if (handle == d->getHandle())
return d->getMusicType();
@@ -107,9 +107,9 @@ MusicType MidiDriver::getMusicType(MidiDriver::DeviceHandle handle) {
Common::String MidiDriver::getDeviceString(DeviceHandle handle, DeviceStringType type) {
if (handle) {
const MusicPlugin::List p = MusicMan.getPlugins();
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (**m)->getDevices();
const PluginList p = MusicMan.getPlugins();
for (PluginList::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); d++) {
if (handle == d->getHandle()) {
if (type == kDriverName)
@@ -226,7 +226,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
// If the selected driver did not match the flags setting,
// we try to determine a suitable and "optimal" music driver.
const MusicPlugin::List p = MusicMan.getPlugins();
const PluginList p = MusicMan.getPlugins();
// If only MDT_MIDI but not MDT_PREFER_MT32 or MDT_PREFER_GM is set we prefer the other devices (which will always be
// detected since they are hard coded and cannot be disabled).
bool skipMidi = !(flags & (MDT_PREFER_GM | MDT_PREFER_MT32));
@@ -280,8 +280,8 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
// and there is no preferred MT32 or GM device selected either or if the detected device is unavailable we arrive here.
// If MT32 is preferred we try for the first available device with music type 'MT_MT32' (usually the mt32 emulator).
if (flags & MDT_PREFER_MT32) {
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (PluginList::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
if (d->getMusicType() == MT_MT32) {
hdl = d->getHandle();
@@ -295,8 +295,8 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
// Now we default to the first available device with music type 'MT_GM' if not
// MT-32 is preferred or if MT-32 is preferred but all other devices have failed.
if (!(flags & MDT_PREFER_MT32) || flags == (MDT_PREFER_MT32 | MDT_MIDI)) {
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (PluginList::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
if (d->getMusicType() == MT_GM || d->getMusicType() == MT_GS) {
hdl = d->getHandle();
@@ -348,8 +348,8 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
tp = MT_AUTO;
}
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (PluginList::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
if (d->getMusicType() == tp) {
hdl = d->getHandle();
@@ -365,33 +365,35 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
MidiDriver *MidiDriver::createMidi(MidiDriver::DeviceHandle handle) {
MidiDriver *driver = 0;
const MusicPlugin::List p = MusicMan.getPlugins();
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); m++) {
if (getDeviceString(handle, MidiDriver::kDriverId).equals((**m)->getId()))
(**m)->createInstance(&driver, handle);
const PluginList p = MusicMan.getPlugins();
for (PluginList::const_iterator m = p.begin(); m != p.end(); m++) {
const MusicPluginObject &musicPlugin = (*m)->get<MusicPluginObject>();
if (getDeviceString(handle, MidiDriver::kDriverId).equals(musicPlugin.getId()))
musicPlugin.createInstance(&driver, handle);
}
return driver;
}
bool MidiDriver::checkDevice(MidiDriver::DeviceHandle handle) {
const MusicPlugin::List p = MusicMan.getPlugins();
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); m++) {
if (getDeviceString(handle, MidiDriver::kDriverId).equals((**m)->getId()))
return (**m)->checkDevice(handle);
const PluginList p = MusicMan.getPlugins();
for (PluginList::const_iterator m = p.begin(); m != p.end(); m++) {
const MusicPluginObject &musicPlugin = (*m)->get<MusicPluginObject>();
if (getDeviceString(handle, MidiDriver::kDriverId).equals(musicPlugin.getId()))
return musicPlugin.checkDevice(handle);
}
return false;
}
MidiDriver::DeviceHandle MidiDriver::getDeviceHandle(const Common::String &identifier) {
const MusicPlugin::List p = MusicMan.getPlugins();
const PluginList p = MusicMan.getPlugins();
if (p.begin() == p.end())
error("MidiDriver::getDeviceHandle: Music plugins must be loaded prior to calling this method");
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (**m)->getDevices();
for (PluginList::const_iterator m = p.begin(); m != p.end(); m++) {
MusicDevices i = (*m)->get<MusicPluginObject>().getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); d++) {
// The music driver id isn't unique, but it will match
// driver's first device. This is useful when selecting
View
@@ -112,11 +112,6 @@ class MusicPluginObject : public PluginObject {
virtual Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const = 0;
};
// Music plugins
typedef PluginSubclass<MusicPluginObject> MusicPlugin;
/**
* Singleton class which manages all Music plugins.
*/
@@ -125,7 +120,7 @@ class MusicManager : public Common::Singleton<MusicManager> {
friend class Common::Singleton<SingletonBaseType>;
public:
const MusicPlugin::List &getPlugins() const;
const PluginList &getPlugins() const;
};
/** Convenience shortcut for accessing the Music manager. */
View
@@ -673,9 +673,9 @@ static void listGames() {
printf("Game ID Full Title \n"
"-------------------- ------------------------------------------------------\n");
const EnginePlugin::List &plugins = EngineMan.getPlugins();
for (EnginePlugin::List::const_iterator iter = plugins.begin(); iter != plugins.end(); ++iter) {
GameList list = (**iter)->getSupportedGames();
const PluginList &plugins = EngineMan.getPlugins();
for (PluginList::const_iterator iter = plugins.begin(); iter != plugins.end(); ++iter) {
GameList list = (*iter)->get<MetaEngine>().getSupportedGames();
for (GameList::iterator v = list.begin(); v != list.end(); ++v) {
printf("%-20s %s\n", v->gameid().c_str(), v->description().c_str());
}
@@ -741,21 +741,23 @@ static Common::Error listSaves(const char *target) {
gameid.toLowercase(); // Normalize it to lower case
// Find the plugin that will handle the specified gameid
const EnginePlugin *plugin = 0;
const Plugin *plugin = nullptr;
GameDescriptor game = EngineMan.findGame(gameid, &plugin);
if (!plugin) {
return Common::Error(Common::kEnginePluginNotFound,
Common::String::format("target '%s', gameid '%s", target, gameid.c_str()));
}
if (!(*plugin)->hasFeature(MetaEngine::kSupportsListSaves)) {
const MetaEngine &metaEngine = plugin->get<MetaEngine>();
if (!metaEngine.hasFeature(MetaEngine::kSupportsListSaves)) {
// TODO: Include more info about the target (desc, engine name, ...) ???
return Common::Error(Common::kEnginePluginNotSupportSaves,
Common::String::format("target '%s', gameid '%s", target, gameid.c_str()));
} else {
// Query the plugin for a list of saved games
SaveStateList saveList = (*plugin)->listSaves(target);
SaveStateList saveList = metaEngine.listSaves(target);
if (saveList.size() > 0) {
// TODO: Include more info about the target (desc, engine name, ...) ???
@@ -793,13 +795,14 @@ static void listThemes() {
/** Lists all output devices */
static void listAudioDevices() {
MusicPlugin::List pluginList = MusicMan.getPlugins();
PluginList pluginList = MusicMan.getPlugins();
printf("ID Description\n");
printf("------------------------------ ------------------------------------------------\n");
for (MusicPlugin::List::const_iterator i = pluginList.begin(), iend = pluginList.end(); i != iend; ++i) {
MusicDevices deviceList = (**i)->getDevices();
for (PluginList::const_iterator i = pluginList.begin(), iend = pluginList.end(); i != iend; ++i) {
const MusicPluginObject &musicObject = (*i)->get<MusicPluginObject>();
MusicDevices deviceList = musicObject.getDevices();
for (MusicDevices::iterator j = deviceList.begin(), jend = deviceList.end(); j != jend; ++j) {
printf("%-30s %s\n", Common::String::format("\"%s\"", j->getCompleteId().c_str()).c_str(), j->getCompleteName().c_str());
}
View
@@ -106,8 +106,8 @@ static bool launcherDialog() {
return (dlg.runModal() != -1);
}
static const EnginePlugin *detectPlugin() {
const EnginePlugin *plugin = 0;
static const Plugin *detectPlugin() {
const Plugin *plugin = nullptr;
// Make sure the gameid is set in the config manager, and that it is lowercase.
Common::String gameid(ConfMan.getActiveDomainName());
@@ -141,7 +141,7 @@ static const EnginePlugin *detectPlugin() {
}
// TODO: specify the possible return values here
static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const Common::String &edebuglevels) {
static Common::Error runGame(const Plugin *plugin, OSystem &system, const Common::String &edebuglevels) {
// Determine the game data path, for validation and error messages
Common::FSNode dir(ConfMan.get("path"));
Common::Error err = Common::kNoError;
@@ -169,15 +169,16 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const
// Create the game engine
if (err.getCode() == Common::kNoError) {
const MetaEngine &metaEngine = plugin->get<MetaEngine>();
// Set default values for all of the custom engine options
// Appareantly some engines query them in their constructor, thus we
// need to set this up before instance creation.
const ExtraGuiOptions engineOptions = (*plugin)->getExtraGuiOptions(Common::String());
const ExtraGuiOptions engineOptions = metaEngine.getExtraGuiOptions(Common::String());
for (uint i = 0; i < engineOptions.size(); i++) {
ConfMan.registerDefault(engineOptions[i].configOption, engineOptions[i].defaultState);
}
err = (*plugin)->createInstance(&system, &engine);
err = metaEngine.createInstance(&system, &engine);
}
// Check for errors
@@ -504,7 +505,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
// cleanly, so this is now enabled to encourage people to fix bits :)
while (0 != ConfMan.getActiveDomain()) {
// Try to find a plugin which feels responsible for the specified game.
const EnginePlugin *plugin = detectPlugin();
const Plugin *plugin = detectPlugin();
if (plugin) {
// Unload all plugins not needed for this game,
// to save memory
View
@@ -457,7 +457,7 @@ DECLARE_SINGLETON(EngineManager);
* For the uncached version, we first try to find the plugin using the gameId
* and only if we can't find it there, we loop through the plugins.
**/
GameDescriptor EngineManager::findGame(const Common::String &gameName, const EnginePlugin **plugin) const {
GameDescriptor EngineManager::findGame(const Common::String &gameName, const Plugin **plugin) const {
GameDescriptor result;
// First look for the game using the plugins in memory. This is critical
@@ -493,18 +493,18 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Eng
/**
* Find the game within the plugins loaded in memory
**/
GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const EnginePlugin **plugin) const {
GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
// Find the GameDescriptor for this target
const EnginePlugin::List &plugins = getPlugins();
const PluginList &plugins = getPlugins();
GameDescriptor result;
if (plugin)
*plugin = 0;
EnginePlugin::List::const_iterator iter;
PluginList::const_iterator iter;
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
result = (**iter)->findGame(gameName.c_str());
result = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
if (!result.gameid().empty()) {
if (plugin)
*plugin = *iter;
@@ -516,22 +516,22 @@ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &game
GameList EngineManager::detectGames(const Common::FSList &fslist) const {
GameList candidates;
EnginePlugin::List plugins;
EnginePlugin::List::const_iterator iter;
PluginList plugins;
PluginList::const_iterator iter;
PluginManager::instance().loadFirstPlugin();
do {
plugins = getPlugins();
// Iterate over all known games and for each check if it might be
// the game in the presented directory.
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
candidates.push_back((**iter)->detectGames(fslist));
candidates.push_back((*iter)->get<MetaEngine>().detectGames(fslist));
}
} while (PluginManager::instance().loadNextPlugin());
return candidates;
}
const EnginePlugin::List &EngineManager::getPlugins() const {
return (const EnginePlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
const PluginList &EngineManager::getPlugins() const {
return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
}
@@ -543,6 +543,6 @@ namespace Common {
DECLARE_SINGLETON(MusicManager);
}
const MusicPlugin::List &MusicManager::getPlugins() const {
return (const MusicPlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
const PluginList &MusicManager::getPlugins() const {
return PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC);
}
View
@@ -190,6 +190,15 @@ class Plugin {
PluginType getType() const;
const char *getName() const;
template <class T>
T &get() const {
T *pluginObject = dynamic_cast<T *>(_pluginObject);
if (!pluginObject) {
error("Invalid cast of plugin %s", getName());
}
return *pluginObject;
}
/**
* The getFileName() function gets the name of the plugin file for those
* plugins that have files (ie. not static). It doesn't require the plugin
@@ -201,25 +210,6 @@ class Plugin {
/** List of Plugin instances. */
typedef Common::Array<Plugin *> PluginList;
/**
* Convenience template to make it easier defining normal Plugin
* subclasses. Namely, the PluginSubclass will manage PluginObjects
* of a type specified via the PO_t template parameter.
*/
template<class PO_t>
class PluginSubclass : public Plugin {
public:
PO_t &operator*() const {
return *(PO_t *)_pluginObject;
}
PO_t *operator->() const {
return (PO_t *)_pluginObject;
}
typedef Common::Array<PluginSubclass *> List;
};
/**
* Abstract base class for Plugin factories. Subclasses of this
* are responsible for creating plugin objects, e.g. by loading
View
@@ -262,20 +262,15 @@ class MetaEngine : public PluginObject {
//@}
};
// Engine plugins
typedef PluginSubclass<MetaEngine> EnginePlugin;
/**
* Singleton class which manages all Engine plugins.
*/
class EngineManager : public Common::Singleton<EngineManager> {
public:
GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const EnginePlugin **plugin = NULL) const;
GameDescriptor findGame(const Common::String &gameName, const EnginePlugin **plugin = NULL) const;
GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const;
GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const;
GameList detectGames(const Common::FSList &fslist) const;
const EnginePlugin::List &getPlugins() const;
const PluginList &getPlugins() const;
};
/** Convenience shortcut for accessing the engine manager. */
@@ -870,7 +870,7 @@ void SavegameListBox::pageDown() {
}
int GameStateMenu::scummVMSaveLoadDialog(bool isSave, Common::String &saveDesc) {
const EnginePlugin *plugin = NULL;
const Plugin *plugin = nullptr;
EngineMan.findGame(ConfMan.get("gameid"), &plugin);
GUI::SaveLoadChooser *dialog;
Common::String desc;
Oops, something went wrong.

1 comment on commit d087c96

@rsn8887

This comment has been minimized.

Show comment
Hide comment
@rsn8887

rsn8887 Dec 30, 2017

Contributor

This commit causes the PSP port to crash when scumm engine is enabled and the user uses the launcher to
a) select "add game" -> "choose", regardless of directory it crashes as soon as "choose" is selected
or
b) selecting "start" (crashes immediately after pressing start, on any game)
or
c) selection "load game" (crashes immediately)

Reverting this commit fixes all the crashes in the PSP port.

Contributor

rsn8887 commented on d087c96 Dec 30, 2017

This commit causes the PSP port to crash when scumm engine is enabled and the user uses the launcher to
a) select "add game" -> "choose", regardless of directory it crashes as soon as "choose" is selected
or
b) selecting "start" (crashes immediately after pressing start, on any game)
or
c) selection "load game" (crashes immediately)

Reverting this commit fixes all the crashes in the PSP port.

Please sign in to comment.