[Scummvm-git-logs] scummvm master -> 61f9398b04a4bce397a8be6ae96491a2015a6da2

bgK bastien.bouclet at gmail.com
Mon May 28 18:43:25 CEST 2018


This automated email contains information about 11 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
9587dd5c21 ENGINES: Fix clang-tidy warnings
cf1ebf2951 ENGINES: Add unknown game variants to the game detector results
8fb149e3c7 ENGINES: Change MetaEngine::findGame to return a plain game descriptor
643c24db75 ENGINES: Change MetaEngine::listSupportedGames to return plain game descriptors
5aff87dc15 ENGINES: Turn GameDescriptor into a simple struct
1de5aca585 ENGINES: Set the GameDescriptor decription in the constructor
faa2534f46 ENGINES: Factor adding games to ConfMan
90b78c5446 ENGINES: Merge GameDescriptor and DetectedGame
2fe060e5c9 ENGINES: Cleanup DetectedGame initialization in the AD
1dcb8076db ENGINES: Remove usage of C++11 extended initializer lists
61f9398b04 Merge pull request #1187 from bgK/detection-refactor-unknown


Commit: 9587dd5c21d388616dc8d42db909390fab384c2f
    https://github.com/scummvm/scummvm/commit/9587dd5c21d388616dc8d42db909390fab384c2f
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T07:50:44+02:00

Commit Message:
ENGINES: Fix clang-tidy warnings

Changed paths:
    engines/advancedDetector.cpp


diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index d258f49..dea9b8e 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -167,7 +167,7 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist, bool useU
 	if (matches.empty()) {
 		// Use fallback detector if there were no matches by other means
 		const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist);
-		if (fallbackDesc != 0) {
+		if (fallbackDesc != nullptr) {
 			GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds));
 			updateGameDescriptor(desc, fallbackDesc);
 			detectedGames.push_back(desc);
@@ -336,7 +336,7 @@ void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFileP
 	Common::String reportTranslated = Common::String::format(_(reportCommon), path.getPath().c_str(), getName(), "https://bugs.scummvm.org/");
 	Common::String bugtrackerAffectedEngine = getName();
 
-	if (matchedGameIds.size()) {
+	if (!matchedGameIds.empty()) {
 		report += "\n\n";
 		reportTranslated += "\n\n";
 		report += "Matched game IDs:";
@@ -460,7 +460,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 
 	// Check which files are included in some ADGameDescription *and* are present.
 	// Compute MD5s and file sizes for these files.
-	for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize) {
+	for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize) {
 		g = (const ADGameDescription *)descPtr;
 
 		for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
@@ -484,7 +484,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 
 	// MD5 based matching
 	uint i;
-	for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize, ++i) {
+	for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize, ++i) {
 		g = (const ADGameDescription *)descPtr;
 		bool fileMissing = false;
 
@@ -516,7 +516,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 			if (hashOrSizeMismatch)
 				continue;
 
-			if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) {
+			if (fileDesc->md5 != nullptr && fileDesc->md5 != filesProps[tstr].md5) {
 				debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str());
 				fileMissing = true;
 				hashOrSizeMismatch = true;
@@ -545,7 +545,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 		// cases.
 		if (allFilesPresent) {
 			gotAnyMatchesWithAllFiles = true;
-			if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), g->gameId) != 0)
+			if (matchedGameIds.empty() || strcmp(matchedGameIds.back(), g->gameId) != 0)
 				matchedGameIds.push_back(g->gameId);
 		}
 


Commit: cf1ebf29516bd74927fd7b632b72fd8973f72e98
    https://github.com/scummvm/scummvm/commit/cf1ebf29516bd74927fd7b632b72fd8973f72e98
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Add unknown game variants to the game detector results

Changed paths:
    base/commandLine.cpp
    base/plugins.cpp
    engines/adl/detection.cpp
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/agi/detection.cpp
    engines/cge/detection.cpp
    engines/cge2/detection.cpp
    engines/director/detection.cpp
    engines/game.cpp
    engines/game.h
    engines/gob/detection/detection.cpp
    engines/made/detection.cpp
    engines/metaengine.h
    engines/mohawk/detection.cpp
    engines/queen/detection.cpp
    engines/sci/detection.cpp
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sludge/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    engines/tinsel/detection.cpp
    engines/toon/detection.cpp
    engines/touche/detection.cpp
    engines/tucker/detection.cpp
    engines/unknown-game-dialog.cpp
    engines/unknown-game-dialog.h
    engines/wintermute/detection.cpp
    gui/launcher.cpp
    gui/massadd.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 83c7b56..8e70140 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -864,12 +864,20 @@ static GameList getGameList(const Common::FSNode &dir) {
 	}
 
 	// detect Games
-	GameList candidates(EngineMan.detectGames(files));
-	Common::String dataPath = dir.getPath();
-	// add game data path
-	for (GameList::iterator v = candidates.begin(); v != candidates.end(); ++v) {
-		(*v)["path"] = dataPath;
+	DetectionResults detectionResults = EngineMan.detectGames(files);
+
+	if (detectionResults.foundUnknownGames()) {
+		Common::String report = detectionResults.generateUnknownGameReport(false, 80);
+		g_system->logMessage(LogMessageType::kInfo, report.c_str());
 	}
+
+	DetectedGames detectedGames = detectionResults.listRecognizedGames();
+
+	GameList candidates;
+	for (uint i = 0; i < detectedGames.size(); i++) {
+		candidates.push_back(detectedGames[i].matchedGame);
+	}
+
 	return candidates;
 }
 
@@ -1014,7 +1022,14 @@ static void runDetectorTest() {
 			continue;
 		}
 
-		GameList candidates(EngineMan.detectGames(files));
+		DetectionResults detectionResults = EngineMan.detectGames(files);
+		DetectedGames detectedGames = detectionResults.listRecognizedGames();
+
+		GameList candidates;
+		for (uint i = 0; i < detectedGames.size(); i++) {
+			candidates.push_back(detectedGames[i].matchedGame);
+		}
+
 		bool gameidDiffers = false;
 		GameList::iterator x;
 		for (x = candidates.begin(); x != candidates.end(); ++x) {
@@ -1092,7 +1107,14 @@ void upgradeTargets() {
 		Common::Platform plat = Common::parsePlatform(dom.getVal("platform"));
 		Common::String desc(dom.getVal("description"));
 
-		GameList candidates(EngineMan.detectGames(files));
+		DetectionResults detectionResults = EngineMan.detectGames(files);
+		DetectedGames detectedGames = detectionResults.listRecognizedGames();
+
+		GameList candidates;
+		for (uint i = 0; i < detectedGames.size(); i++) {
+			candidates.push_back(detectedGames[i].matchedGame);
+		}
+
 		GameDescriptor *g = 0;
 
 		// We proceed as follows:
@@ -1100,7 +1122,7 @@ void upgradeTargets() {
 		// * If there is a unique detector match, trust it.
 		// * If there are multiple match, run over them comparing gameid, language and platform.
 		//   If we end up with a unique match, use it. Otherwise, skip.
-		if (candidates.size() == 0) {
+		if (candidates.empty()) {
 			printf(" ... failed to detect game, skipping\n");
 			continue;
 		}
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 8527869..02f6998 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -22,6 +22,7 @@
 
 #include "base/plugins.h"
 
+#include "common/translation.h"
 #include "common/func.h"
 #include "common/debug.h"
 #include "common/config-manager.h"
@@ -514,8 +515,9 @@ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &game
 	return result;
 }
 
-GameList EngineManager::detectGames(const Common::FSList &fslist, bool useUnknownGameDialog) const {
-	GameList candidates;
+DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
+	DetectedGames candidates;
+	Common::String path = fslist.begin()->getParent().getPath();
 	PluginList plugins;
 	PluginList::const_iterator iter;
 	PluginManager::instance().loadFirstPlugin();
@@ -524,17 +526,25 @@ GameList EngineManager::detectGames(const Common::FSList &fslist, bool useUnknow
 		// 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)->get<MetaEngine>().detectGames(fslist, useUnknownGameDialog));
+			const MetaEngine &metaEngine = (*iter)->get<MetaEngine>();
+			DetectedGames engineCandidates = metaEngine.detectGames(fslist);
+
+			for (uint i = 0; i < engineCandidates.size(); i++) {
+				engineCandidates[i].engineName = metaEngine.getName();
+				engineCandidates[i].matchedGame["path"] = path;
+				candidates.push_back(engineCandidates[i]);
+			}
+
 		}
 	} while (PluginManager::instance().loadNextPlugin());
-	return candidates;
+
+	return DetectionResults(candidates);
 }
 
 const PluginList &EngineManager::getPlugins() const {
 	return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
 }
 
-
 // Music plugins
 
 #include "audio/musicplugin.h"
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
index 14646c7..2276576 100644
--- a/engines/adl/detection.cpp
+++ b/engines/adl/detection.cpp
@@ -332,9 +332,9 @@ public:
 	int getMaximumSaveSlot() const { return 'O' - 'A'; }
 	SaveStateList listSaves(const char *target) const;
 	void removeSaveState(const char *target, int slot) const;
-	virtual ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra, bool useUnknownGameDialog = false) const;
+	ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
 
-	bool addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const;
+	bool addFileProps(const FileMap &allFiles, Common::String fname, FilePropertiesMap &filePropsMap) const;
 
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
 };
@@ -492,14 +492,14 @@ Common::Platform getPlatform(const AdlGameDescription &adlDesc) {
 	return adlDesc.desc.platform;
 }
 
-bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const {
+bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, FilePropertiesMap &filePropsMap) const {
 	if (filePropsMap.contains(fname))
 		return true;
 
 	if (!allFiles.contains(fname))
 		return false;
 
-	ADFileProperties fileProps;
+	FileProperties fileProps;
 	fileProps.size = computeMD5(allFiles[fname], fileProps.md5, 16384);
 
 	if (fileProps.size != -1) {
@@ -511,42 +511,39 @@ bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname,
 }
 
 // Based on AdvancedMetaEngine::detectGame
-ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra, bool useUnknownGameDialog) const {
+ADDetectedGames AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
 	// We run the file-based detector first and then add to the returned list
-	ADGameDescList matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra, useUnknownGameDialog);
+	ADDetectedGames matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra);
 
 	debug(3, "Starting disk image detection in dir '%s'", parent.getPath().c_str());
 
-	ADFilePropertiesMap filesProps;
-	ADGameIdList matchedGameIds;
+	FilePropertiesMap filesProps;
 	bool gotAnyMatchesWithAllFiles = false;
 
 	for (uint g = 0; gameDiskDescriptions[g].desc.gameId != 0; ++g) {
-		const ADGameDescription &desc = gameDiskDescriptions[g].desc;
+		ADDetectedGame game(&gameDiskDescriptions[g].desc);
 
 		// Skip games that don't meet the language/platform/extra criteria
-		if (language != Common::UNK_LANG && desc.language != Common::UNK_LANG) {
-			if (desc.language != language && !(language == Common::EN_ANY && (desc.flags & ADGF_ADDENGLISH)))
-			    continue;
+		if (language != Common::UNK_LANG && game.desc->language != Common::UNK_LANG) {
+			if (game.desc->language != language && !(language == Common::EN_ANY && (game.desc->flags & ADGF_ADDENGLISH)))
+				continue;
 		}
 
-		if (platform != Common::kPlatformUnknown && desc.platform != Common::kPlatformUnknown && desc.platform != platform)
+		if (platform != Common::kPlatformUnknown && game.desc->platform != Common::kPlatformUnknown && game.desc->platform != platform)
 			continue;
 
-		if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && desc.extra != extra)
+		if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && game.desc->extra != extra)
 			continue;
 
-		bool fileMissing = false;
 		bool allFilesPresent = true;
-		bool hashOrSizeMismatch = false;
 
-		for (uint f = 0;  desc.filesDescriptions[f].fileName; ++f) {
-			const ADGameFileDescription &fDesc = desc.filesDescriptions[f];
+		for (uint f = 0; game.desc->filesDescriptions[f].fileName; ++f) {
+			const ADGameFileDescription &fDesc = game.desc->filesDescriptions[f];
 			Common::String fileName;
 			bool foundDiskImage = false;
 
 			for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) {
-				if (diskImageExts[e].platform == desc.platform) {
+				if (diskImageExts[e].platform == game.desc->platform) {
 					Common::String testFileName(fDesc.fileName);
 					testFileName += diskImageExts[e].extension;
 
@@ -563,49 +560,41 @@ ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const Fil
 			}
 
 			if (!foundDiskImage) {
-				fileMissing = true;
 				allFilesPresent = false;
 				break;
 			}
 
-			if (hashOrSizeMismatch)
+			game.matchedFiles[fileName] = filesProps[fileName];
+
+			if (game.hasUnknownFiles)
 				continue;
 
 			if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) {
 				debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str());
-				fileMissing = true;
-				hashOrSizeMismatch = true;
+				game.hasUnknownFiles = true;
 				continue;
 			}
 
 			if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) {
 				debug(3, "Size Mismatch. Skipping");
-				fileMissing = true;
-				hashOrSizeMismatch = true;
+				game.hasUnknownFiles = true;
 				continue;
 			}
 
 			debug(3, "Matched file: %s", fileName.c_str());
 		}
 
-		if (!fileMissing) {
-			debug(2, "Found game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
-			matched.push_back(&desc);
+		if (allFilesPresent && !game.hasUnknownFiles) {
+			debug(2, "Found game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g);
+			gotAnyMatchesWithAllFiles = true;
+			matched.push_back(game);
 		} else {
-			if (allFilesPresent) {
-				gotAnyMatchesWithAllFiles = true;
-				if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), desc.gameId) != 0)
-					matchedGameIds.push_back(desc.gameId);
+			if (allFilesPresent && !gotAnyMatchesWithAllFiles) {
+				if (matched.empty() || strcmp(matched.back().desc->gameId, game.desc->gameId) != 0)
+					matched.push_back(game);
 			}
 
-			debug(5, "Skipping game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
-		}
-	}
-
-	// TODO: This could be improved to handle matched and unknown games together in a single directory
-	if (matched.empty()) {
-		if (!filesProps.empty() && gotAnyMatchesWithAllFiles) {
-			reportUnknown(parent, filesProps, matchedGameIds, useUnknownGameDialog);
+			debug(5, "Skipping game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g);
 		}
 	}
 
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index dea9b8e..9c1a700 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -30,8 +30,6 @@
 #include "common/textconsole.h"
 #include "common/translation.h"
 #include "gui/EventRecorder.h"
-#include "gui/gui-manager.h"
-#include "engines/unknown-game-dialog.h"
 #include "engines/advancedDetector.h"
 #include "engines/obsolete.h"
 
@@ -63,6 +61,17 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa
 	return gd;
 }
 
+DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const {
+	DetectedGame game;
+	game.engineName = getName();
+	game.gameId = adGame.desc->gameId;
+	game.hasUnknownFiles = adGame.hasUnknownFiles;
+	game.matchedFiles = adGame.matchedFiles;
+	game.matchedGame = toGameDescriptor(*adGame.desc, _gameIds);
+	updateGameDescriptor(game.matchedGame, adGame.desc);
+	return game;
+}
+
 /**
  * Generate a preferred target value as
  *   GAMEID-PLAFORM-LANG
@@ -129,11 +138,11 @@ void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGame
 		desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
 }
 
-bool cleanupPirated(ADGameDescList &matched) {
+bool cleanupPirated(ADDetectedGames &matched) {
 	// OKay, now let's sense presence of pirated games
 	if (!matched.empty()) {
 		for (uint j = 0; j < matched.size();) {
-			if (matched[j]->flags & ADGF_PIRATED)
+			if (matched[j].desc->flags & ADGF_PIRATED)
 				matched.remove_at(j);
 			else
 				++j;
@@ -150,35 +159,46 @@ bool cleanupPirated(ADGameDescList &matched) {
 }
 
 
-GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist, bool useUnknownGameDialog) const {
-	ADGameDescList matches;
-	GameList detectedGames;
+DetectedGames AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
 	FileMap allFiles;
 
 	if (fslist.empty())
-		return detectedGames;
+		return DetectedGames();
 
 	// Compose a hashmap of all files in fslist.
 	composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
 
 	// Run the detector on this
-       matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, "", useUnknownGameDialog);
+	ADDetectedGames matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, "");
 
-	if (matches.empty()) {
-		// Use fallback detector if there were no matches by other means
-		const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist);
-		if (fallbackDesc != nullptr) {
-			GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds));
-			updateGameDescriptor(desc, fallbackDesc);
-			detectedGames.push_back(desc);
+	cleanupPirated(matches);
+
+	DetectedGames detectedGames;
+	for (uint i = 0; i < matches.size(); i++) {
+		DetectedGame game = toDetectedGame(matches[i]);
+
+		if (game.hasUnknownFiles) {
+			// Non fallback games with unknown files cannot be added/launched
+			game.canBeAdded = false;
 		}
-	} else {
-		// Otherwise use the found matches
-		cleanupPirated(matches);
-		for (uint i = 0; i < matches.size(); i++) {
-			GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds));
-			updateGameDescriptor(desc, matches[i]);
-			detectedGames.push_back(desc);
+
+		detectedGames.push_back(game);
+	}
+
+	bool foundKnownGames = false;
+	for (uint i = 0; i < detectedGames.size(); i++) {
+		foundKnownGames |= detectedGames[i].canBeAdded;
+	}
+
+	if (!foundKnownGames) {
+		// Use fallback detector if there were no matches by other means
+		ADDetectedGame fallbackDetectionResult = fallbackDetect(allFiles, fslist);
+
+		if (fallbackDetectionResult.desc) {
+			DetectedGame fallbackDetectedGame = toDetectedGame(fallbackDetectionResult);
+			fallbackDetectedGame.preferredTarget += "-fallback";
+
+			detectedGames.push_back(fallbackDetectedGame);
 		}
 	}
 
@@ -216,7 +236,6 @@ const ExtraGuiOptions AdvancedMetaEngine::getExtraGuiOptions(const Common::Strin
 Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
 	assert(engine);
 
-	const ADGameDescription *agdDesc = 0;
 	Common::Language language = Common::UNK_LANG;
 	Common::Platform platform = Common::kPlatformUnknown;
 	Common::String extra;
@@ -266,46 +285,43 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
 	composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
 
 	// Run the detector on this
-	ADGameDescList matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra);
+	ADDetectedGames matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra);
 
 	if (cleanupPirated(matches))
 		return Common::kNoGameDataFoundError;
 
-	if (_singleId == NULL) {
-		// Find the first match with correct gameid.
-		for (uint i = 0; i < matches.size(); i++) {
-			if (matches[i]->gameId == gameid) {
-				agdDesc = matches[i];
-				break;
-			}
+	ADDetectedGame agdDesc;
+	for (uint i = 0; i < matches.size(); i++) {
+		if ((_singleId || matches[i].desc->gameId == gameid) && !matches[i].hasUnknownFiles) {
+			agdDesc = matches[i];
+			break;
 		}
-	} else if (matches.size() > 0) {
-		agdDesc = matches[0];
 	}
 
-	if (agdDesc == 0) {
+	if (!agdDesc.desc) {
 		// Use fallback detector if there were no matches by other means
-		agdDesc = fallbackDetect(allFiles, files);
-		if (agdDesc != 0) {
+		ADDetectedGame fallbackDetectedGame = fallbackDetect(allFiles, files);
+		agdDesc = fallbackDetectedGame;
+		if (agdDesc.desc) {
 			// Seems we found a fallback match. But first perform a basic
 			// sanity check: the gameid must match.
-			if (_singleId == NULL && agdDesc->gameId != gameid)
-				agdDesc = 0;
+			if (!_singleId && agdDesc.desc->gameId != gameid)
+				agdDesc = ADDetectedGame();
 		}
 	}
 
-	if (agdDesc == 0)
+	if (!agdDesc.desc)
 		return Common::kNoGameDataFoundError;
 
 	// If the GUI options were updated, we catch this here and update them in the users config
 	// file transparently.
-	Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc->language);
-	if (agdDesc->flags & ADGF_ADDENGLISH)
+	Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc.desc->language);
+	if (agdDesc.desc->flags & ADGF_ADDENGLISH)
 		lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY);
 
-	Common::updateGameGUIOptions(agdDesc->guiOptions + _guiOptions, lang);
+	Common::updateGameGUIOptions(agdDesc.desc->guiOptions + _guiOptions, lang);
 
-	GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameIds);
+	GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc.desc, _gameIds);
 
 	bool showTestingWarning = false;
 
@@ -320,65 +336,13 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
 		return Common::kUserCanceled;
 
 	debug(2, "Running %s", gameDescriptor.description().c_str());
-	initSubSystems(agdDesc);
-	if (!createInstance(syst, engine, agdDesc))
+	initSubSystems(agdDesc.desc);
+	if (!createInstance(syst, engine, agdDesc.desc))
 		return Common::kNoGameDataFoundError;
 	else
 		return Common::kNoError;
 }
 
-void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds, bool useUnknownGameDialog) const {
-	const char *reportCommon = _s("The game in '%s' seems to be an unknown %s engine game "
-							   "variant.\n\nPlease report the following data to the ScummVM "
-							   "team at %s along with the name of the game you tried to add and "
-							   "its version, language, etc.:");
-	Common::String report           = Common::String::format(reportCommon, path.getPath().c_str(), getName(), "https://bugs.scummvm.org/");
-	Common::String reportTranslated = Common::String::format(_(reportCommon), path.getPath().c_str(), getName(), "https://bugs.scummvm.org/");
-	Common::String bugtrackerAffectedEngine = getName();
-
-	if (!matchedGameIds.empty()) {
-		report += "\n\n";
-		reportTranslated += "\n\n";
-		report += "Matched game IDs:";
-		reportTranslated += _("Matched game IDs:");
-		report += " ";
-		reportTranslated += " ";
-
-		for (ADGameIdList::const_iterator gameId = matchedGameIds.begin(); gameId != matchedGameIds.end(); ++gameId) {
-			if (gameId != matchedGameIds.begin()) {
-				report += ", ";
-				reportTranslated += ", ";
-			}
-			report += *gameId;
-			reportTranslated += *gameId;
-		}
-	}
-
-	report += "\n\n";
-	reportTranslated += "\n\n";
-
-	reportTranslated.wordWrap(65);
-	Common::String reportLog = report;
-	reportLog.wordWrap(80);
-
-	Common::String unknownFiles;
-	for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file)
-		unknownFiles += Common::String::format("  {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);
-
-	report += unknownFiles;
-	reportTranslated += unknownFiles;
-	reportLog += unknownFiles + "\n";
-
-	// Write the original message about the unknown game to the log file
-	g_system->logMessage(LogMessageType::kInfo, reportLog.c_str());
-
-	// Check if the GUI is running, show the UnknownGameDialog and print the translated unknown game information
-       if (GUI::GuiManager::hasInstance() && g_gui.isActive() && useUnknownGameDialog == true) {
-		UnknownGameDialog dialog(report, reportTranslated, bugtrackerAffectedEngine);
-		dialog.runModal();
-	}
-}
-
 void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const {
 	if (depth <= 0)
 		return;
@@ -419,7 +383,7 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL
 	}
 }
 
-bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const {
+bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, FileProperties &fileProps) const {
 	// FIXME/TODO: We don't handle the case that a file is listed as a regular
 	// file and as one with resource fork.
 
@@ -449,8 +413,9 @@ bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const F
 	return true;
 }
 
-ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra, bool useUnknownGameDialog) const {
-	ADFilePropertiesMap filesProps;
+ADDetectedGames AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
+	FilePropertiesMap filesProps;
+	ADDetectedGames matched;
 
 	const ADGameFileDescription *fileDesc;
 	const ADGameDescription *g;
@@ -465,7 +430,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 
 		for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
 			Common::String fname = fileDesc->fileName;
-			ADFileProperties tmp;
+			FileProperties tmp;
 
 			if (filesProps.contains(fname))
 				continue;
@@ -477,8 +442,6 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 		}
 	}
 
-	ADGameDescList matched;
-	ADGameIdList matchedGameIds;
 	int maxFilesMatched = 0;
 	bool gotAnyMatchesWithAllFiles = false;
 
@@ -486,7 +449,6 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 	uint i;
 	for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize, ++i) {
 		g = (const ADGameDescription *)descPtr;
-		bool fileMissing = false;
 
 		// Do not even bother to look at entries which do not have matching
 		// language and platform (if specified).
@@ -499,34 +461,33 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 		if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
 			continue;
 
+		ADDetectedGame game(g);
 		bool allFilesPresent = true;
 		int curFilesMatched = 0;
-		bool hashOrSizeMismatch = false;
 
 		// Try to match all files for this game
-		for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
+		for (fileDesc = game.desc->filesDescriptions; fileDesc->fileName; fileDesc++) {
 			Common::String tstr = fileDesc->fileName;
 
 			if (!filesProps.contains(tstr)) {
-				fileMissing = true;
 				allFilesPresent = false;
 				break;
 			}
 
-			if (hashOrSizeMismatch)
+			game.matchedFiles[tstr] = filesProps[tstr];
+
+			if (game.hasUnknownFiles)
 				continue;
 
 			if (fileDesc->md5 != nullptr && fileDesc->md5 != filesProps[tstr].md5) {
 				debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str());
-				fileMissing = true;
-				hashOrSizeMismatch = true;
+				game.hasUnknownFiles = true;
 				continue;
 			}
 
 			if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) {
 				debug(3, "Size Mismatch. Skipping");
-				fileMissing = true;
-				hashOrSizeMismatch = true;
+				game.hasUnknownFiles = true;
 				continue;
 			}
 
@@ -543,13 +504,12 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 		// Potentially this could rule out variants where some particular file
 		// is really missing, but the developers should better know about such
 		// cases.
-		if (allFilesPresent) {
-			gotAnyMatchesWithAllFiles = true;
-			if (matchedGameIds.empty() || strcmp(matchedGameIds.back(), g->gameId) != 0)
-				matchedGameIds.push_back(g->gameId);
+		if (allFilesPresent && !gotAnyMatchesWithAllFiles) {
+			if (matched.empty() || strcmp(matched.back().desc->gameId, g->gameId) != 0)
+				matched.push_back(game);
 		}
 
-		if (!fileMissing) {
+		if (allFilesPresent && !game.hasUnknownFiles) {
 			debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
 			 getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
 
@@ -558,37 +518,29 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
 				maxFilesMatched = curFilesMatched;
 
 				matched.clear();	// Remove any prior, lower ranked matches.
-				matched.push_back(g);
+				matched.push_back(game);
 			} else if (curFilesMatched == maxFilesMatched) {
-				matched.push_back(g);
+				matched.push_back(game);
 			} else {
 				debug(2, " ... skipped");
 			}
 
+			gotAnyMatchesWithAllFiles = true;
 		} else {
 			debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
 			 getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
 		}
 	}
 
-	// We didn't find a match
-	if (matched.empty()) {
-		if (!filesProps.empty() && gotAnyMatchesWithAllFiles) {
-			reportUnknown(parent, filesProps, matchedGameIds, useUnknownGameDialog);
-		}
-
-		// Filename based fallback
-	}
-
 	return matched;
 }
 
-const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps) const {
+ADDetectedGame AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const {
 	const ADFileBasedFallback *ptr;
 	const char* const* filenames;
 
 	int maxNumMatchedFiles = 0;
-	const ADGameDescription *matchedDesc = 0;
+	ADDetectedGame result;
 
 	for (ptr = fileBasedFallback; ptr->desc; ++ptr) {
 		const ADGameDescription *agdesc = ptr->desc;
@@ -609,25 +561,26 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &
 			debug(4, "Matched: %s", agdesc->gameId);
 
 			if (numMatchedFiles > maxNumMatchedFiles) {
-				matchedDesc = agdesc;
 				maxNumMatchedFiles = numMatchedFiles;
 
 				debug(4, "and overridden");
 
-				if (filesProps) {
-					for (filenames = ptr->filenames; *filenames; ++filenames) {
-						ADFileProperties tmp;
+				ADDetectedGame game(agdesc);
+				game.hasUnknownFiles = true;
+
+				for (filenames = ptr->filenames; *filenames; ++filenames) {
+					FileProperties tmp;
 
-						if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp))
-							(*filesProps)[*filenames] = tmp;
-					}
+					if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp))
+						game.matchedFiles[*filenames] = tmp;
 				}
 
+				result = game;
 			}
 		}
 	}
 
-	return matchedDesc;
+	return result;
 }
 
 GameList AdvancedMetaEngine::getSupportedGames() const {
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index d7e85f8..f1f55d0 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -48,20 +48,6 @@ struct ADGameFileDescription {
 };
 
 /**
- * A record describing the properties of a file. Used on the existing
- * files while detecting a game.
- */
-struct ADFileProperties {
-	int32 size;
-	Common::String md5;
-};
-
-/**
- * A map of all relevant existing files in a game directory while detecting.
- */
-typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ADFilePropertiesMap;
-
-/**
  * A shortcut to produce an empty ADGameFileDescription record. Used to mark
  * the end of a list of these.
  */
@@ -112,14 +98,19 @@ struct ADGameDescription {
 };
 
 /**
- * A list of pointers to ADGameDescription structs (or subclasses thereof).
+ * A game installation matching an AD game description
  */
-typedef Common::Array<const ADGameDescription *> ADGameDescList;
+struct ADDetectedGame {
+	bool hasUnknownFiles;
+	FilePropertiesMap matchedFiles;
+	const ADGameDescription *desc;
 
-/**
- * A list of raw game ID strings.
- */
-typedef Common::Array<const char *> ADGameIdList;
+	ADDetectedGame() : desc(nullptr), hasUnknownFiles(false) {}
+	explicit ADDetectedGame(const ADGameDescription *d) : desc(d), hasUnknownFiles(false) {}
+};
+
+/** A list of games detected by the AD */
+typedef Common::Array<ADDetectedGame> ADDetectedGames;
 
 /**
  * End marker for a table of ADGameDescription structs. Use this to
@@ -278,7 +269,7 @@ public:
 
 	virtual GameDescriptor findGame(const char *gameId) const;
 
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const;
+	DetectedGames detectGames(const Common::FSList &fslist) const override;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
@@ -294,8 +285,8 @@ protected:
 	 * An (optional) generic fallback detect function which is invoked
 	 * if the regular MD5 based detection failed to detect anything.
 	 */
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
-		return 0;
+	virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+		return ADDetectedGame();
 	}
 
 private:
@@ -313,7 +304,7 @@ protected:
 	 * @param extra		restrict results to specified extra string (only if kADFlagUseExtraAsHint is set)
 	 * @return	list of ADGameDescription pointers corresponding to matched games
 	 */
-	virtual ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra, bool useUnknownGameDialog = false) const;
+	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const;
 
 	/**
 	 * Iterates over all ADFileBasedFallback records inside fileBasedFallback.
@@ -327,13 +318,7 @@ protected:
 	 * @param fileBasedFallback	a list of ADFileBasedFallback records, zero-terminated
 	 * @param filesProps	if not 0, return a map of properties for all detected files here
 	 */
-	const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps = 0) const;
-
-	/**
-	 * Log and print a report that we found an unknown game variant, together with the file
-	 * names, sizes and MD5 sums.
-	 */
-	void reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds = ADGameIdList(), bool useUnknownGameDialog = false) const;
+	ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const;
 
 	// TODO
 	void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const;
@@ -345,7 +330,10 @@ protected:
 	void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName = Common::String()) const;
 
 	/** Get the properties (size and MD5) of this file. */
-	bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const;
+	bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, FileProperties &fileProps) const;
+
+	/** Convert an AD game description into the shared game description format */
+	DetectedGame toDetectedGame(const ADDetectedGame &adGame) const;
 };
 
 #endif
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 817be08..39275c4 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -220,7 +220,7 @@ public:
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 };
 
 bool AgiMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -421,7 +421,7 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl
 	}
 }
 
-const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const {
+ADDetectedGame AgiMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const {
 	typedef Common::HashMap<Common::String, int32> IntMap;
 	IntMap allFiles;
 	bool matchedUsingFilenames = false;
@@ -584,10 +584,10 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
 
 		g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str());
 
-		return (const ADGameDescription *)&g_fallbackDesc;
+		return ADDetectedGame(&g_fallbackDesc.desc);
 	}
 
-	return 0;
+	return ADDetectedGame();
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(AGI)
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 4c23939..f6399d4 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -126,7 +126,7 @@ public:
 		return "Soltys (C) 1994-1996 L.K. Avalon";
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
@@ -135,13 +135,8 @@ public:
 	virtual void removeSaveState(const char *target, int slot) const;
 };
 
-static const ADFileBasedFallback fileBasedFallback[] = {
-	{ &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } },
-	{ 0, { 0 } }
-};
-
 static ADGameDescription s_fallbackDesc = {
-	"Soltys",
+	"soltys",
 	"Unknown version",
 	AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
 	Common::UNK_LANG,
@@ -150,28 +145,29 @@ static ADGameDescription s_fallbackDesc = {
 	GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
 };
 
-const ADGameDescription *CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
-	ADFilePropertiesMap filesProps;
+static const ADFileBasedFallback fileBasedFallback[] = {
+	{ &s_fallbackDesc, { "vol.cat", "vol.dat", 0 } },
+	{ 0, { 0 } }
+};
 
-	const ADGameDescription *game;
-	game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback, &filesProps);
+ADDetectedGame CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback);
 
-	if (!game)
-		return nullptr;
+	if (!game.desc)
+		return ADDetectedGame();
 
 	SearchMan.addDirectory("CGEMetaEngine::fallbackDetect", fslist.begin()->getParent());
 	ResourceManager *resman;
 	resman = new ResourceManager();
-	bool result = resman->exist("CGE.SAY");
+	bool sayFileFound = resman->exist("CGE.SAY");
 	delete resman;
 
 	SearchMan.remove("CGEMetaEngine::fallbackDetect");
 
-	if (!result)
-		return nullptr;
+	if (!sayFileFound)
+		return ADDetectedGame();
 
-	reportUnknown(fslist.begin()->getParent(), filesProps);
-	return &s_fallbackDesc;
+	return game;
 }
 
 bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const {
diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp
index 648dcc0..ec6925a 100644
--- a/engines/cge2/detection.cpp
+++ b/engines/cge2/detection.cpp
@@ -132,7 +132,7 @@ public:
 		return "Sfinx (C) 1994-1997 Janus B. Wisniewski and L.K. Avalon";
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
@@ -141,13 +141,8 @@ public:
 	virtual void removeSaveState(const char *target, int slot) const;
 };
 
-static const ADFileBasedFallback fileBasedFallback[] = {
-	{ &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } },
-	{ 0, { 0 } }
-};
-
 static ADGameDescription s_fallbackDesc = {
-	"Sfinx",
+	"sfinx",
 	"Unknown version",
 	AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
 	Common::UNK_LANG,
@@ -156,30 +151,31 @@ static ADGameDescription s_fallbackDesc = {
 	GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
 };
 
+static const ADFileBasedFallback fileBasedFallback[] = {
+	{ &s_fallbackDesc, { "vol.cat", "vol.dat", 0 } },
+	{ 0, { 0 } }
+};
+
 // This fallback detection looks identical to the one used for CGE. In fact, the difference resides
 // in the ResourceManager which handles a different archive format. The rest of the detection is identical.
-const ADGameDescription *CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
-	ADFilePropertiesMap filesProps;
-
-	const ADGameDescription *game;
-	game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback, &filesProps);
+ADDetectedGame CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback);
 
-	if (!game)
-		return 0;
+	if (!game.desc)
+		return ADDetectedGame();
 
 	SearchMan.addDirectory("CGE2MetaEngine::fallbackDetect", fslist.begin()->getParent());
 	ResourceManager *resman;
 	resman = new ResourceManager();
-	bool result = resman->exist("CGE.SAY");
+	bool sayFileFound = resman->exist("CGE.SAY");
 	delete resman;
 
 	SearchMan.remove("CGE2MetaEngine::fallbackDetect");
 
-	if (!result)
-		return 0;
+	if (!sayFileFound)
+		return ADDetectedGame();
 
-	reportUnknown(fslist.begin()->getParent(), filesProps);
-	return &s_fallbackDesc;
+	return game;
 }
 
 bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
diff --git a/engines/director/detection.cpp b/engines/director/detection.cpp
index 16d838f..9d29384 100644
--- a/engines/director/detection.cpp
+++ b/engines/director/detection.cpp
@@ -112,7 +112,7 @@ public:
 		return "Macromedia Director (C) Macromedia";
 	}
 
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 };
 
@@ -141,7 +141,7 @@ static Director::DirectorGameDescription s_fallbackDesc = {
 
 static char s_fallbackFileNameBuffer[51];
 
-const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ADDetectedGame DirectorMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
 	// TODO: Handle Mac fallback
 
 	// reset fallback description
@@ -230,10 +230,10 @@ const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFi
 
 		warning("Director fallback detection D%d", desc->version);
 
-		return (ADGameDescription *)desc;
+		return ADDetectedGame(&desc->desc);
 	}
 
-	return 0;
+	return ADDetectedGame();
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(DIRECTOR)
diff --git a/engines/game.cpp b/engines/game.cpp
index 7ff51a9..177880c 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -22,6 +22,7 @@
 
 #include "engines/game.h"
 #include "common/gui_options.h"
+#include "common/translation.h"
 
 
 const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) {
@@ -124,3 +125,89 @@ void GameDescriptor::setSupportLevel(GameSupportLevel gsl) {
 		erase("gsl");
 	}
 }
+
+DetectionResults::DetectionResults(const DetectedGames &detectedGames) :
+		_detectedGames(detectedGames) {
+}
+
+bool DetectionResults::foundUnknownGames() const {
+	for (uint i = 0; i < _detectedGames.size(); i++) {
+		if (_detectedGames[i].hasUnknownFiles) {
+			return true;
+		}
+	}
+	return false;
+}
+
+DetectedGames DetectionResults::listRecognizedGames() {
+	DetectedGames candidates;
+	for (uint i = 0; i < _detectedGames.size(); i++) {
+		if (_detectedGames[i].canBeAdded) {
+			candidates.push_back(_detectedGames[i]);
+		}
+	}
+	return candidates;
+}
+
+Common::String DetectionResults::generateUnknownGameReport(bool translate, uint32 wordwrapAt) const {
+	assert(!_detectedGames.empty());
+
+	const char *reportStart = _s("The game in '%s' seems to be an unknown game variant.\n\n"
+	                             "Please report the following data to the ScummVM team at %s "
+	                             "along with the name of the game you tried to add and "
+	                             "its version, language, etc.:");
+	const char *reportEngineHeader = _s("Matched game IDs for the %s engine:");
+
+	Common::String report = Common::String::format(
+			translate ? _(reportStart) : reportStart, _detectedGames[0].matchedGame["path"].c_str(),
+			"https://bugs.scummvm.org/"
+	);
+	report += "\n";
+
+	FilePropertiesMap matchedFiles;
+
+	const char *currentEngineName = nullptr;
+	for (uint i = 0; i < _detectedGames.size(); i++) {
+		const DetectedGame &game = _detectedGames[i];
+
+		if (!game.hasUnknownFiles) continue;
+
+		if (!currentEngineName || strcmp(currentEngineName, game.engineName) != 0) {
+			currentEngineName = game.engineName;
+
+			// If the engine is not the same as for the previous entry, print an engine line header
+			report += "\n";
+			report += Common::String::format(
+					translate ? _(reportEngineHeader) : reportEngineHeader,
+					game.engineName
+			);
+			report += " ";
+
+		} else {
+			report += ", ";
+		}
+
+		// Add the gameId to the list of matched games for the engine
+		// TODO: Use the gameId here instead of the preferred target.
+		// This is currently impossible due to the AD singleId feature losing the information.
+		report += game.matchedGame["preferredtarget"];
+
+		// Consolidate matched files across all engines and detection entries
+		for (FilePropertiesMap::const_iterator it = game.matchedFiles.begin(); it != game.matchedFiles.end(); it++) {
+			matchedFiles.setVal(it->_key, it->_value);
+		}
+	}
+
+	if (wordwrapAt) {
+		report.wordWrap(wordwrapAt);
+	}
+
+	report += "\n\n";
+
+	for (FilePropertiesMap::const_iterator file = matchedFiles.begin(); file != matchedFiles.end(); ++file)
+		report += Common::String::format("  {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);
+
+	report += "\n";
+
+	return report;
+}
diff --git a/engines/game.h b/engines/game.h
index e01e5c6..54c1af3 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -115,4 +115,108 @@ public:
 	}
 };
 
+/**
+ * A record describing the properties of a file. Used on the existing
+ * files while detecting a game.
+ */
+struct FileProperties {
+	int32 size;
+	Common::String md5;
+
+	FileProperties() : size(-1) {}
+};
+
+/**
+ * A map of all relevant existing files while detecting.
+ */
+typedef Common::HashMap<Common::String, FileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FilePropertiesMap;
+
+struct DetectedGame {
+	/**
+	 * The name of the engine supporting the detected game
+	 */
+	const char *engineName;
+
+	/**
+	 * The identifier of the detected game
+	 *
+	 * For engines using the singleId feature, this is the true engine-specific gameId, not the singleId.
+	 */
+	const char *gameId;
+
+	/**
+	 * A game was detected, but some files were not recognized
+	 *
+	 * This can happen when the md5 or size of the detected files did not match the engine's detection tables.
+	 * When this is true, the list of matched files below contains detail about the unknown files.
+	 *
+	 * @see matchedFiles
+	 */
+	bool hasUnknownFiles;
+
+	/**
+	 * An optional list of the files that were used to match the game with the engine's detection tables
+	 */
+	FilePropertiesMap matchedFiles;
+
+	/**
+	 * This detection entry contains enough data to add the game to the configuration manager and launch it
+	 *
+	 * @see matchedGame
+	 */
+	bool canBeAdded;
+
+	/**
+	 * Details about the detected game
+	 */
+	GameDescriptor matchedGame;
+
+	DetectedGame() : engineName(nullptr), gameId(nullptr), hasUnknownFiles(false), canBeAdded(true) {}
+};
+
+typedef Common::Array<DetectedGame> DetectedGames;
+
+/**
+ * Contains a list of games found by the engines' detectors.
+ *
+ * Each detected game can either:
+ * - be fully recognized (e.g. an exact match was found in the detection tables of an engine)
+ * - be an unknown variant (e.g. a game using files with the same name was found in the detection tables)
+ * - be recognized with unknown files (e.g. the game was exactly not found in the detection tables,
+ *              but the detector was able to gather enough data to allow launching the game)
+ *
+ * Practically, this means a detected game can be in both the recognized game list and in the unknown game
+ * report handled by this class.
+ */
+class DetectionResults {
+public:
+	explicit DetectionResults(const DetectedGames &detectedGames);
+
+	/**
+	 * List all the games that were recognized by the engines
+	 *
+	 * Recognized games can be added to the configuration manager and then launched.
+	 */
+	DetectedGames listRecognizedGames();
+
+	/**
+	 * Were unknown game variants found by the engines?
+	 *
+	 * When unknown game variants are found, an unknown game report can be generated.
+	 */
+	bool foundUnknownGames() const;
+
+	/**
+	 * Generate a report that we found an unknown game variant, together with the file
+	 * names, sizes and MD5 sums.
+	 *
+	 * @param translate translate the report to the currently active GUI language
+	 * @param wordwrapAt word wrap the text part of the report after a number of characters
+	 */
+	Common::String generateUnknownGameReport(bool translate, uint32 wordwrapAt = 0) const;
+
+private:
+	DetectedGames _detectedGames;
+};
+
 #endif
diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp
index e204ced..487b65f 100644
--- a/engines/gob/detection/detection.cpp
+++ b/engines/gob/detection/detection.cpp
@@ -35,7 +35,7 @@ public:
 
 	virtual GameDescriptor findGame(const char *gameId) const;
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 
 	virtual const char *getName() const;
 	virtual const char *getOriginalCopyright() const;
@@ -63,25 +63,22 @@ GameDescriptor GobMetaEngine::findGame(const char *gameId) const {
 	return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
 }
 
-const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
-	ADFilePropertiesMap filesProps;
+ADDetectedGame GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	ADDetectedGame detectedGame = detectGameFilebased(allFiles, fslist, Gob::fileBased);
+	if (!detectedGame.desc) {
+		return ADDetectedGame();
+	}
 
-	const Gob::GOBGameDescription *game;
-	game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps);
-	if (!game)
-		return 0;
+	const Gob::GOBGameDescription *game = (const Gob::GOBGameDescription *)detectedGame.desc;
 
 	if (game->gameType == Gob::kGameTypeOnceUponATime) {
 		game = detectOnceUponATime(fslist);
-		if (!game)
-			return 0;
+		if (game) {
+			detectedGame.desc = &game->desc;
+		}
 	}
 
-	ADGameIdList gameIds;
-	gameIds.push_back(game->desc.gameId);
-
-	reportUnknown(fslist.begin()->getParent(), filesProps, gameIds);
-	return (const ADGameDescription *)game;
+	return detectedGame;
 }
 
 const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) {
diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp
index 636c2d1..bf05385 100644
--- a/engines/made/detection.cpp
+++ b/engines/made/detection.cpp
@@ -535,7 +535,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 
 };
 
@@ -557,7 +557,7 @@ bool MadeMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame
 	return gd != 0;
 }
 
-const ADGameDescription *MadeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ADDetectedGame MadeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
 	// Set the default values for the fallback descriptor's ADGameDescription part.
 	Made::g_fallbackDesc.desc.language = Common::UNK_LANG;
 	Made::g_fallbackDesc.desc.platform = Common::kPlatformDOS;
@@ -569,7 +569,7 @@ const ADGameDescription *MadeMetaEngine::fallbackDetect(const FileMap &allFiles,
 	Made::g_fallbackDesc.version = 3;
 
 	//return (const ADGameDescription *)&Made::g_fallbackDesc;
-	return NULL;
+	return ADDetectedGame();
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(MADE)
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 68f4b36..9a0280d 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -79,7 +79,7 @@ public:
 	 * (possibly empty) list of games supported by the engine which it was able
 	 * to detect amongst the given files.
 	 */
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const = 0;
+	virtual DetectedGames detectGames(const Common::FSList &fslist) const = 0;
 
 	/**
 	 * Tries to instantiate an engine instance based on the settings of
@@ -269,7 +269,7 @@ class EngineManager : public Common::Singleton<EngineManager> {
 public:
 	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, bool useUnknownGameDialog = false) const;
+	DetectionResults detectGames(const Common::FSList &fslist) const;
 	const PluginList &getPlugins() const;
 };
 
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 82f901d..94ca4f0 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -184,7 +184,7 @@ public:
 		_directoryGlobs = directoryGlobs;
 	}
 
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
 		return detectGameFilebased(allFiles, fslist, Mohawk::fileBased);
 	}
 
diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp
index aed8b7d..f7cde05 100644
--- a/engines/queen/detection.cpp
+++ b/engines/queen/detection.cpp
@@ -447,7 +447,7 @@ public:
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual void removeSaveState(const char *target, int slot) const;
 
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 };
 
 bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -457,7 +457,7 @@ bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsDeleteSave);
 }
 
-const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ADDetectedGame QueenMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
 	static ADGameDescription desc;
 
 	// Iterate over all files in the given directory
@@ -492,11 +492,13 @@ const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles
 					desc.extra = "Talkie";
 					desc.guiOptions = GAMEOPTION_ALT_INTRO;
 				}
-				return (const ADGameDescription *)&desc;
+
+				return ADDetectedGame(&desc);
 			}
 		}
 	}
-	return 0;
+
+	return ADDetectedGame();
 }
 
 SaveStateList QueenMetaEngine::listSaves(const char *target) const {
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index bc72412..9a70429 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -562,7 +562,7 @@ public:
 	}
 
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
@@ -590,7 +590,7 @@ Common::Language charToScummVMLanguage(const char c) {
 	}
 }
 
-const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ADDetectedGame SciMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
 	bool foundResMap = false;
 	bool foundRes000 = false;
 
@@ -647,7 +647,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 
 	// If these files aren't found, it can't be SCI
 	if (!foundResMap && !foundRes000)
-		return 0;
+		return ADDetectedGame();
 
 	ResourceManager resMan(true);
 	resMan.addAppropriateSourcesForDetection(fslist);
@@ -658,7 +658,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 	// Is SCI32 compiled in? If not, and this is a SCI32 game,
 	// stop here
 	if (getSciVersionForDetection() >= SCI_VERSION_2)
-		return 0;
+		return ADDetectedGame();
 #endif
 
 	ViewType gameViews = resMan.getViewType();
@@ -667,7 +667,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 	// Can't be SCI (or unsupported SCI views). Pinball Creep by Sierra also uses resource.map/resource.000 files
 	// but doesn't share SCI format at all
 	if (gameViews == kViewUnknown)
-		return 0;
+		return ADDetectedGame();
 
 	// Set the platform to Amiga if the game is using Amiga views
 	if (gameViews == kViewAmiga)
@@ -678,7 +678,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 
 	// If we don't have a game id, the game is not SCI
 	if (sierraGameId.empty())
-		return 0;
+		return ADDetectedGame();
 
 	Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
 	strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
@@ -753,7 +753,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 			s_fallbackDesc.extra = "CD";
 	}
 
-	return &s_fallbackDesc;
+	return ADDetectedGame(&s_fallbackDesc);
 }
 
 bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 9573db5..078cc8d 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -961,7 +961,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
 	virtual GameDescriptor findGame(const char *gameid) const;
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const;
+	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
@@ -1026,29 +1026,30 @@ static Common::String generatePreferredTarget(const DetectorResult &x) {
 	return res;
 }
 
-GameList ScummMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
-	GameList detectedGames;
+DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
+	DetectedGames detectedGames;
 	Common::List<DetectorResult> results;
-
 	::detectGames(fslist, results, 0);
 
 	for (Common::List<DetectorResult>::iterator
 	          x = results.begin(); x != results.end(); ++x) {
 		const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions);
 		assert(g);
-		GameDescriptor dg(x->game.gameid, g->description, x->language, x->game.platform);
+
+		DetectedGame game;
+		game.matchedGame = GameDescriptor(x->game.gameid, g->description, x->language, x->game.platform);
 
 		// Append additional information, if set, to the description.
-		dg.updateDesc(x->extra);
+		game.matchedGame.updateDesc(x->extra);
 
 		// Compute and set the preferred target name for this game.
 		// Based on generateComplexID() in advancedDetector.cpp.
-		dg["preferredtarget"] = generatePreferredTarget(*x);
+		game.matchedGame["preferredtarget"] = generatePreferredTarget(*x);
 
-		dg.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
-		dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
+		game.matchedGame.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
+		game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
 
-		detectedGames.push_back(dg);
+		detectedGames.push_back(game);
 	}
 
 	return detectedGames;
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index a74b63f..cc1f1ad 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -79,7 +79,7 @@ public:
 	virtual GameList getSupportedGames() const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual GameDescriptor findGame(const char *gameid) const;
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const;
+	DetectedGames detectGames(const Common::FSList &fslist) const override;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
@@ -141,8 +141,8 @@ GameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
 	return GameDescriptor();
 }
 
-GameList SkyMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
-	GameList detectedGames;
+DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
+	DetectedGames detectedGames;
 	bool hasSkyDsk = false;
 	bool hasSkyDnr = false;
 	int dinnerTableEntries = -1;
@@ -173,18 +173,19 @@ GameList SkyMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnkn
 		// Match found, add to list of candidates, then abort inner loop.
 		// The game detector uses US English by default. We want British
 		// English to match the recorded voices better.
-		GameDescriptor dg(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
+		DetectedGame game;
+		game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
 		const SkyVersion *sv = skyVersions;
 		while (sv->dinnerTableEntries) {
 			if (dinnerTableEntries == sv->dinnerTableEntries &&
 				(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
-				dg.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
-				dg.setGUIOptions(sv->guioptions);
+				game.matchedGame.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
+				game.matchedGame.setGUIOptions(sv->guioptions);
 				break;
 			}
 			++sv;
 		}
-		detectedGames.push_back(dg);
+		detectedGames.push_back(game);
 	}
 
 	return detectedGames;
diff --git a/engines/sludge/detection.cpp b/engines/sludge/detection.cpp
index a530a5c..85c0f22 100644
--- a/engines/sludge/detection.cpp
+++ b/engines/sludge/detection.cpp
@@ -100,10 +100,10 @@ public:
 	}
 
 	// for fall back detection
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 };
 
-const ADGameDescription *SludgeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ADDetectedGame SludgeMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
 	// reset fallback description
 	s_fallbackDesc.desc.gameId = "sludge";
 	s_fallbackDesc.desc.extra = "";
@@ -147,9 +147,19 @@ const ADGameDescription *SludgeMetaEngine::fallbackDetect(const FileMap &allFile
 		s_fallbackFileNameBuffer[50] = '\0';
 		s_fallbackDesc.desc.filesDescriptions[0].fileName = s_fallbackFileNameBuffer;
 
-		return (const ADGameDescription *)&s_fallbackDesc;
+		ADDetectedGame game;
+		game.desc = &s_fallbackDesc.desc;
+
+		FileProperties tmp;
+		if (getFileProperties(file->getParent(), allFiles, s_fallbackDesc.desc, fileName, tmp)) {
+			game.hasUnknownFiles = true;
+			game.matchedFiles[fileName] = tmp;
+		}
+
+		return game;
 	}
-	return 0;
+
+	return ADDetectedGame();
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(SLUDGE)
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index ddfc4b8..7fb86fe 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -89,7 +89,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
 	virtual GameDescriptor findGame(const char *gameid) const;
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const;
+	DetectedGames detectGames(const Common::FSList &fslist) const override;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
@@ -175,9 +175,9 @@ void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool r
 	}
 }
 
-GameList SwordMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
+DetectedGames SwordMetaEngine::detectGames(const Common::FSList &fslist) const {
 	int i, j;
-	GameList detectedGames;
+	DetectedGames detectedGames;
 	bool filesFound[NUM_FILES_TO_CHECK];
 	for (i = 0; i < NUM_FILES_TO_CHECK; i++)
 		filesFound[i] = false;
@@ -212,31 +212,31 @@ GameList SwordMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUn
 		if (!filesFound[i] || psxFilesFound)
 			psxDemoFilesFound = false;
 
-	GameDescriptor gd;
+	DetectedGame game;
 	if (mainFilesFound && pcFilesFound && demoFilesFound)
-		gd = GameDescriptor(sword1DemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1DemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else if (mainFilesFound && pcFilesFound && psxFilesFound)
-		gd = GameDescriptor(sword1PSXSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1PSXSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else if (mainFilesFound && pcFilesFound && psxDemoFilesFound)
-		gd = GameDescriptor(sword1PSXDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1PSXDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else if (mainFilesFound && pcFilesFound && !psxFilesFound)
-		gd = GameDescriptor(sword1FullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1FullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else if (mainFilesFound && macFilesFound)
-		gd = GameDescriptor(sword1MacFullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1MacFullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else if (mainFilesFound && macDemoFilesFound)
-		gd = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 	else
 		return detectedGames;
 
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
-	gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
+	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
 
-	detectedGames.push_back(gd);
+	detectedGames.push_back(game);
 
 	return detectedGames;
 }
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 10ddda7..27fcc74 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -95,7 +95,7 @@ public:
 	virtual GameList getSupportedGames() const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual GameDescriptor findGame(const char *gameid) const;
-	virtual GameList detectGames(const Common::FSList &fslist, bool useUnknownGameDialog = false) const;
+	virtual DetectedGames detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
@@ -148,7 +148,7 @@ GameDescriptor Sword2MetaEngine::findGame(const char *gameid) const {
 bool isFullGame(const Common::FSList &fslist) {
 	Common::FSList::const_iterator file;
 
-	// We distinguish between the two versions by the presense of paris.clu
+	// We distinguish between the two versions by the presence of paris.clu
 	for (file = fslist.begin(); file != fslist.end(); ++file) {
 		if (!file->isDirectory()) {
 			if (file->getName().equalsIgnoreCase("paris.clu"))
@@ -159,8 +159,8 @@ bool isFullGame(const Common::FSList &fslist) {
 	return false;
 }
 
-GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
-	GameList detectedGames;
+DetectedGames detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
+	DetectedGames detectedGames;
 	const Sword2::GameSettings *g;
 	Common::FSList::const_iterator file;
 	bool isFullVersion = isFullGame(fslist);
@@ -192,7 +192,10 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
 						continue;
 
 					// Match found, add to list of candidates, then abort inner loop.
-					detectedGames.push_back(GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)));
+					DetectedGame game;
+					game.matchedGame = GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+
+					detectedGames.push_back(game);
 					break;
 				}
 			}
@@ -208,7 +211,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
 				if (file->getName().equalsIgnoreCase("clusters")) {
 					Common::FSList recList;
 					if (file->getChildren(recList, Common::FSNode::kListAll)) {
-						GameList recGames(detectGamesImpl(recList, true));
+						DetectedGames recGames = detectGamesImpl(recList, true);
 						if (!recGames.empty()) {
 							detectedGames.push_back(recGames);
 							break;
@@ -223,7 +226,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
 	return detectedGames;
 }
 
-GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
+DetectedGames Sword2MetaEngine::detectGames(const Common::FSList &fslist) const {
 	return detectGamesImpl(fslist);
 }
 
@@ -278,10 +281,10 @@ Common::Error Sword2MetaEngine::createInstance(OSystem *syst, Engine **engine) c
 
 	// Invoke the detector
 	Common::String gameid = ConfMan.get("gameid");
-	GameList detectedGames = detectGames(fslist);
+	DetectedGames detectedGames = detectGames(fslist);
 
 	for (uint i = 0; i < detectedGames.size(); i++) {
-		if (detectedGames[i].gameid() == gameid) {
+		if (detectedGames[i].matchedGame.gameid() == gameid) {
 			*engine = new Sword2::Sword2Engine(syst);
 			return Common::kNoError;
 		}
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index d6bcfe5..1c60c5e 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -97,7 +97,7 @@ public:
 	}
 
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
-	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
@@ -185,7 +185,7 @@ typedef Common::Array<const ADGameDescription *> ADGameDescList;
  * Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation
  * where the files haven't been renamed (i.e. don't have the '1' just before the extension)
  */
-const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const {
+ADDetectedGame TinselMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const {
 	Common::String extra;
 	FileMap allFiles;
 	SizeMD5Map filesSizeMD5;
@@ -194,7 +194,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
 	const Tinsel::TinselGameDescription *g;
 
 	if (fslist.empty())
-		return NULL;
+		return ADDetectedGame();
 
 	// TODO: The following code is essentially a slightly modified copy of the
 	// complete code of function detectGame() in engines/advancedDetector.cpp.
@@ -262,7 +262,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
 		}
 	}
 
-	ADGameDescList matched;
+	ADDetectedGame matched;
 	int maxFilesMatched = 0;
 
 	// MD5 based matching
@@ -310,22 +310,15 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
 			for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
 				curFilesMatched++;
 
-			if (curFilesMatched > maxFilesMatched) {
+			if (curFilesMatched >= maxFilesMatched) {
 				maxFilesMatched = curFilesMatched;
 
-				matched.clear();	// Remove any prior, lower ranked matches.
-				matched.push_back((const ADGameDescription *)g);
-			} else if (curFilesMatched == maxFilesMatched) {
-				matched.push_back((const ADGameDescription *)g);
+				matched = ADDetectedGame(&g->desc);
 			}
 		}
 	}
 
-	// We didn't find a match
-	if (matched.empty())
-		return NULL;
-
-	return *matched.begin();
+	return matched;
 }
 
 int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index 6eb38c4..634d286 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -132,7 +132,7 @@ public:
 		_directoryGlobs = directoryGlobs;
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
 		return detectGameFilebased(allFiles, fslist, Toon::fileBasedFallback);
 	}
 
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index dcb58ff..51b17b2 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -133,15 +133,8 @@ public:
 		_directoryGlobs = directoryGlobs;
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
-		ADFilePropertiesMap filesProps;
-
-		const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback, &filesProps);
-		if (!matchedDesc)
-			return 0;
-
-		reportUnknown(fslist.begin()->getParent(), filesProps);
-		return matchedDesc;
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
+		return detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback);
 	}
 
 	virtual const char *getName() const {
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index 119d60f..2318947 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -149,18 +149,19 @@ public:
 		return desc != 0;
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
 		for (Common::FSList::const_iterator d = fslist.begin(); d != fslist.end(); ++d) {
 			Common::FSList audiofslist;
 			if (d->isDirectory() && d->getName().equalsIgnoreCase("audio") && d->getChildren(audiofslist, Common::FSNode::kListFilesOnly)) {
 				for (Common::FSList::const_iterator f = audiofslist.begin(); f != audiofslist.end(); ++f) {
 					if (!f->isDirectory() && f->getName().equalsIgnoreCase("demorolc.raw")) {
-						return &tuckerDemoGameDescription;
+						return ADDetectedGame(&tuckerDemoGameDescription);
 					}
 				}
 			}
 		}
-		return 0;
+
+		return ADDetectedGame();
 	}
 
 	virtual SaveStateList listSaves(const char *target) const {
diff --git a/engines/unknown-game-dialog.cpp b/engines/unknown-game-dialog.cpp
index f737eb1..1b7dd73 100644
--- a/engines/unknown-game-dialog.cpp
+++ b/engines/unknown-game-dialog.cpp
@@ -20,14 +20,16 @@
  *
  */
 
+#include "engines/unknown-game-dialog.h"
+
 #include "common/translation.h"
 #include "common/str-array.h"
 #include "common/system.h"
+
 #include "gui/gui-manager.h"
 #include "gui/message.h"
 #include "gui/ThemeEval.h"
 #include "gui/widgets/popup.h"
-#include "engines/unknown-game-dialog.h"
 
 enum {
 	kCopyToClipboard = 'cpcl',
@@ -35,36 +37,33 @@ enum {
 	kClose = 'clse'
 };
 
-UnknownGameDialog::UnknownGameDialog(const Common::String &reportData, const Common::String &reportTranslated, const Common::String &bugtrackerAffectedEngine)
-	: Dialog(30, 20, 260, 124) {
-
-	_reportData = reportData;
-	_reportTranslated = reportTranslated;
-	_bugtrackerAffectedEngine = bugtrackerAffectedEngine;
+UnknownGameDialog::UnknownGameDialog(const DetectionResults &detectionResults) :
+		Dialog(30, 20, 260, 124),
+		_detectionResults(detectionResults) {
+	Common::String reportTranslated = _detectionResults.generateUnknownGameReport(true);
 
-	//Check if we have clipboard functionality and expand the reportTranslated message if needed...
+	// Check if we have clipboard functionality and expand the reportTranslated message if needed...
 	if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
-		_reportTranslated += "\n";
-		_reportTranslated += _("Use the button below to copy the required game information into your clipboard.");
+		reportTranslated += "\n";
+		reportTranslated += _("Use the button below to copy the required game information into your clipboard.");
 	}
 
 #if 0
-	//Check if we have support for opening URLs and expand the reportTranslated message if needed...
+	// Check if we have support for opening URLs and expand the reportTranslated message if needed...
 	if (g_system->hasFeature(OSystem::kFeatureOpenUrl)) {
-		_reportTranslated += "\n";
-		_reportTranslated += _("You can also directly report your game to the Bug Tracker!");
+		reportTranslated += "\n";
+		reportTranslated += _("You can also directly report your game to the Bug Tracker.");
 	}
 #endif
 
 	const int screenW = g_system->getOverlayWidth();
-	const int screenH = g_system->getOverlayHeight();
 
 	int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0);
 	int buttonHeight = g_gui.xmlEval()->getVar("Globals.Button.Height", 0);
 
-	//Calculate the size the dialog needs
+	// Calculate the size the dialog needs
 	Common::Array<Common::String> lines;
-	int maxlineWidth = g_gui.getFont().wordWrapText(_reportTranslated, screenW - 2 * 20, lines);
+	int maxlineWidth = g_gui.getFont().wordWrapText(reportTranslated, screenW - 2 * 20, lines);
 	int lineCount = lines.size() + 1;
 
 	_h =  3 * kLineHeight + lineCount * kLineHeight;
@@ -84,7 +83,7 @@ UnknownGameDialog::UnknownGameDialog(const Common::String &reportData, const Com
 	int buttonPos = _w - closeButtonWidth - 10;
 	new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, _("Close"), 0, kClose);
 
-	//Check if we have clipboard functionality
+	// Check if we have clipboard functionality
 	if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
 		buttonPos -= copyToClipboardButtonWidth + 5;
 		new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, copyToClipboardButtonWidth, buttonHeight, _("Copy to clipboard"), 0, kCopyToClipboard);
@@ -98,15 +97,10 @@ UnknownGameDialog::UnknownGameDialog(const Common::String &reportData, const Com
 	// https://www.scummvm.org/unknowngame?engine=Foo&description=Bar) that would
 	// redirect to whatever our bugtracker system is.
 
-	//Check if we have support for opening URLs
+	// Check if we have support for opening URLs
 	if (g_system->hasFeature(OSystem::kFeatureOpenUrl)) {
 		buttonPos -= openBugtrackerURLButtonWidth + 5;
 		new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, openBugtrackerURLButtonWidth, buttonHeight, _("Report game"), 0, kOpenBugtrackerURL);
-		//Formatting the reportData for bugtracker submission [replace line breaks]...
-		_bugtrackerGameData = _reportData;
-		while (_bugtrackerGameData.contains("\n")) {
-			Common::replace(_bugtrackerGameData, "\n", "%0A");
-		}
 	}
 #endif
 
@@ -126,27 +120,39 @@ void UnknownGameDialog::reflowLayout() {
 }
 
 Common::String UnknownGameDialog::generateBugtrackerURL() {
-	return Common::String::format((
+	// TODO: Remove the filesystem path from the bugtracker report
+	Common::String report = _detectionResults.generateUnknownGameReport(false);
+
+	// Formatting the report for bugtracker submission [replace line breaks]...
+	while (report.contains("\n")) {
+		Common::replace(report, "\n", "%0A");
+	}
+
+	return Common::String::format(
 		"https://bugs.scummvm.org/newticket?"
-		"summary=[UNK] Unknown game for engine %s:"
 		"&description=%s"
-		"&component=Engine%%3A%s"
 		"&type=enhancement"
-		"&keywords=unknown-game,%s"),
-		_bugtrackerAffectedEngine.c_str(), _bugtrackerGameData.c_str(), _bugtrackerAffectedEngine.c_str(), _bugtrackerAffectedEngine.c_str());
+		"&keywords=unknown-game",
+		report.c_str());
 }
 
 void UnknownGameDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
 	switch(cmd) {
-	case kCopyToClipboard:
-		g_system->setTextInClipboard(_reportData);
-		if (g_system->setTextInClipboard(_reportData)) {
-			g_system->displayMessageOnOSD(_("All necessary information about your game has been copied into the clipboard"));
+	case kCopyToClipboard: {
+		// TODO: Remove the filesystem path from the report
+		Common::String report = _detectionResults.generateUnknownGameReport(false);
+
+		if (g_system->setTextInClipboard(report)) {
+			g_system->displayMessageOnOSD(
+					_("All necessary information about your game has been copied into the clipboard"));
 		} else {
 			g_system->displayMessageOnOSD(_("Copying the game information to the clipboard has failed!"));
 		}
 		break;
+	}
 	case kClose:
+		// When the detection entry comes from the fallback detector, the game can be added / launched anyways.
+		// TODO: Add a button to cancel adding the game. And make it clear that launching the game may not work properly.
 		close();
 		break;
 	case kOpenBugtrackerURL:
diff --git a/engines/unknown-game-dialog.h b/engines/unknown-game-dialog.h
index 51adf27..0878406 100644
--- a/engines/unknown-game-dialog.h
+++ b/engines/unknown-game-dialog.h
@@ -22,16 +22,18 @@
 
 #include "gui/dialog.h"
 
+#include "engines/metaengine.h"
+
 class UnknownGameDialog : public GUI::Dialog {
 public:
-	UnknownGameDialog(const Common::String &reportData, const Common::String &reportTranslated, const Common::String &bugtrackerAffectedEngine);
-	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
-	virtual Common::String generateBugtrackerURL();
-	virtual void reflowLayout();
+	UnknownGameDialog(const DetectionResults &detectionResults);
 
 private:
-	Common::String _reportData;
-	Common::String _reportTranslated;
-	Common::String _bugtrackerGameData;
-	Common::String _bugtrackerAffectedEngine;
+	// Dialog API
+	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+	void reflowLayout() override;
+
+	Common::String generateBugtrackerURL();
+
+	const DetectionResults &_detectionResults;
 };
diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp
index df5cc41..6208d77 100644
--- a/engines/wintermute/detection.cpp
+++ b/engines/wintermute/detection.cpp
@@ -100,7 +100,7 @@ public:
 		return "Copyright (C) 2011 Jan Nedoma";
 	}
 
-	virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override {
 		// Set some defaults
 		s_fallbackDesc.extra = "";
 		s_fallbackDesc.language = Common::UNK_LANG;
@@ -130,10 +130,12 @@ public:
 					s_fallbackDesc.extra = offset;
 					s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE;
 				}
-				return &s_fallbackDesc;
+
+				return ADDetectedGame(&s_fallbackDesc);
 			} // Fall through to return 0;
 		}
-		return 0;
+
+		return ADDetectedGame();
 	}
 
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 857d7d0..b14be6c 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -45,6 +45,7 @@
 #include "gui/EventRecorder.h"
 #endif
 #include "gui/saveload.h"
+#include "engines/unknown-game-dialog.h"
 #include "gui/widgets/edittext.h"
 #include "gui/widgets/list.h"
 #include "gui/widgets/tab.h"
@@ -573,7 +574,17 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 
 	// ...so let's determine a list of candidates, games that
 	// could be contained in the specified directory.
-	GameList candidates(EngineMan.detectGames(files, true));
+	DetectionResults detectionResults = EngineMan.detectGames(files);
+
+	if (detectionResults.foundUnknownGames()) {
+		Common::String report = detectionResults.generateUnknownGameReport(false, 80);
+		g_system->logMessage(LogMessageType::kInfo, report.c_str());
+
+		UnknownGameDialog dialog(detectionResults);
+		dialog.runModal();
+	}
+
+	Common::Array<DetectedGame> candidates = detectionResults.listRecognizedGames();
 
 	int idx;
 	if (candidates.empty()) {
@@ -589,17 +600,14 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 		// Display the candidates to the user and let her/him pick one
 		StringArray list;
 		for (idx = 0; idx < (int)candidates.size(); idx++)
-			list.push_back(candidates[idx].description());
+			list.push_back(candidates[idx].matchedGame.description());
 
 		ChooserDialog dialog(_("Pick the game:"));
 		dialog.setList(list);
 		idx = dialog.runModal();
 	}
 	if (0 <= idx && idx < (int)candidates.size()) {
-		GameDescriptor result = candidates[idx];
-
-		// TODO: Change the detectors to set "path" !
-		result["path"] = dir.getPath();
+		const GameDescriptor &result = candidates[idx].matchedGame;
 
 		Common::String domain = addGameToConf(result);
 
diff --git a/gui/massadd.cpp b/gui/massadd.cpp
index db569fa..2774d47 100644
--- a/gui/massadd.cpp
+++ b/gui/massadd.cpp
@@ -187,7 +187,12 @@ void MassAddDialog::handleTickle() {
 		}
 
 		// Run the detector on the dir
-		GameList candidates(EngineMan.detectGames(files));
+		DetectionResults detectionResults = EngineMan.detectGames(files);
+
+		if (detectionResults.foundUnknownGames()) {
+			Common::String report = detectionResults.generateUnknownGameReport(false, 80);
+			g_system->logMessage(LogMessageType::kInfo, report.c_str());
+		}
 
 		// Just add all detected games / game variants. If we get more than one,
 		// that either means the directory contains multiple games, or the detector
@@ -195,8 +200,9 @@ void MassAddDialog::handleTickle() {
 		// case, let the user choose which entries he wants to keep.
 		//
 		// However, we only add games which are not already in the config file.
-		for (GameList::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
-			GameDescriptor result = *cand;
+		DetectedGames candidates = detectionResults.listRecognizedGames();
+		for (DetectedGames::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
+			const GameDescriptor &result = cand->matchedGame;
 			Common::String path = dir.getPath();
 
 			// Remove trailing slashes
@@ -224,7 +230,6 @@ void MassAddDialog::handleTickle() {
 					break;	// Skip duplicates
 				}
 			}
-			result["path"] = path;
 			_games.push_back(result);
 
 			_list->append(result.description());


Commit: 8fb149e3c7603f023dfccf2b2056a9a2fda431c2
    https://github.com/scummvm/scummvm/commit/8fb149e3c7603f023dfccf2b2056a9a2fda431c2
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Change MetaEngine::findGame to return a plain game descriptor

Changed paths:
    base/commandLine.cpp
    base/main.cpp
    base/plugins.cpp
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/agos/detection.cpp
    engines/cine/detection.cpp
    engines/game.h
    engines/gob/detection/detection.cpp
    engines/metaengine.h
    engines/obsolete.cpp
    engines/obsolete.h
    engines/saga/detection.cpp
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    gui/EventRecorder.cpp
    gui/editgamedialog.cpp
    gui/editgamedialog.h
    gui/launcher.cpp
    gui/recorderdialog.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 8e70140..0755165 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -714,10 +714,10 @@ static void listTargets() {
 			// FIXME: At this point, we should check for a "gameid" override
 			// to find the proper desc. In fact, the platform probably should
 			// be taken into account, too.
-			Common::String gameid(name);
-			GameDescriptor g = EngineMan.findGame(gameid);
-			if (g.description().size() > 0)
-				description = g.description();
+			const Common::String &gameid = name;
+			PlainGameDescriptor g = EngineMan.findGame(gameid);
+			if (g.description)
+				description = g.description;
 		}
 
 		targets.push_back(Common::String::format("%-20s %s", name.c_str(), description.c_str()));
@@ -770,7 +770,7 @@ static Common::Error listSaves(const Common::String &target) {
 
 		// Find the plugin that will handle the specified gameid
 		const Plugin *plugin = nullptr;
-		GameDescriptor game = EngineMan.findGame(gameid, &plugin);
+		EngineMan.findGame(gameid, &plugin);
 
 		if (!plugin) {
 			// If the target was specified, treat this as an error, and otherwise skip it.
@@ -1276,8 +1276,8 @@ bool processSettings(Common::String &command, Common::StringMap &settings, Commo
 	// domain (i.e. a target) matching this argument, or alternatively
 	// whether there is a gameid matching that name.
 	if (!command.empty()) {
-		GameDescriptor gd = EngineMan.findGame(command);
-		if (ConfMan.hasGameDomain(command) || !gd.gameid().empty()) {
+		PlainGameDescriptor gd = EngineMan.findGame(command);
+		if (ConfMan.hasGameDomain(command) || gd.gameId) {
 			bool idCameFromCommandLine = false;
 
 			// WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching
diff --git a/base/main.cpp b/base/main.cpp
index 8e783c9..385b8f3 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -128,13 +128,13 @@ static const Plugin *detectPlugin() {
 	printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveDomainName().c_str(), gameid.c_str());
 	printf("  Looking for a plugin supporting this gameid... ");
 
- 	GameDescriptor game = EngineMan.findGame(gameid, &plugin);
+	PlainGameDescriptor game = EngineMan.findGame(gameid, &plugin);
 
 	if (plugin == 0) {
 		printf("failed\n");
 		warning("%s is an invalid gameid. Use the --list-games option to list supported gameid", gameid.c_str());
 	} else {
-		printf("%s\n  Starting '%s'\n", plugin->getName(), game.description().c_str());
+		printf("%s\n  Starting '%s'\n", plugin->getName(), game.description);
 	}
 
 	return plugin;
@@ -210,7 +210,10 @@ static Common::Error runGame(const Plugin *plugin, OSystem &system, const Common
 	Common::String caption(ConfMan.get("description"));
 
 	if (caption.empty()) {
-		caption = EngineMan.findGame(ConfMan.get("gameid")).description();
+		PlainGameDescriptor game = EngineMan.findGame(ConfMan.get("gameid"));
+		if (game.description) {
+			caption = game.description;
+		}
 	}
 	if (caption.empty())
 		caption = ConfMan.getActiveDomainName(); // Use the domain (=target) name
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 02f6998..25dd3e1 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -458,13 +458,13 @@ 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 Plugin **plugin) const {
-	GameDescriptor result;
+PlainGameDescriptor EngineManager::findGame(const Common::String &gameName, const Plugin **plugin) const {
+	PlainGameDescriptor result;
 
 	// First look for the game using the plugins in memory. This is critical
 	// for calls coming from inside games
 	result = findGameInLoadedPlugins(gameName, plugin);
-	if (!result.gameid().empty()) {
+	if (result.gameId) {
 		return result;
 	}
 
@@ -472,7 +472,7 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Plu
 	// by plugin
 	if (PluginMan.loadPluginFromGameId(gameName))  {
 		result = findGameInLoadedPlugins(gameName, plugin);
-		if (!result.gameid().empty()) {
+		if (result.gameId) {
 			return result;
 		}
 	}
@@ -481,7 +481,7 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Plu
 	PluginMan.loadFirstPlugin();
 	do {
 		result = findGameInLoadedPlugins(gameName, plugin);
-		if (!result.gameid().empty()) {
+		if (result.gameId) {
 			// Update with new plugin file name
 			PluginMan.updateConfigWithFileName(gameName);
 			break;
@@ -494,10 +494,10 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Plu
 /**
  * Find the game within the plugins loaded in memory
  **/
-GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
+PlainGameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
 	// Find the GameDescriptor for this target
 	const PluginList &plugins = getPlugins();
-	GameDescriptor result;
+	PlainGameDescriptor result;
 
 	if (plugin)
 		*plugin = 0;
@@ -506,7 +506,7 @@ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &game
 
 	for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
 		result = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
-		if (!result.gameid().empty()) {
+		if (result.gameId) {
 			if (plugin)
 				*plugin = *iter;
 			return result;
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 9c1a700..43c2082 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -602,14 +602,14 @@ GameList AdvancedMetaEngine::getSupportedGames() const {
 	return GameList(_gameIds);
 }
 
-GameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const {
+PlainGameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const {
 	// First search the list of supported gameids for a match.
 	const PlainGameDescriptor *g = findPlainGameDescriptor(gameId, _gameIds);
 	if (g)
-		return GameDescriptor(*g);
+		return *g;
 
 	// No match found
-	return GameDescriptor();
+	return PlainGameDescriptor();
 }
 
 AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions)
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index f1f55d0..ec52134 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -267,7 +267,7 @@ public:
 	 */
 	virtual GameList getSupportedGames() const;
 
-	virtual GameDescriptor findGame(const char *gameId) const;
+	PlainGameDescriptor findGame(const char *gameId) const override;
 
 	DetectedGames detectGames(const Common::FSList &fslist) const override;
 
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index dbc4ee9..1847434 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -99,7 +99,7 @@ public:
 		_directoryGlobs = directoryGlobs;
 	}
 
-	virtual GameDescriptor findGame(const char *gameId) const {
+	PlainGameDescriptor findGame(const char *gameId) const override {
 		return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
 	}
 
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index 6c8b4a6..f1636c9 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -84,7 +84,7 @@ public:
 		_guiOptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD);
 	}
 
-	virtual GameDescriptor findGame(const char *gameId) const {
+	PlainGameDescriptor findGame(const char *gameId) const override {
 		return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
 	}
 
diff --git a/engines/game.h b/engines/game.h
index 54c1af3..88033dc 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -38,6 +38,9 @@
 struct PlainGameDescriptor {
 	const char *gameId;
 	const char *description;
+
+	PlainGameDescriptor() : gameId(nullptr), description(nullptr) {}
+	PlainGameDescriptor(const char *id, const char *desc) : gameId(id), description(desc) {}
 };
 
 /**
@@ -66,7 +69,7 @@ enum GameSupportLevel {
 class GameDescriptor : public Common::StringMap {
 public:
 	GameDescriptor();
-	GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions = Common::String());
+	explicit GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions = Common::String());
 	GameDescriptor(const Common::String &gameid,
 	              const Common::String &description,
 	              Common::Language language = Common::UNK_LANG,
diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp
index 487b65f..864a701 100644
--- a/engines/gob/detection/detection.cpp
+++ b/engines/gob/detection/detection.cpp
@@ -33,7 +33,7 @@ class GobMetaEngine : public AdvancedMetaEngine {
 public:
 	GobMetaEngine();
 
-	virtual GameDescriptor findGame(const char *gameId) const;
+	PlainGameDescriptor findGame(const char *gameId) const override;
 
 	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
 
@@ -59,7 +59,7 @@ GobMetaEngine::GobMetaEngine() :
 	_guiOptions = GUIO1(GUIO_NOLAUNCHLOAD);
 }
 
-GameDescriptor GobMetaEngine::findGame(const char *gameId) const {
+PlainGameDescriptor GobMetaEngine::findGame(const char *gameId) const {
 	return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
 }
 
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 9a0280d..9ce8dc9 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -71,8 +71,8 @@ public:
 	/** Returns a list of games supported by this engine. */
 	virtual GameList getSupportedGames() const = 0;
 
-	/** Query the engine for a GameDescriptor for the specified gameid, if any. */
-	virtual GameDescriptor findGame(const char *gameid) const = 0;
+	/** Query the engine for a PlainGameDescriptor for the specified gameid, if any. */
+	virtual PlainGameDescriptor findGame(const char *gameId) const = 0;
 
 	/**
 	 * Runs the engine's game detector on the given list of files, and returns a
@@ -267,8 +267,8 @@ public:
  */
 class EngineManager : public Common::Singleton<EngineManager> {
 public:
-	GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const;
-	GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const;
+	PlainGameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const;
+	PlainGameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const;
 	DetectionResults detectGames(const Common::FSList &fslist) const;
 	const PluginList &getPlugins() const;
 };
diff --git a/engines/obsolete.cpp b/engines/obsolete.cpp
index d65fb13..48809df 100644
--- a/engines/obsolete.cpp
+++ b/engines/obsolete.cpp
@@ -55,7 +55,7 @@ void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList) {
 	}
 }
 
-GameDescriptor findGameID(
+PlainGameDescriptor findGameID(
 	const char *gameid,
 	const PlainGameDescriptor *gameids,
 	const ObsoleteGameID *obsoleteList
@@ -63,7 +63,7 @@ GameDescriptor findGameID(
 	// First search the list of supported gameids for a match.
 	const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, gameids);
 	if (g)
-		return GameDescriptor(*g);
+		return *g;
 
 	// If we didn't find the gameid in the main list, check if it
 	// is an obsolete game id.
@@ -73,16 +73,16 @@ GameDescriptor findGameID(
 			if (0 == scumm_stricmp(gameid, o->from)) {
 				g = findPlainGameDescriptor(o->to, gameids);
 				if (g && g->description)
-					return GameDescriptor(gameid, "Obsolete game ID (" + Common::String(g->description) + ")");
+					return PlainGameDescriptor(gameid, g->description);
 				else
-					return GameDescriptor(gameid, "Obsolete game ID");
+					return PlainGameDescriptor(gameid, "Obsolete game ID");
 			}
 			o++;
 		}
 	}
 
 	// No match found
-	return GameDescriptor();
+	return PlainGameDescriptor();
 }
 
 } // End of namespace Engines
diff --git a/engines/obsolete.h b/engines/obsolete.h
index be0963a..7c7249e 100644
--- a/engines/obsolete.h
+++ b/engines/obsolete.h
@@ -66,7 +66,7 @@ void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList);
  * Optionally can take a list of obsolete game ids into account in order
  * to support obsolete gameids.
  */
-GameDescriptor findGameID(
+PlainGameDescriptor findGameID(
 	const char *gameid,
 	const PlainGameDescriptor *gameids,
 	const ObsoleteGameID *obsoleteList = 0
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index fcd7850..82c29d3 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -105,7 +105,7 @@ public:
 		_singleId = "saga";
 	}
 
-	virtual GameDescriptor findGame(const char *gameId) const {
+	PlainGameDescriptor findGame(const char *gameId) const override {
 		return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
 	}
 
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 078cc8d..2ba5bd3 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -960,7 +960,7 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
-	virtual GameDescriptor findGame(const char *gameid) const;
+	PlainGameDescriptor findGame(const char *gameid) const override;
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
@@ -996,7 +996,7 @@ GameList ScummMetaEngine::getSupportedGames() const {
 	return GameList(gameDescriptions);
 }
 
-GameDescriptor ScummMetaEngine::findGame(const char *gameid) const {
+PlainGameDescriptor ScummMetaEngine::findGame(const char *gameid) const {
 	return Engines::findGameID(gameid, gameDescriptions, obsoleteGameIDsTable);
 }
 
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index cc1f1ad..629996b 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -78,7 +78,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
-	virtual GameDescriptor findGame(const char *gameid) const;
+	PlainGameDescriptor findGame(const char *gameid) const override;
 	DetectedGames detectGames(const Common::FSList &fslist) const override;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
@@ -112,7 +112,7 @@ bool Sky::SkyEngine::hasFeature(EngineFeature f) const {
 
 GameList SkyMetaEngine::getSupportedGames() const {
 	GameList games;
-	games.push_back(skySetting);
+	games.push_back(GameDescriptor(skySetting));
 	return games;
 }
 
@@ -135,10 +135,10 @@ const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &ta
 	return options;
 }
 
-GameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
+PlainGameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
 	if (0 == scumm_stricmp(gameid, skySetting.gameId))
 		return skySetting;
-	return GameDescriptor();
+	return PlainGameDescriptor();
 }
 
 DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 7fb86fe..62e8f1c 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -88,7 +88,7 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
-	virtual GameDescriptor findGame(const char *gameid) const;
+	PlainGameDescriptor findGame(const char *gameId) const override;
 	DetectedGames detectGames(const Common::FSList &fslist) const override;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
@@ -127,20 +127,20 @@ GameList SwordMetaEngine::getSupportedGames() const {
 	return games;
 }
 
-GameDescriptor SwordMetaEngine::findGame(const char *gameid) const {
-	if (0 == scumm_stricmp(gameid, sword1FullSettings.gameId))
+PlainGameDescriptor SwordMetaEngine::findGame(const char *gameId) const {
+	if (0 == scumm_stricmp(gameId, sword1FullSettings.gameId))
 		return sword1FullSettings;
-	if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameId))
+	if (0 == scumm_stricmp(gameId, sword1DemoSettings.gameId))
 		return sword1DemoSettings;
-	if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameId))
+	if (0 == scumm_stricmp(gameId, sword1MacFullSettings.gameId))
 		return sword1MacFullSettings;
-	if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameId))
+	if (0 == scumm_stricmp(gameId, sword1MacDemoSettings.gameId))
 		return sword1MacDemoSettings;
-	if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameId))
+	if (0 == scumm_stricmp(gameId, sword1PSXSettings.gameId))
 		return sword1PSXSettings;
-	if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameId))
+	if (0 == scumm_stricmp(gameId, sword1PSXDemoSettings.gameId))
 		return sword1PSXDemoSettings;
-	return GameDescriptor();
+	return PlainGameDescriptor();
 }
 
 void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool recursion = false) {
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 27fcc74..bf2f329 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -94,7 +94,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
-	virtual GameDescriptor findGame(const char *gameid) const;
+	PlainGameDescriptor findGame(const char *gameid) const override;
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
@@ -135,14 +135,14 @@ const ExtraGuiOptions Sword2MetaEngine::getExtraGuiOptions(const Common::String
 	return options;
 }
 
-GameDescriptor Sword2MetaEngine::findGame(const char *gameid) const {
+PlainGameDescriptor Sword2MetaEngine::findGame(const char *gameid) const {
 	const Sword2::GameSettings *g = Sword2::sword2_settings;
 	while (g->gameid) {
 		if (0 == scumm_stricmp(gameid, g->gameid))
 			break;
 		g++;
 	}
-	return GameDescriptor(g->gameid, g->description);
+	return PlainGameDescriptor(g->gameid, g->description);
 }
 
 bool isFullGame(const Common::FSList &fslist) {
diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp
index 8494054..560df0e 100644
--- a/gui/EventRecorder.cpp
+++ b/gui/EventRecorder.cpp
@@ -600,13 +600,13 @@ void EventRecorder::setFileHeader() {
 		return;
 	}
 	TimeDate t;
-	GameDescriptor desc = EngineMan.findGame(ConfMan.getActiveDomainName());
+	PlainGameDescriptor desc = EngineMan.findGame(ConfMan.getActiveDomainName());
 	g_system->getTimeAndDate(t);
 	if (_author.empty()) {
 		setAuthor("Unknown Author");
 	}
 	if (_name.empty()) {
-		g_eventRec.setName(Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description());
+		g_eventRec.setName(Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description);
 	}
 	_playbackFile->getHeader().author = _author;
 	_playbackFile->getHeader().notes = _desc;
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
index 4192c40..c4cf6e7 100644
--- a/gui/editgamedialog.cpp
+++ b/gui/editgamedialog.cpp
@@ -95,7 +95,7 @@ protected:
 	}
 };
 
-EditGameDialog::EditGameDialog(const String &domain, const String &desc)
+EditGameDialog::EditGameDialog(const String &domain)
 	: OptionsDialog(domain, "GameOptions") {
 	// Retrieve all game specific options.
 	const Plugin *plugin = nullptr;
@@ -106,7 +106,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
 		gameId = domain;
 	// Retrieve the plugin, since we need to access the engine's MetaEngine
 	// implementation.
-	EngineMan.findGame(gameId, &plugin);
+	PlainGameDescriptor pgd = EngineMan.findGame(gameId, &plugin);
 	if (plugin) {
 		_engineOptions = plugin->get<MetaEngine>().getExtraGuiOptions(domain);
 	} else {
@@ -120,8 +120,8 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
 
 	// GAME: Determine the description string
 	String description(ConfMan.get("description", domain));
-	if (description.empty() && !desc.empty()) {
-		description = desc;
+	if (description.empty() && pgd.description) {
+		description = pgd.description;
 	}
 
 	// GUI:  Add tab widget
diff --git a/gui/editgamedialog.h b/gui/editgamedialog.h
index a317e36..06a8514 100644
--- a/gui/editgamedialog.h
+++ b/gui/editgamedialog.h
@@ -62,7 +62,7 @@ class EditGameDialog : public OptionsDialog {
 	typedef Common::String String;
 	typedef Common::Array<Common::String> StringArray;
 public:
-	EditGameDialog(const String &domain, const String &desc);
+	EditGameDialog(const String &domain);
 
 	void open();
 	virtual void apply();
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index b14be6c..f257c53 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -267,9 +267,9 @@ void LauncherDialog::updateListing() {
 		if (gameid.empty())
 			gameid = iter->_key;
 		if (description.empty()) {
-			GameDescriptor g = EngineMan.findGame(gameid);
-			if (g.contains("description"))
-				description = g.description();
+			PlainGameDescriptor g = EngineMan.findGame(gameid);
+			if (g.description)
+				description = g.description;
 		}
 
 		if (description.empty()) {
@@ -443,7 +443,8 @@ void LauncherDialog::editGame(int item) {
 	String gameId(ConfMan.get("gameid", _domains[item]));
 	if (gameId.empty())
 		gameId = _domains[item];
-	EditGameDialog editDialog(_domains[item], EngineMan.findGame(gameId).description());
+
+	EditGameDialog editDialog(_domains[item]);
 	if (editDialog.runModal() > 0) {
 		// User pressed OK, so make changes permanent
 
@@ -612,7 +613,7 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 		Common::String domain = addGameToConf(result);
 
 		// Display edit dialog for the new entry
-		EditGameDialog editDialog(domain, result.description());
+		EditGameDialog editDialog(domain);
 		if (editDialog.runModal() > 0) {
 			// User pressed OK, so make changes permanent
 
diff --git a/gui/recorderdialog.cpp b/gui/recorderdialog.cpp
index cd89b58..7a2cd04 100644
--- a/gui/recorderdialog.cpp
+++ b/gui/recorderdialog.cpp
@@ -167,9 +167,9 @@ void RecorderDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	case kRecordCmd: {
 		TimeDate t;
 		Common::String gameId = ConfMan.get("gameid", _target);
-		GameDescriptor desc = EngineMan.findGame(gameId);
+		PlainGameDescriptor desc = EngineMan.findGame(gameId);
 		g_system->getTimeAndDate(t);
-		EditRecordDialog editDlg(_("Unknown Author"), Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description(), "");
+		EditRecordDialog editDlg(_("Unknown Author"), Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description, "");
 		if (editDlg.runModal() != kOKCmd) {
 			return;
 		}


Commit: 643c24db75797728087999abd8acf1ecc83757fa
    https://github.com/scummvm/scummvm/commit/643c24db75797728087999abd8acf1ecc83757fa
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Change MetaEngine::listSupportedGames to return plain game descriptors

Changed paths:
    base/commandLine.cpp
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/game.h
    engines/metaengine.h
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 0755165..1c7916c 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -687,9 +687,9 @@ static void listGames() {
 
 	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());
+		PlainGameList list = (*iter)->get<MetaEngine>().getSupportedGames();
+		for (PlainGameList::iterator v = list.begin(); v != list.end(); ++v) {
+			printf("%-20s %s\n", v->gameId, v->description);
 		}
 	}
 }
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 43c2082..562ad5d 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -583,14 +583,14 @@ ADDetectedGame AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles,
 	return result;
 }
 
-GameList AdvancedMetaEngine::getSupportedGames() const {
+PlainGameList AdvancedMetaEngine::getSupportedGames() const {
 	if (_singleId != NULL) {
-		GameList gl;
+		PlainGameList gl;
 
 		const PlainGameDescriptor *g = _gameIds;
 		while (g->gameId) {
 			if (0 == scumm_stricmp(_singleId, g->gameId)) {
-				gl.push_back(GameDescriptor(g->gameId, g->description));
+				gl.push_back(*g);
 
 				return gl;
 			}
@@ -599,7 +599,7 @@ GameList AdvancedMetaEngine::getSupportedGames() const {
 		error("Engine %s doesn't have its singleid specified in ids list", _singleId);
 	}
 
-	return GameList(_gameIds);
+	return PlainGameList(_gameIds);
 }
 
 PlainGameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const {
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index ec52134..5762009 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -265,7 +265,7 @@ public:
 	 * Returns list of targets supported by the engine.
 	 * Distinguishes engines with single ID
 	 */
-	virtual GameList getSupportedGames() const;
+	PlainGameList getSupportedGames() const override;
 
 	PlainGameDescriptor findGame(const char *gameId) const override;
 
diff --git a/engines/game.h b/engines/game.h
index 88033dc..a5cc4de 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -50,6 +50,18 @@ struct PlainGameDescriptor {
  */
 const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list);
 
+class PlainGameList : public Common::Array<PlainGameDescriptor> {
+public:
+	PlainGameList() {}
+	PlainGameList(const PlainGameList &list) : Common::Array<PlainGameDescriptor>(list) {}
+	PlainGameList(const PlainGameDescriptor *g) {
+		while (g->gameId) {
+			push_back(*g);
+			g++;
+		}
+	}
+};
+
 /**
  * Ths is an enum to describe how done a game is. This also indicates what level of support is expected.
  */
@@ -106,17 +118,7 @@ public:
 };
 
 /** List of games. */
-class GameList : public Common::Array<GameDescriptor> {
-public:
-	GameList() {}
-	GameList(const GameList &list) : Common::Array<GameDescriptor>(list) {}
-	GameList(const PlainGameDescriptor *g) {
-		while (g->gameId) {
-			push_back(GameDescriptor(*g));
-			g++;
-		}
-	}
-};
+typedef Common::Array<GameDescriptor> GameList;
 
 /**
  * A record describing the properties of a file. Used on the existing
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 9ce8dc9..74eb367 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -69,7 +69,7 @@ public:
 	virtual const char *getOriginalCopyright() const = 0;
 
 	/** Returns a list of games supported by this engine. */
-	virtual GameList getSupportedGames() const = 0;
+	virtual PlainGameList getSupportedGames() const = 0;
 
 	/** Query the engine for a PlainGameDescriptor for the specified gameid, if any. */
 	virtual PlainGameDescriptor findGame(const char *gameId) const = 0;
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 2ba5bd3..6276f1a 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -959,7 +959,7 @@ public:
 	virtual const char *getOriginalCopyright() const;
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
-	virtual GameList getSupportedGames() const;
+	PlainGameList getSupportedGames() const override;
 	PlainGameDescriptor findGame(const char *gameid) const override;
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
 
@@ -992,8 +992,8 @@ bool ScummEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsSubtitleOptions);
 }
 
-GameList ScummMetaEngine::getSupportedGames() const {
-	return GameList(gameDescriptions);
+PlainGameList ScummMetaEngine::getSupportedGames() const {
+	return PlainGameList(gameDescriptions);
 }
 
 PlainGameDescriptor ScummMetaEngine::findGame(const char *gameid) const {
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index 629996b..ffed998 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -76,7 +76,7 @@ public:
 	virtual const char *getOriginalCopyright() const;
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
-	virtual GameList getSupportedGames() const;
+	PlainGameList getSupportedGames() const override;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	PlainGameDescriptor findGame(const char *gameid) const override;
 	DetectedGames detectGames(const Common::FSList &fslist) const override;
@@ -110,9 +110,9 @@ bool Sky::SkyEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsSavingDuringRuntime);
 }
 
-GameList SkyMetaEngine::getSupportedGames() const {
-	GameList games;
-	games.push_back(GameDescriptor(skySetting));
+PlainGameList SkyMetaEngine::getSupportedGames() const {
+	PlainGameList games;
+	games.push_back(skySetting);
 	return games;
 }
 
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 62e8f1c..80176ae 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -87,7 +87,7 @@ public:
 	}
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
-	virtual GameList getSupportedGames() const;
+	PlainGameList getSupportedGames() const override;
 	PlainGameDescriptor findGame(const char *gameId) const override;
 	DetectedGames detectGames(const Common::FSList &fslist) const override;
 	virtual SaveStateList listSaves(const char *target) const;
@@ -116,14 +116,14 @@ bool Sword1::SwordEngine::hasFeature(EngineFeature f) const {
 	    (f == kSupportsLoadingDuringRuntime);
 }
 
-GameList SwordMetaEngine::getSupportedGames() const {
-	GameList games;
-	games.push_back(GameDescriptor(sword1FullSettings, GUIO_NOMIDI));
-	games.push_back(GameDescriptor(sword1DemoSettings, GUIO_NOMIDI));
-	games.push_back(GameDescriptor(sword1MacFullSettings, GUIO_NOMIDI));
-	games.push_back(GameDescriptor(sword1MacDemoSettings, GUIO_NOMIDI));
-	games.push_back(GameDescriptor(sword1PSXSettings, GUIO_NOMIDI));
-	games.push_back(GameDescriptor(sword1PSXDemoSettings, GUIO_NOMIDI));
+PlainGameList SwordMetaEngine::getSupportedGames() const {
+	PlainGameList games;
+	games.push_back(sword1FullSettings);
+	games.push_back(sword1DemoSettings);
+	games.push_back(sword1MacFullSettings);
+	games.push_back(sword1MacDemoSettings);
+	games.push_back(sword1PSXSettings);
+	games.push_back(sword1PSXDemoSettings);
 	return games;
 }
 
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index bf2f329..f9e308c 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -92,7 +92,7 @@ public:
 	}
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
-	virtual GameList getSupportedGames() const;
+	PlainGameList getSupportedGames() const override;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	PlainGameDescriptor findGame(const char *gameid) const override;
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const;
@@ -119,11 +119,11 @@ bool Sword2::Sword2Engine::hasFeature(EngineFeature f) const {
 		(f == kSupportsLoadingDuringRuntime);
 }
 
-GameList Sword2MetaEngine::getSupportedGames() const {
+PlainGameList Sword2MetaEngine::getSupportedGames() const {
 	const Sword2::GameSettings *g = Sword2::sword2_settings;
-	GameList games;
+	PlainGameList games;
 	while (g->gameid) {
-		games.push_back(GameDescriptor(g->gameid, g->description));
+		games.push_back(PlainGameDescriptor(g->gameid, g->description));
 		g++;
 	}
 	return games;


Commit: 5aff87dc153f392cb14423efa78a96397789a6fd
    https://github.com/scummvm/scummvm/commit/5aff87dc153f392cb14423efa78a96397789a6fd
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Turn GameDescriptor into a simple struct

Changed paths:
    base/commandLine.cpp
    base/plugins.cpp
    engines/advancedDetector.cpp
    engines/game.cpp
    engines/game.h
    engines/scumm/detection.cpp
    engines/sword2/sword2.cpp
    gui/launcher.cpp
    gui/massadd.cpp
    gui/massadd.h


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 1c7916c..e1539d7 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -881,8 +881,17 @@ static GameList getGameList(const Common::FSNode &dir) {
 	return candidates;
 }
 
+namespace {
+
+static void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
+	if (!value.empty())
+		ConfMan.set(key, value, domain);
+}
+
+} // End of anonymous namespace
+
 static bool addGameToConf(const GameDescriptor &gd) {
-	const Common::String &domain = gd.preferredtarget();
+	const Common::String &domain = gd.preferredTarget;
 
 	// If game has already been added, don't add
 	if (ConfMan.hasGameDomain(domain))
@@ -891,18 +900,22 @@ static bool addGameToConf(const GameDescriptor &gd) {
 	// Add the name domain
 	ConfMan.addGameDomain(domain);
 
-	// Copy all non-empty key/value pairs into the new domain
-	for (GameDescriptor::const_iterator iter = gd.begin(); iter != gd.end(); ++iter) {
-		if (!iter->_value.empty() && iter->_key != "preferredtarget")
-			ConfMan.set(iter->_key, iter->_value, domain);
-	}
+	// Copy all non-empty relevant values into the new domain
+	// FIXME: Factor out
+	addStringToConf("gameid", gd.gameId, domain);
+	addStringToConf("description", gd.description, domain);
+	addStringToConf("language", Common::getLanguageCode(gd.language), domain);
+	addStringToConf("platform", Common::getPlatformCode(gd.platform), domain);
+	addStringToConf("path", gd.path, domain);
+	addStringToConf("extra", gd.extra, domain);
+	addStringToConf("guioptions", gd.getGUIOptions(), domain);
 
 	// Display added game info
 	printf("Game Added: \n  GameID:   %s\n  Name:     %s\n  Language: %s\n  Platform: %s\n",
-			gd.gameid().c_str(),
-			gd.description().c_str(),
-			Common::getLanguageDescription(gd.language()),
-			Common::getPlatformDescription(gd.platform()));
+			gd.gameId.c_str(),
+			gd.description.c_str(),
+			Common::getLanguageDescription(gd.language),
+			Common::getPlatformDescription(gd.platform));
 
 	return true;
 }
@@ -916,7 +929,7 @@ static GameList recListGames(const Common::FSNode &dir, const Common::String &ga
 		for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) {
 			GameList rec = recListGames(*file, gameId, recursive);
 			for (GameList::const_iterator game = rec.begin(); game != rec.end(); ++game) {
-				if (gameId.empty() || game->gameid().c_str() == gameId)
+				if (gameId.empty() || game->gameId == gameId)
 					list.push_back(*game);
 			}
 		}
@@ -946,23 +959,23 @@ static Common::String detectGames(const Common::String &path, const Common::Stri
 	printf("ID             Description                                                Full Path\n");
 	printf("-------------- ---------------------------------------------------------- ---------------------------------------------------------\n");
 	for (GameList::iterator v = candidates.begin(); v != candidates.end(); ++v) {
-		printf("%-14s %-58s %s\n", v->gameid().c_str(), v->description().c_str(), (*v)["path"].c_str());
+		printf("%-14s %-58s %s\n", v->gameId.c_str(), v->description.c_str(), v->path.c_str());
 	}
 
-	return candidates[0].gameid();
+	return candidates[0].gameId;
 }
 
 static int recAddGames(const Common::FSNode &dir, const Common::String &game, bool recursive) {
 	int count = 0;
 	GameList list = getGameList(dir);
 	for (GameList::iterator v = list.begin(); v != list.end(); ++v) {
-		if (v->gameid().c_str() != game && !game.empty()) {
-			printf("Found %s, only adding %s per --game option, ignoring...\n", v->gameid().c_str(), game.c_str());
+		if (v->gameId != game && !game.empty()) {
+			printf("Found %s, only adding %s per --game option, ignoring...\n", v->gameId.c_str(), game.c_str());
 		} else if (!addGameToConf(*v)) {
 			// TODO Is it reall the case that !addGameToConf iff already added?
-			printf("Found %s, but has already been added, skipping\n", v->gameid().c_str());
+			printf("Found %s, but has already been added, skipping\n", v->gameId.c_str());
 		} else {
-			printf("Found %s, adding...\n", v->gameid().c_str());
+			printf("Found %s, adding...\n", v->gameId.c_str());
 			count++;
 		}
 	}
@@ -1033,7 +1046,7 @@ static void runDetectorTest() {
 		bool gameidDiffers = false;
 		GameList::iterator x;
 		for (x = candidates.begin(); x != candidates.end(); ++x) {
-			gameidDiffers |= (scumm_stricmp(gameid.c_str(), x->gameid().c_str()) != 0);
+			gameidDiffers |= (scumm_stricmp(gameid.c_str(), x->gameId.c_str()) != 0);
 		}
 
 		if (candidates.empty()) {
@@ -1056,10 +1069,10 @@ static void runDetectorTest() {
 
 		for (x = candidates.begin(); x != candidates.end(); ++x) {
 			printf("    gameid '%s', desc '%s', language '%s', platform '%s'\n",
-				   x->gameid().c_str(),
-				   x->description().c_str(),
-				   Common::getLanguageCode(x->language()),
-				   Common::getPlatformCode(x->platform()));
+				   x->gameId.c_str(),
+				   x->description.c_str(),
+				   Common::getLanguageDescription(x->language),
+				   Common::getPlatformDescription(x->platform));
 		}
 	}
 	int total = domains.size();
@@ -1131,7 +1144,7 @@ void upgradeTargets() {
 			GameList::iterator x;
 			int matchesFound = 0;
 			for (x = candidates.begin(); x != candidates.end(); ++x) {
-				if (x->gameid() == gameid && x->language() == lang && x->platform() == plat) {
+				if (x->gameId == gameid && x->language == lang && x->platform == plat) {
 					matchesFound++;
 					g = &(*x);
 				}
@@ -1149,27 +1162,27 @@ void upgradeTargets() {
 		// the target referred to by dom. We update several things
 
 		// Always set the gameid explicitly (in case of legacy targets)
-		dom["gameid"] = g->gameid();
+		dom["gameid"] = g->gameId;
 
 		// Always set the GUI options. The user should not modify them, and engines might
 		// gain more features over time, so we want to keep this list up-to-date.
-		if (g->contains("guioptions")) {
-			printf("  -> update guioptions to '%s'\n", (*g)["guioptions"].c_str());
-			dom["guioptions"] = (*g)["guioptions"];
+		if (!g->getGUIOptions().empty()) {
+			printf("  -> update guioptions to '%s'\n", g->getGUIOptions().c_str());
+			dom["guioptions"] = g->getGUIOptions();
 		} else if (dom.contains("guioptions")) {
 			dom.erase("guioptions");
 		}
 
 		// Update the language setting but only if none has been set yet.
-		if (lang == Common::UNK_LANG && g->language() != Common::UNK_LANG) {
-			printf("  -> set language to '%s'\n", Common::getLanguageCode(g->language()));
-			dom["language"] = (*g)["language"];
+		if (lang == Common::UNK_LANG && g->language != Common::UNK_LANG) {
+			printf("  -> set language to '%s'\n", Common::getLanguageCode(g->language));
+			dom["language"] = Common::getLanguageCode(g->language);
 		}
 
 		// Update the platform setting but only if none has been set yet.
-		if (plat == Common::kPlatformUnknown && g->platform() != Common::kPlatformUnknown) {
-			printf("  -> set platform to '%s'\n", Common::getPlatformCode(g->platform()));
-			dom["platform"] = (*g)["platform"];
+		if (plat == Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown) {
+			printf("  -> set platform to '%s'\n", Common::getPlatformCode(g->platform));
+			dom["platform"] = Common::getPlatformCode(g->platform);
 		}
 
 		// TODO: We could also update the description. But not everybody will want that.
@@ -1178,8 +1191,8 @@ void upgradeTargets() {
 		// should only be updated if the user explicitly requests this.
 #if 0
 		if (desc != g->description()) {
-			printf("  -> update desc from '%s' to\n                      '%s' ?\n", desc.c_str(), g->description().c_str());
-			dom["description"] = (*g)["description"];
+			printf("  -> update desc from '%s' to\n                      '%s' ?\n", desc.c_str(), g->description.c_str());
+			dom["description"] = g->description;
 		}
 #endif
 	}
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 25dd3e1..61dae91 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -531,7 +531,7 @@ DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const
 
 			for (uint i = 0; i < engineCandidates.size(); i++) {
 				engineCandidates[i].engineName = metaEngine.getName();
-				engineCandidates[i].matchedGame["path"] = path;
+				engineCandidates[i].matchedGame.path = path;
 				candidates.push_back(engineCandidates[i]);
 			}
 
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 562ad5d..ac606d3 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -114,22 +114,22 @@ static Common::String sanitizeName(const char *name) {
 
 void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const {
 	if (_singleId != NULL) {
-		desc["preferredtarget"] = desc["gameid"];
-		desc["gameid"] = _singleId;
+		desc.preferredTarget = desc.gameId;
+		desc.gameId = _singleId;
 	}
 
-	if (!desc.contains("preferredtarget"))
-		desc["preferredtarget"] = desc["gameid"];
+	if (desc.preferredTarget.empty())
+		desc.preferredTarget = desc.gameId;
 
 	if (realDesc->flags & ADGF_AUTOGENTARGET) {
 		if (*realDesc->extra)
-			desc["preferredtarget"] = sanitizeName(realDesc->extra);
+			desc.preferredTarget = sanitizeName(realDesc->extra);
 	}
 
-	desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc);
+	desc.preferredTarget = generatePreferredTarget(desc.preferredTarget, realDesc);
 
 	if (_flags & kADFlagUseExtraAsHint)
-		desc["extra"] = realDesc->extra;
+		desc.extra = realDesc->extra;
 
 	desc.setGUIOptions(realDesc->guiOptions + _guiOptions);
 	desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language));
@@ -329,13 +329,13 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
 	showTestingWarning = true;
 #endif
 
-	if (((gameDescriptor.getSupportLevel() == kUnstableGame
-			|| (gameDescriptor.getSupportLevel() == kTestingGame
+	if (((gameDescriptor.gameSupportLevel == kUnstableGame
+			|| (gameDescriptor.gameSupportLevel == kTestingGame
 					&& showTestingWarning)))
 			&& !Engine::warnUserAboutUnsupportedGame())
 		return Common::kUserCanceled;
 
-	debug(2, "Running %s", gameDescriptor.description().c_str());
+	debug(2, "Running %s", gameDescriptor.description.c_str());
 	initSubSystems(agdDesc.desc);
 	if (!createInstance(syst, engine, agdDesc.desc))
 		return Common::kNoGameDataFoundError;
diff --git a/engines/game.cpp b/engines/game.cpp
index 177880c..1e96020 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -35,94 +35,75 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const Pla
 	return 0;
 }
 
-GameDescriptor::GameDescriptor() {
-	setVal("gameid", "");
-	setVal("description", "");
+GameDescriptor::GameDescriptor() :
+		language(Common::UNK_LANG),
+		platform(Common::kPlatformUnknown),
+		gameSupportLevel(kStableGame) {
 }
 
-GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions) {
-	setVal("gameid", pgd.gameId);
-	setVal("description", pgd.description);
+GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, const Common::String &guioptions) :
+		language(Common::UNK_LANG),
+		platform(Common::kPlatformUnknown),
+		gameSupportLevel(kStableGame) {
+
+	gameId = pgd.gameId;
+	preferredTarget = pgd.gameId;
+	description = pgd.description;
 
 	if (!guioptions.empty())
-		setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions));
+		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
-GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l, Common::Platform p, Common::String guioptions, GameSupportLevel gsl) {
-	setVal("gameid", g);
-	setVal("description", d);
-	if (l != Common::UNK_LANG)
-		setVal("language", Common::getLanguageCode(l));
-	if (p != Common::kPlatformUnknown)
-		setVal("platform", Common::getPlatformCode(p));
-	if (!guioptions.empty())
-		setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions));
+GameDescriptor::GameDescriptor(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &guioptions, GameSupportLevel gsl) {
+	gameId = id;
+	preferredTarget = id;
+	description = d;
+	language = l;
+	platform = p;
+	gameSupportLevel = gsl;
 
-	setSupportLevel(gsl);
+	if (!guioptions.empty())
+		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
-void GameDescriptor::setGUIOptions(Common::String guioptions) {
-	if (!guioptions.empty())
-		setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions));
+void GameDescriptor::setGUIOptions(const Common::String &guioptions) {
+	if (guioptions.empty())
+		_guiOptions.clear();
 	else
-		erase("guioptions");
+		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
 void GameDescriptor::appendGUIOptions(const Common::String &str) {
-	setVal("guioptions", getVal("guioptions", "") + " " + str);
+	if (!_guiOptions.empty())
+		_guiOptions += " ";
+
+	_guiOptions += str;
 }
 
-void GameDescriptor::updateDesc(const char *extra) {
-	const bool hasCustomLanguage = (language() != Common::UNK_LANG);
-	const bool hasCustomPlatform = (platform() != Common::kPlatformUnknown);
-	const bool hasExtraDesc = (extra && extra[0]);
+void GameDescriptor::updateDesc(const char *extraDesc) {
+	const bool hasCustomLanguage = (language != Common::UNK_LANG);
+	const bool hasCustomPlatform = (platform != Common::kPlatformUnknown);
+	const bool hasExtraDesc = (extraDesc && extraDesc[0]);
 
 	// Adapt the description string if custom platform/language is set.
 	if (hasCustomLanguage || hasCustomPlatform || hasExtraDesc) {
-		Common::String descr = description();
+		Common::String descr = description;
 
 		descr += " (";
 		if (hasExtraDesc)
-			descr += extra;
+			descr += extraDesc;
 		if (hasCustomPlatform) {
 			if (hasExtraDesc)
 				descr += "/";
-			descr += Common::getPlatformDescription(platform());
+			descr += Common::getPlatformDescription(platform);
 		}
 		if (hasCustomLanguage) {
 			if (hasExtraDesc || hasCustomPlatform)
 				descr += "/";
-			descr += Common::getLanguageDescription(language());
+			descr += Common::getLanguageDescription(language);
 		}
 		descr += ")";
-		setVal("description", descr);
-	}
-}
-
-GameSupportLevel GameDescriptor::getSupportLevel() {
-	GameSupportLevel gsl = kStableGame;
-	if (contains("gsl")) {
-		Common::String gslString = getVal("gsl");
-		if (gslString.equals("unstable"))
-			gsl = kUnstableGame;
-		else if (gslString.equals("testing"))
-			gsl = kTestingGame;
-	}
-	return gsl;
-}
-
-void GameDescriptor::setSupportLevel(GameSupportLevel gsl) {
-	switch (gsl) {
-	case kUnstableGame:
-		setVal("gsl", "unstable");
-		break;
-	case kTestingGame:
-		setVal("gsl", "testing");
-		break;
-	case kStableGame:
-		// Fall Through intended
-	default:
-		erase("gsl");
+		description = descr;
 	}
 }
 
@@ -159,7 +140,7 @@ Common::String DetectionResults::generateUnknownGameReport(bool translate, uint3
 	const char *reportEngineHeader = _s("Matched game IDs for the %s engine:");
 
 	Common::String report = Common::String::format(
-			translate ? _(reportStart) : reportStart, _detectedGames[0].matchedGame["path"].c_str(),
+			translate ? _(reportStart) : reportStart, _detectedGames[0].matchedGame.path.c_str(),
 			"https://bugs.scummvm.org/"
 	);
 	report += "\n";
@@ -190,7 +171,7 @@ Common::String DetectionResults::generateUnknownGameReport(bool translate, uint3
 		// Add the gameId to the list of matched games for the engine
 		// TODO: Use the gameId here instead of the preferred target.
 		// This is currently impossible due to the AD singleId feature losing the information.
-		report += game.matchedGame["preferredtarget"];
+		report += game.matchedGame.preferredTarget;
 
 		// Consolidate matched files across all engines and detection entries
 		for (FilePropertiesMap::const_iterator it = game.matchedFiles.begin(); it != game.matchedFiles.end(); it++) {
diff --git a/engines/game.h b/engines/game.h
index a5cc4de..053c0c8 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -78,15 +78,15 @@ enum GameSupportLevel {
  * can be contained in a GameDescriptor.
  * This is an essential part of the glue between the game engines and the launcher code.
  */
-class GameDescriptor : public Common::StringMap {
+class GameDescriptor {
 public:
 	GameDescriptor();
-	explicit GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions = Common::String());
-	GameDescriptor(const Common::String &gameid,
+	explicit GameDescriptor(const PlainGameDescriptor &pgd, const Common::String &guioptions = Common::String());
+	GameDescriptor(const Common::String &id,
 	              const Common::String &description,
 	              Common::Language language = Common::UNK_LANG,
 				  Common::Platform platform = Common::kPlatformUnknown,
-				  Common::String guioptions = Common::String(),
+				  const Common::String &guioptions = Common::String(),
 				  GameSupportLevel gsl = kStableGame);
 
 	/**
@@ -94,27 +94,27 @@ public:
 	 * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be
 	 * added if no platform has been specified but a language and an extra string.
 	 */
-	void updateDesc(const char *extra = 0);
+	void updateDesc(const char *extraDesc = 0);
 
-	void setGUIOptions(Common::String options);
+	void setGUIOptions(const Common::String &options);
 	void appendGUIOptions(const Common::String &str);
+	Common::String getGUIOptions() const { return _guiOptions; }
+
+	Common::String gameId;
+	Common::String preferredTarget;
+	Common::String description;
+	Common::Language language;
+	Common::Platform platform;
+	Common::String path;
+	Common::String extra;
 
 	/**
 	 * What level of support is expected of this game
 	 */
-	GameSupportLevel getSupportLevel();
-	void setSupportLevel(GameSupportLevel gsl);
-
-	Common::String &gameid() { return getVal("gameid"); }
-	Common::String &description() { return getVal("description"); }
-	const Common::String &gameid() const { return getVal("gameid"); }
-	const Common::String &description() const { return getVal("description"); }
-	Common::Language language() const { return contains("language") ? Common::parseLanguage(getVal("language")) : Common::UNK_LANG; }
-	Common::Platform platform() const { return contains("platform") ? Common::parsePlatform(getVal("platform")) : Common::kPlatformUnknown; }
-
-	const Common::String &preferredtarget() const {
-		return contains("preferredtarget") ? getVal("preferredtarget") : getVal("gameid");
-	}
+	GameSupportLevel gameSupportLevel;
+
+private:
+	Common::String _guiOptions;
 };
 
 /** List of games. */
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 6276f1a..dea0d3a 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1044,7 +1044,7 @@ DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
 
 		// Compute and set the preferred target name for this game.
 		// Based on generateComplexID() in advancedDetector.cpp.
-		game.matchedGame["preferredtarget"] = generatePreferredTarget(*x);
+		game.matchedGame.preferredTarget = generatePreferredTarget(*x);
 
 		game.matchedGame.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
 		game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index f9e308c..67f6904 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -284,7 +284,7 @@ Common::Error Sword2MetaEngine::createInstance(OSystem *syst, Engine **engine) c
 	DetectedGames detectedGames = detectGames(fslist);
 
 	for (uint i = 0; i < detectedGames.size(); i++) {
-		if (detectedGames[i].matchedGame.gameid() == gameid) {
+		if (detectedGames[i].matchedGame.gameId == gameid) {
 			*engine = new Sword2::Sword2Engine(syst);
 			return Common::kNoError;
 		}
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index f257c53..4edd352 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -376,11 +376,20 @@ void LauncherDialog::addGame() {
 	} while (looping);
 }
 
+namespace {
+
+static void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
+	if (!value.empty())
+		ConfMan.set(key, value, domain);
+}
+
+} // End of anonymous namespace
+
 Common::String addGameToConf(const GameDescriptor &result) {
 	// The auto detector or the user made a choice.
 	// Pick a domain name which does not yet exist (after all, we
 	// are *adding* a game to the config, not replacing).
-	Common::String domain = result.preferredtarget();
+	Common::String domain = result.preferredTarget;
 
 	assert(!domain.empty());
 	if (ConfMan.hasGameDomain(domain)) {
@@ -396,11 +405,15 @@ Common::String addGameToConf(const GameDescriptor &result) {
 	// Add the name domain
 	ConfMan.addGameDomain(domain);
 
-	// Copy all non-empty key/value pairs into the new domain
-	for (GameDescriptor::const_iterator iter = result.begin(); iter != result.end(); ++iter) {
-		if (!iter->_value.empty() && iter->_key != "preferredtarget")
-			ConfMan.set(iter->_key, iter->_value, domain);
-	}
+	// Copy all non-empty relevant values into the new domain
+	// FIXME: Factor out
+	addStringToConf("gameid", result.gameId, domain);
+	addStringToConf("description", result.description, domain);
+	addStringToConf("language", Common::getLanguageCode(result.language), domain);
+	addStringToConf("platform", Common::getPlatformCode(result.platform), domain);
+	addStringToConf("path", result.path, domain);
+	addStringToConf("extra", result.extra, domain);
+	addStringToConf("guioptions", result.getGUIOptions(), domain);
 
 	// TODO: Setting the description field here has the drawback
 	// that the user does never notice when we upgrade our descriptions.
@@ -601,7 +614,7 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 		// Display the candidates to the user and let her/him pick one
 		StringArray list;
 		for (idx = 0; idx < (int)candidates.size(); idx++)
-			list.push_back(candidates[idx].matchedGame.description());
+			list.push_back(candidates[idx].matchedGame.description);
 
 		ChooserDialog dialog(_("Pick the game:"));
 		dialog.setList(list);
diff --git a/gui/massadd.cpp b/gui/massadd.cpp
index 2774d47..7c54c50 100644
--- a/gui/massadd.cpp
+++ b/gui/massadd.cpp
@@ -121,13 +121,13 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir)
 
 struct GameTargetLess {
 	bool operator()(const GameDescriptor &x, const GameDescriptor &y) const {
-		return x.preferredtarget().compareToIgnoreCase(y.preferredtarget()) < 0;
+		return x.preferredTarget.compareToIgnoreCase(y.preferredTarget) < 0;
 	}
 };
 
 struct GameDescLess {
 	bool operator()(const GameDescriptor &x, const GameDescriptor &y) const {
-		return x.description().compareToIgnoreCase(y.description()) < 0;
+		return x.description.compareToIgnoreCase(y.description) < 0;
 	}
 };
 
@@ -143,13 +143,13 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
 	if (cmd == kOkCmd) {
 		// Sort the detected games. This is not strictly necessary, but nice for
 		// people who want to edit their config file by hand after a mass add.
-		sort(_games.begin(), _games.end(), GameTargetLess());
+		Common::sort(_games.begin(), _games.end(), GameTargetLess());
 		// Add all the detected games to the config
 		for (GameList::iterator iter = _games.begin(); iter != _games.end(); ++iter) {
 			debug(1, "  Added gameid '%s', desc '%s'\n",
-				(*iter)["gameid"].c_str(),
-				(*iter)["description"].c_str());
-			(*iter)["gameid"] = addGameToConf(*iter);
+				iter->gameId.c_str(),
+				iter->description.c_str());
+			iter->gameId = addGameToConf(*iter);
 		}
 
 		// Write everything to disk
@@ -157,8 +157,8 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
 
 		// And scroll to first detected game
 		if (!_games.empty()) {
-			sort(_games.begin(), _games.end(), GameDescLess());
-			ConfMan.set("temp_selection", _games.front().gameid());
+			Common::sort(_games.begin(), _games.end(), GameDescLess());
+			ConfMan.set("temp_selection", _games.front().gameId);
 		}
 
 		close();
@@ -211,6 +211,9 @@ void MassAddDialog::handleTickle() {
 
 			// Check for existing config entries for this path/gameid/lang/platform combination
 			if (_pathToTargets.contains(path)) {
+				const char *resultPlatformCode = Common::getPlatformCode(result.platform);
+				const char *resultLanguageCode = Common::getLanguageCode(result.language);
+
 				bool duplicate = false;
 				const StringArray &targets = _pathToTargets[path];
 				for (StringArray::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) {
@@ -218,9 +221,9 @@ void MassAddDialog::handleTickle() {
 					Common::ConfigManager::Domain *dom = ConfMan.getDomain(*iter);
 					assert(dom);
 
-					if ((*dom)["gameid"] == result["gameid"] &&
-					    (*dom)["platform"] == result["platform"] &&
-					    (*dom)["language"] == result["language"]) {
+					if ((*dom)["gameid"] == result.gameId &&
+					    (*dom)["platform"] == resultPlatformCode &&
+					    (*dom)["language"] == resultLanguageCode) {
 						duplicate = true;
 						break;
 					}
@@ -232,7 +235,7 @@ void MassAddDialog::handleTickle() {
 			}
 			_games.push_back(result);
 
-			_list->append(result.description());
+			_list->append(result.description);
 		}
 
 
diff --git a/gui/massadd.h b/gui/massadd.h
index 116a420..58071cd 100644
--- a/gui/massadd.h
+++ b/gui/massadd.h
@@ -44,7 +44,7 @@ public:
 
 	Common::String getFirstAddedTarget() const {
 		if (!_games.empty())
-			return _games.front().gameid();
+			return _games.front().gameId;
 		return Common::String();
 	}
 


Commit: 1de5aca585af3e04a64a4f287dd348c0e7b4b967
    https://github.com/scummvm/scummvm/commit/1de5aca585af3e04a64a4f287dd348c0e7b4b967
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Set the GameDescriptor decription in the constructor

Changed paths:
    engines/advancedDetector.cpp
    engines/game.cpp
    engines/game.h
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp


diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index ac606d3..b7bf4b4 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -56,8 +56,8 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa
 	else if (g.flags & ADGF_TESTING)
 		gsl = kTestingGame;
 
-	GameDescriptor gd(g.gameId, title, g.language, g.platform, 0, gsl);
-	gd.updateDesc(extra);
+	GameDescriptor gd(g.gameId, title, g.language, g.platform, extra);
+	gd.gameSupportLevel = gsl;
 	return gd;
 }
 
diff --git a/engines/game.cpp b/engines/game.cpp
index 1e96020..bdf8e32 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -41,7 +41,7 @@ GameDescriptor::GameDescriptor() :
 		gameSupportLevel(kStableGame) {
 }
 
-GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, const Common::String &guioptions) :
+GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd) :
 		language(Common::UNK_LANG),
 		platform(Common::kPlatformUnknown),
 		gameSupportLevel(kStableGame) {
@@ -49,21 +49,18 @@ GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, const Common::Str
 	gameId = pgd.gameId;
 	preferredTarget = pgd.gameId;
 	description = pgd.description;
-
-	if (!guioptions.empty())
-		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
-GameDescriptor::GameDescriptor(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &guioptions, GameSupportLevel gsl) {
+GameDescriptor::GameDescriptor(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &ex) {
 	gameId = id;
 	preferredTarget = id;
 	description = d;
 	language = l;
 	platform = p;
-	gameSupportLevel = gsl;
+	extra = ex;
 
-	if (!guioptions.empty())
-		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
+	// Append additional information, if set, to the description.
+	description += updateDesc();
 }
 
 void GameDescriptor::setGUIOptions(const Common::String &guioptions) {
@@ -80,31 +77,34 @@ void GameDescriptor::appendGUIOptions(const Common::String &str) {
 	_guiOptions += str;
 }
 
-void GameDescriptor::updateDesc(const char *extraDesc) {
+Common::String GameDescriptor::updateDesc() const {
 	const bool hasCustomLanguage = (language != Common::UNK_LANG);
 	const bool hasCustomPlatform = (platform != Common::kPlatformUnknown);
-	const bool hasExtraDesc = (extraDesc && extraDesc[0]);
+	const bool hasExtraDesc = !extra.empty();
 
 	// Adapt the description string if custom platform/language is set.
-	if (hasCustomLanguage || hasCustomPlatform || hasExtraDesc) {
-		Common::String descr = description;
+	Common::String descr;
+	if (!hasCustomLanguage && !hasCustomPlatform && !hasExtraDesc)
+		return descr;
 
-		descr += " (";
+	descr += " (";
+
+	if (hasExtraDesc)
+		descr += extra;
+	if (hasCustomPlatform) {
 		if (hasExtraDesc)
-			descr += extraDesc;
-		if (hasCustomPlatform) {
-			if (hasExtraDesc)
-				descr += "/";
-			descr += Common::getPlatformDescription(platform);
-		}
-		if (hasCustomLanguage) {
-			if (hasExtraDesc || hasCustomPlatform)
-				descr += "/";
-			descr += Common::getLanguageDescription(language);
-		}
-		descr += ")";
-		description = descr;
+			descr += "/";
+		descr += Common::getPlatformDescription(platform);
+	}
+	if (hasCustomLanguage) {
+		if (hasExtraDesc || hasCustomPlatform)
+			descr += "/";
+		descr += Common::getLanguageDescription(language);
 	}
+
+	descr += ")";
+
+	return descr;
 }
 
 DetectionResults::DetectionResults(const DetectedGames &detectedGames) :
diff --git a/engines/game.h b/engines/game.h
index 053c0c8..d675e7b 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -81,20 +81,12 @@ enum GameSupportLevel {
 class GameDescriptor {
 public:
 	GameDescriptor();
-	explicit GameDescriptor(const PlainGameDescriptor &pgd, const Common::String &guioptions = Common::String());
+	explicit GameDescriptor(const PlainGameDescriptor &pgd);
 	GameDescriptor(const Common::String &id,
 	              const Common::String &description,
 	              Common::Language language = Common::UNK_LANG,
-				  Common::Platform platform = Common::kPlatformUnknown,
-				  const Common::String &guioptions = Common::String(),
-				  GameSupportLevel gsl = kStableGame);
-
-	/**
-	 * Update the description string by appending (EXTRA/PLATFORM/LANG) to it.
-	 * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be
-	 * added if no platform has been specified but a language and an extra string.
-	 */
-	void updateDesc(const char *extraDesc = 0);
+	              Common::Platform platform = Common::kPlatformUnknown,
+	              const Common::String &extra = Common::String());
 
 	void setGUIOptions(const Common::String &options);
 	void appendGUIOptions(const Common::String &str);
@@ -114,6 +106,13 @@ public:
 	GameSupportLevel gameSupportLevel;
 
 private:
+	/**
+	 * Update the description string by appending (EXTRA/PLATFORM/LANG) to it.
+	 * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be
+	 * added if no platform has been specified but a language and an extra string.
+	 */
+	Common::String updateDesc() const;
+
 	Common::String _guiOptions;
 };
 
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index dea0d3a..1395fc4 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1037,10 +1037,7 @@ DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
 		assert(g);
 
 		DetectedGame game;
-		game.matchedGame = GameDescriptor(x->game.gameid, g->description, x->language, x->game.platform);
-
-		// Append additional information, if set, to the description.
-		game.matchedGame.updateDesc(x->extra);
+		game.matchedGame = GameDescriptor(x->game.gameid, g->description, x->language, x->game.platform, x->extra);
 
 		// Compute and set the preferred target name for this game.
 		// Based on generateComplexID() in advancedDetector.cpp.
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index ffed998..b8abd6b 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -173,18 +173,26 @@ DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
 		// Match found, add to list of candidates, then abort inner loop.
 		// The game detector uses US English by default. We want British
 		// English to match the recorded voices better.
-		DetectedGame game;
-		game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
 		const SkyVersion *sv = skyVersions;
 		while (sv->dinnerTableEntries) {
 			if (dinnerTableEntries == sv->dinnerTableEntries &&
 				(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
-				game.matchedGame.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
-				game.matchedGame.setGUIOptions(sv->guioptions);
 				break;
 			}
 			++sv;
 		}
+
+		DetectedGame game;
+		if (sv->dinnerTableEntries) {
+			Common::String extra = Common::String::format("v0.0%d %s", sv->version, sv->extraDesc);
+
+			game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown, extra);
+			game.matchedGame.setGUIOptions(sv->guioptions);
+
+		} else {
+			game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description);
+		}
+
 		detectedGames.push_back(game);
 	}
 
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 80176ae..205640a 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -214,20 +214,22 @@ DetectedGames SwordMetaEngine::detectGames(const Common::FSList &fslist) const {
 
 	DetectedGame game;
 	if (mainFilesFound && pcFilesFound && demoFilesFound)
-		game.matchedGame = GameDescriptor(sword1DemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1DemoSettings);
 	else if (mainFilesFound && pcFilesFound && psxFilesFound)
-		game.matchedGame = GameDescriptor(sword1PSXSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1PSXSettings);
 	else if (mainFilesFound && pcFilesFound && psxDemoFilesFound)
-		game.matchedGame = GameDescriptor(sword1PSXDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1PSXDemoSettings);
 	else if (mainFilesFound && pcFilesFound && !psxFilesFound)
-		game.matchedGame = GameDescriptor(sword1FullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1FullSettings);
 	else if (mainFilesFound && macFilesFound)
-		game.matchedGame = GameDescriptor(sword1MacFullSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1MacFullSettings);
 	else if (mainFilesFound && macDemoFilesFound)
-		game.matchedGame = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+		game.matchedGame = GameDescriptor(sword1MacDemoSettings);
 	else
 		return detectedGames;
 
+	game.matchedGame.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+
 	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
 	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
 	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 67f6904..2310746 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -193,7 +193,8 @@ DetectedGames detectGamesImpl(const Common::FSList &fslist, bool recursion = fal
 
 					// Match found, add to list of candidates, then abort inner loop.
 					DetectedGame game;
-					game.matchedGame = GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+					game.matchedGame = GameDescriptor(g->gameid, g->description);
+					game.matchedGame.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 
 					detectedGames.push_back(game);
 					break;


Commit: faa2534f46611a47913004b55aa0e5ed5b7e4b7a
    https://github.com/scummvm/scummvm/commit/faa2534f46611a47913004b55aa0e5ed5b7e4b7a
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Factor adding games to ConfMan

Changed paths:
    base/commandLine.cpp
    base/plugins.cpp
    engines/metaengine.h
    gui/launcher.cpp
    gui/launcher.h
    gui/massadd.cpp
    gui/massadd.h


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index e1539d7..02c3d1c 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -881,45 +881,6 @@ static GameList getGameList(const Common::FSNode &dir) {
 	return candidates;
 }
 
-namespace {
-
-static void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
-	if (!value.empty())
-		ConfMan.set(key, value, domain);
-}
-
-} // End of anonymous namespace
-
-static bool addGameToConf(const GameDescriptor &gd) {
-	const Common::String &domain = gd.preferredTarget;
-
-	// If game has already been added, don't add
-	if (ConfMan.hasGameDomain(domain))
-		return false;
-
-	// Add the name domain
-	ConfMan.addGameDomain(domain);
-
-	// Copy all non-empty relevant values into the new domain
-	// FIXME: Factor out
-	addStringToConf("gameid", gd.gameId, domain);
-	addStringToConf("description", gd.description, domain);
-	addStringToConf("language", Common::getLanguageCode(gd.language), domain);
-	addStringToConf("platform", Common::getPlatformCode(gd.platform), domain);
-	addStringToConf("path", gd.path, domain);
-	addStringToConf("extra", gd.extra, domain);
-	addStringToConf("guioptions", gd.getGUIOptions(), domain);
-
-	// Display added game info
-	printf("Game Added: \n  GameID:   %s\n  Name:     %s\n  Language: %s\n  Platform: %s\n",
-			gd.gameId.c_str(),
-			gd.description.c_str(),
-			Common::getLanguageDescription(gd.language),
-			Common::getPlatformDescription(gd.platform));
-
-	return true;
-}
-
 static GameList recListGames(const Common::FSNode &dir, const Common::String &gameId, bool recursive) {
 	GameList list = getGameList(dir);
 
@@ -971,11 +932,12 @@ static int recAddGames(const Common::FSNode &dir, const Common::String &game, bo
 	for (GameList::iterator v = list.begin(); v != list.end(); ++v) {
 		if (v->gameId != game && !game.empty()) {
 			printf("Found %s, only adding %s per --game option, ignoring...\n", v->gameId.c_str(), game.c_str());
-		} else if (!addGameToConf(*v)) {
-			// TODO Is it reall the case that !addGameToConf iff already added?
+		} else if (ConfMan.hasGameDomain(v->preferredTarget)) {
+			// TODO Better check for game already added?
 			printf("Found %s, but has already been added, skipping\n", v->gameId.c_str());
 		} else {
 			printf("Found %s, adding...\n", v->gameId.c_str());
+			EngineMan.createTargetForGame(*v);
 			count++;
 		}
 	}
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 61dae91..1bfb929 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -545,6 +545,57 @@ const PluginList &EngineManager::getPlugins() const {
 	return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
 }
 
+namespace {
+
+void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
+	if (!value.empty())
+		ConfMan.set(key, value, domain);
+}
+
+} // End of anonymous namespace
+
+Common::String EngineManager::createTargetForGame(const GameDescriptor &game) {
+	// The auto detector or the user made a choice.
+	// Pick a domain name which does not yet exist (after all, we
+	// are *adding* a game to the config, not replacing).
+	Common::String domain = game.preferredTarget;
+
+	assert(!domain.empty());
+	if (ConfMan.hasGameDomain(domain)) {
+		int suffixN = 1;
+		Common::String gameid(domain);
+
+		while (ConfMan.hasGameDomain(domain)) {
+			domain = gameid + Common::String::format("-%d", suffixN);
+			suffixN++;
+		}
+	}
+
+	// Add the name domain
+	ConfMan.addGameDomain(domain);
+
+	// Copy all non-empty relevant values into the new domain
+	addStringToConf("gameid", game.gameId, domain);
+	addStringToConf("description", game.description, domain);
+	addStringToConf("language", Common::getLanguageCode(game.language), domain);
+	addStringToConf("platform", Common::getPlatformCode(game.platform), domain);
+	addStringToConf("path", game.path, domain);
+	addStringToConf("extra", game.extra, domain);
+	addStringToConf("guioptions", game.getGUIOptions(), domain);
+
+	// TODO: Setting the description field here has the drawback
+	// that the user does never notice when we upgrade our descriptions.
+	// It might be nice to leave this field empty, and only set it to
+	// a value when the user edits the description string.
+	// However, at this point, that's impractical. Once we have a method
+	// to query all backends for the proper & full description of a given
+	// game target, we can change this (currently, you can only query
+	// for the generic gameid description; it's not possible to obtain
+	// a description which contains extended information like language, etc.).
+
+	return domain;
+}
+
 // Music plugins
 
 #include "audio/musicplugin.h"
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 74eb367..6e8ab16 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -271,6 +271,13 @@ public:
 	PlainGameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const;
 	DetectionResults detectGames(const Common::FSList &fslist) const;
 	const PluginList &getPlugins() const;
+
+	/**
+	 * Create a target from the supplied game descriptor
+	 *
+	 * Returns the created target name.
+	 */
+	Common::String createTargetForGame(const GameDescriptor &game);
 };
 
 /** Convenience shortcut for accessing the engine manager. */
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 4edd352..67a62ad 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -376,58 +376,6 @@ void LauncherDialog::addGame() {
 	} while (looping);
 }
 
-namespace {
-
-static void addStringToConf(const Common::String &key, const Common::String &value, const Common::String &domain) {
-	if (!value.empty())
-		ConfMan.set(key, value, domain);
-}
-
-} // End of anonymous namespace
-
-Common::String addGameToConf(const GameDescriptor &result) {
-	// The auto detector or the user made a choice.
-	// Pick a domain name which does not yet exist (after all, we
-	// are *adding* a game to the config, not replacing).
-	Common::String domain = result.preferredTarget;
-
-	assert(!domain.empty());
-	if (ConfMan.hasGameDomain(domain)) {
-		int suffixN = 1;
-		Common::String gameid(domain);
-
-		while (ConfMan.hasGameDomain(domain)) {
-			domain = gameid + Common::String::format("-%d", suffixN);
-			suffixN++;
-		}
-	}
-
-	// Add the name domain
-	ConfMan.addGameDomain(domain);
-
-	// Copy all non-empty relevant values into the new domain
-	// FIXME: Factor out
-	addStringToConf("gameid", result.gameId, domain);
-	addStringToConf("description", result.description, domain);
-	addStringToConf("language", Common::getLanguageCode(result.language), domain);
-	addStringToConf("platform", Common::getPlatformCode(result.platform), domain);
-	addStringToConf("path", result.path, domain);
-	addStringToConf("extra", result.extra, domain);
-	addStringToConf("guioptions", result.getGUIOptions(), domain);
-
-	// TODO: Setting the description field here has the drawback
-	// that the user does never notice when we upgrade our descriptions.
-	// It might be nice ot leave this field empty, and only set it to
-	// a value when the user edits the description string.
-	// However, at this point, that's impractical. Once we have a method
-	// to query all backends for the proper & full description of a given
-	// game target, we can change this (currently, you can only query
-	// for the generic gameid description; it's not possible to obtain
-	// a description which contains extended information like language, etc.).
-
-	return domain;
-}
-
 void LauncherDialog::removeGame(int item) {
 	MessageDialog alert(_("Do you really want to remove this game configuration?"), _("Yes"), _("No"));
 
@@ -623,7 +571,7 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 	if (0 <= idx && idx < (int)candidates.size()) {
 		const GameDescriptor &result = candidates[idx].matchedGame;
 
-		Common::String domain = addGameToConf(result);
+		Common::String domain = EngineMan.createTargetForGame(result);
 
 		// Display edit dialog for the new entry
 		EditGameDialog editDialog(domain);
diff --git a/gui/launcher.h b/gui/launcher.h
index 08413fe..9f0a1c8 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -38,8 +38,6 @@ class StaticTextWidget;
 class EditTextWidget;
 class SaveLoadChooser;
 
-Common::String addGameToConf(const GameDescriptor &result);
-
 class LauncherDialog : public Dialog {
 	typedef Common::String String;
 	typedef Common::Array<Common::String> StringArray;
diff --git a/gui/massadd.cpp b/gui/massadd.cpp
index 7c54c50..56b15ec 100644
--- a/gui/massadd.cpp
+++ b/gui/massadd.cpp
@@ -28,10 +28,7 @@
 #include "common/taskbar.h"
 #include "common/translation.h"
 
-#include "gui/launcher.h"	// For addGameToConf()
 #include "gui/massadd.h"
-#include "gui/widget.h"
-#include "gui/widgets/list.h"
 
 #ifndef DISABLE_MASS_ADD
 namespace GUI {
@@ -149,7 +146,7 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
 			debug(1, "  Added gameid '%s', desc '%s'\n",
 				iter->gameId.c_str(),
 				iter->description.c_str());
-			iter->gameId = addGameToConf(*iter);
+			iter->gameId = EngineMan.createTargetForGame(*iter);
 		}
 
 		// Write everything to disk
diff --git a/gui/massadd.h b/gui/massadd.h
index 58071cd..b954c87 100644
--- a/gui/massadd.h
+++ b/gui/massadd.h
@@ -24,6 +24,7 @@
 #define MASSADD_DIALOG_H
 
 #include "gui/dialog.h"
+#include "gui/widgets/list.h"
 #include "common/fs.h"
 #include "common/hashmap.h"
 #include "common/stack.h"


Commit: 90b78c544657bf0fc41d6b86276a0873060345b5
    https://github.com/scummvm/scummvm/commit/90b78c544657bf0fc41d6b86276a0873060345b5
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Merge GameDescriptor and DetectedGame

Changed paths:
    backends/platform/dc/selector.cpp
    base/commandLine.cpp
    base/plugins.cpp
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/game.cpp
    engines/game.h
    engines/metaengine.h
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    gui/editgamedialog.h
    gui/launcher.cpp
    gui/massadd.cpp
    gui/massadd.h


diff --git a/backends/platform/dc/selector.cpp b/backends/platform/dc/selector.cpp
index 033f06e..3192f3e 100644
--- a/backends/platform/dc/selector.cpp
+++ b/backends/platform/dc/selector.cpp
@@ -271,21 +271,21 @@ static int findGames(Game *games, int max, bool use_ini)
     }
 
     if (!use_ini) {
-      GameList candidates = EngineMan.detectGames(files);
+      DetectedGames candidates = EngineMan.detectGames(files);
 
-      for (GameList::const_iterator ge = candidates.begin();
+      for (DetectedGames::const_iterator ge = candidates.begin();
 	   ge != candidates.end(); ++ge)
 	if (curr_game < max) {
-	  strcpy(games[curr_game].filename_base, ge->gameid().c_str());
+	  strcpy(games[curr_game].filename_base, ge->gameId.c_str());
 	  strcpy(games[curr_game].dir, dirs[curr_dir-1].name);
-	  games[curr_game].language = ge->language();
-	  games[curr_game].platform = ge->platform();
+	  games[curr_game].language = ge->language;
+	  games[curr_game].platform = ge->platform;
 	  if (uniqueGame(games[curr_game].filename_base,
 			 games[curr_game].dir,
 			 games[curr_game].language,
 			 games[curr_game].platform, games, curr_game)) {
 
-	    strcpy(games[curr_game].text, ge->description().c_str());
+	    strcpy(games[curr_game].text, ge->description.c_str());
 #if 0
 	    printf("Registered game <%s> (l:%d p:%d) in <%s> <%s> because of <%s> <*>\n",
 		   games[curr_game].text,
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 02c3d1c..96548b9 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -854,13 +854,13 @@ static void listAudioDevices() {
 }
 
 /** Display all games in the given directory, or current directory if empty */
-static GameList getGameList(const Common::FSNode &dir) {
+static DetectedGames getGameList(const Common::FSNode &dir) {
 	Common::FSList files;
 
 	// Collect all files from directory
 	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
 		printf("Path %s does not exist or is not a directory.\n", dir.getPath().c_str());
-		return GameList();
+		return DetectedGames();
 	}
 
 	// detect Games
@@ -871,25 +871,18 @@ static GameList getGameList(const Common::FSNode &dir) {
 		g_system->logMessage(LogMessageType::kInfo, report.c_str());
 	}
 
-	DetectedGames detectedGames = detectionResults.listRecognizedGames();
-
-	GameList candidates;
-	for (uint i = 0; i < detectedGames.size(); i++) {
-		candidates.push_back(detectedGames[i].matchedGame);
-	}
-
-	return candidates;
+	return detectionResults.listRecognizedGames();
 }
 
-static GameList recListGames(const Common::FSNode &dir, const Common::String &gameId, bool recursive) {
-	GameList list = getGameList(dir);
+static DetectedGames recListGames(const Common::FSNode &dir, const Common::String &gameId, bool recursive) {
+	DetectedGames list = getGameList(dir);
 
 	if (recursive) {
 		Common::FSList files;
 		dir.getChildren(files, Common::FSNode::kListDirectoriesOnly);
 		for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) {
-			GameList rec = recListGames(*file, gameId, recursive);
-			for (GameList::const_iterator game = rec.begin(); game != rec.end(); ++game) {
+			DetectedGames rec = recListGames(*file, gameId, recursive);
+			for (DetectedGames::const_iterator game = rec.begin(); game != rec.end(); ++game) {
 				if (gameId.empty() || game->gameId == gameId)
 					list.push_back(*game);
 			}
@@ -904,7 +897,7 @@ static Common::String detectGames(const Common::String &path, const Common::Stri
 	bool noPath = path.empty();
 	//Current directory
 	Common::FSNode dir(path);
-	GameList candidates = recListGames(dir, gameId, recursive);
+	DetectedGames candidates = recListGames(dir, gameId, recursive);
 
 	if (candidates.empty()) {
 		printf("WARNING: ScummVM could not find any game in %s\n", dir.getPath().c_str());
@@ -919,7 +912,7 @@ static Common::String detectGames(const Common::String &path, const Common::Stri
 	// TODO this is not especially pretty
 	printf("ID             Description                                                Full Path\n");
 	printf("-------------- ---------------------------------------------------------- ---------------------------------------------------------\n");
-	for (GameList::iterator v = candidates.begin(); v != candidates.end(); ++v) {
+	for (DetectedGames::const_iterator v = candidates.begin(); v != candidates.end(); ++v) {
 		printf("%-14s %-58s %s\n", v->gameId.c_str(), v->description.c_str(), v->path.c_str());
 	}
 
@@ -928,17 +921,25 @@ static Common::String detectGames(const Common::String &path, const Common::Stri
 
 static int recAddGames(const Common::FSNode &dir, const Common::String &game, bool recursive) {
 	int count = 0;
-	GameList list = getGameList(dir);
-	for (GameList::iterator v = list.begin(); v != list.end(); ++v) {
+	DetectedGames list = getGameList(dir);
+	for (DetectedGames::const_iterator v = list.begin(); v != list.end(); ++v) {
 		if (v->gameId != game && !game.empty()) {
 			printf("Found %s, only adding %s per --game option, ignoring...\n", v->gameId.c_str(), game.c_str());
 		} else if (ConfMan.hasGameDomain(v->preferredTarget)) {
 			// TODO Better check for game already added?
 			printf("Found %s, but has already been added, skipping\n", v->gameId.c_str());
 		} else {
-			printf("Found %s, adding...\n", v->gameId.c_str());
-			EngineMan.createTargetForGame(*v);
+			Common::String target = EngineMan.createTargetForGame(*v);
 			count++;
+
+			// Display added game info
+			printf("Game Added: \n  Target:   %s\n  GameID:   %s\n  Name:     %s\n  Language: %s\n  Platform: %s\n",
+			       target.c_str(),
+			       v->gameId.c_str(),
+			       v->description.c_str(),
+			       Common::getLanguageDescription(v->language),
+			       Common::getPlatformDescription(v->platform)
+			);
 		}
 	}
 
@@ -998,15 +999,10 @@ static void runDetectorTest() {
 		}
 
 		DetectionResults detectionResults = EngineMan.detectGames(files);
-		DetectedGames detectedGames = detectionResults.listRecognizedGames();
-
-		GameList candidates;
-		for (uint i = 0; i < detectedGames.size(); i++) {
-			candidates.push_back(detectedGames[i].matchedGame);
-		}
+		DetectedGames candidates = detectionResults.listRecognizedGames();
 
 		bool gameidDiffers = false;
-		GameList::iterator x;
+		DetectedGames::const_iterator x;
 		for (x = candidates.begin(); x != candidates.end(); ++x) {
 			gameidDiffers |= (scumm_stricmp(gameid.c_str(), x->gameId.c_str()) != 0);
 		}
@@ -1083,14 +1079,9 @@ void upgradeTargets() {
 		Common::String desc(dom.getVal("description"));
 
 		DetectionResults detectionResults = EngineMan.detectGames(files);
-		DetectedGames detectedGames = detectionResults.listRecognizedGames();
-
-		GameList candidates;
-		for (uint i = 0; i < detectedGames.size(); i++) {
-			candidates.push_back(detectedGames[i].matchedGame);
-		}
+		DetectedGames candidates = detectionResults.listRecognizedGames();
 
-		GameDescriptor *g = 0;
+		DetectedGame *g = 0;
 
 		// We proceed as follows:
 		// * If detection failed to produce candidates, skip.
@@ -1103,7 +1094,7 @@ void upgradeTargets() {
 		}
 		if (candidates.size() > 1) {
 			// Scan over all candidates, check if there is a unique match for gameid, language and platform
-			GameList::iterator x;
+			DetectedGames::iterator x;
 			int matchesFound = 0;
 			for (x = candidates.begin(); x != candidates.end(); ++x) {
 				if (x->gameId == gameid && x->language == lang && x->platform == plat) {
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 1bfb929..40a4d77 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -531,7 +531,7 @@ DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const
 
 			for (uint i = 0; i < engineCandidates.size(); i++) {
 				engineCandidates[i].engineName = metaEngine.getName();
-				engineCandidates[i].matchedGame.path = path;
+				engineCandidates[i].path = path;
 				candidates.push_back(engineCandidates[i]);
 			}
 
@@ -554,7 +554,7 @@ void addStringToConf(const Common::String &key, const Common::String &value, con
 
 } // End of anonymous namespace
 
-Common::String EngineManager::createTargetForGame(const GameDescriptor &game) {
+Common::String EngineManager::createTargetForGame(const DetectedGame &game) {
 	// The auto detector or the user made a choice.
 	// Pick a domain name which does not yet exist (after all, we
 	// are *adding* a game to the config, not replacing).
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index b7bf4b4..8e7ec48 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -33,42 +33,32 @@
 #include "engines/advancedDetector.h"
 #include "engines/obsolete.h"
 
-static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGameDescriptor *sg) {
-	const char *title = 0;
+DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const {
+	const char *title;
 	const char *extra;
 
-	if (g.flags & ADGF_USEEXTRAASTITLE) {
-		title = g.extra;
+	if (adGame.desc->flags & ADGF_USEEXTRAASTITLE) {
+		title = adGame.desc->extra;
 		extra = "";
 	} else {
-		while (sg->gameId) {
-			if (!scumm_stricmp(g.gameId, sg->gameId))
-				title = sg->description;
-			sg++;
-		}
-
-		extra = g.extra;
+		const PlainGameDescriptor *pgd = findPlainGameDescriptor(adGame.desc->gameId, _gameIds);
+		title = pgd->description;
+		extra = adGame.desc->extra;
 	}
 
-	GameSupportLevel gsl = kStableGame;
-	if (g.flags & ADGF_UNSTABLE)
-		gsl = kUnstableGame;
-	else if (g.flags & ADGF_TESTING)
-		gsl = kTestingGame;
-
-	GameDescriptor gd(g.gameId, title, g.language, g.platform, extra);
-	gd.gameSupportLevel = gsl;
-	return gd;
-}
-
-DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const {
-	DetectedGame game;
+	DetectedGame game(adGame.desc->gameId, title, adGame.desc->language, adGame.desc->platform, extra);
 	game.engineName = getName();
-	game.gameId = adGame.desc->gameId;
 	game.hasUnknownFiles = adGame.hasUnknownFiles;
 	game.matchedFiles = adGame.matchedFiles;
-	game.matchedGame = toGameDescriptor(*adGame.desc, _gameIds);
-	updateGameDescriptor(game.matchedGame, adGame.desc);
+
+	game.gameSupportLevel = kStableGame;
+	if (adGame.desc->flags & ADGF_UNSTABLE)
+		game.gameSupportLevel = kUnstableGame;
+	else if (adGame.desc->flags & ADGF_TESTING)
+		game.gameSupportLevel = kTestingGame;
+
+	updateGameDescriptor(game, adGame.desc);
+
 	return game;
 }
 
@@ -112,8 +102,8 @@ static Common::String sanitizeName(const char *name) {
 	return res;
 }
 
-void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const {
-	if (_singleId != NULL) {
+void AdvancedMetaEngine::updateGameDescriptor(DetectedGame &desc, const ADGameDescription *realDesc) const {
+	if (_singleId) {
 		desc.preferredTarget = desc.gameId;
 		desc.gameId = _singleId;
 	}
@@ -321,7 +311,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
 
 	Common::updateGameGUIOptions(agdDesc.desc->guiOptions + _guiOptions, lang);
 
-	GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc.desc, _gameIds);
+	DetectedGame gameDescriptor = toDetectedGame(agdDesc);
 
 	bool showTestingWarning = false;
 
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 5762009..9f20164 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -321,7 +321,7 @@ protected:
 	ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const;
 
 	// TODO
-	void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const;
+	void updateGameDescriptor(DetectedGame &desc, const ADGameDescription *realDesc) const;
 
 	/**
 	 * Compose a hashmap of all files in fslist.
diff --git a/engines/game.cpp b/engines/game.cpp
index bdf8e32..d23a006 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -35,13 +35,19 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const Pla
 	return 0;
 }
 
-GameDescriptor::GameDescriptor() :
+DetectedGame::DetectedGame() :
+		engineName(nullptr),
+		hasUnknownFiles(false),
+		canBeAdded(true),
 		language(Common::UNK_LANG),
 		platform(Common::kPlatformUnknown),
 		gameSupportLevel(kStableGame) {
 }
 
-GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd) :
+DetectedGame::DetectedGame(const PlainGameDescriptor &pgd) :
+		engineName(nullptr),
+		hasUnknownFiles(false),
+		canBeAdded(true),
 		language(Common::UNK_LANG),
 		platform(Common::kPlatformUnknown),
 		gameSupportLevel(kStableGame) {
@@ -51,7 +57,12 @@ GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd) :
 	description = pgd.description;
 }
 
-GameDescriptor::GameDescriptor(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &ex) {
+DetectedGame::DetectedGame(const Common::String &id, const Common::String &d, Common::Language l, Common::Platform p, const Common::String &ex) :
+		engineName(nullptr),
+		hasUnknownFiles(false),
+		canBeAdded(true),
+		gameSupportLevel(kStableGame) {
+
 	gameId = id;
 	preferredTarget = id;
 	description = d;
@@ -63,21 +74,21 @@ GameDescriptor::GameDescriptor(const Common::String &id, const Common::String &d
 	description += updateDesc();
 }
 
-void GameDescriptor::setGUIOptions(const Common::String &guioptions) {
+void DetectedGame::setGUIOptions(const Common::String &guioptions) {
 	if (guioptions.empty())
 		_guiOptions.clear();
 	else
 		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
-void GameDescriptor::appendGUIOptions(const Common::String &str) {
+void DetectedGame::appendGUIOptions(const Common::String &str) {
 	if (!_guiOptions.empty())
 		_guiOptions += " ";
 
 	_guiOptions += str;
 }
 
-Common::String GameDescriptor::updateDesc() const {
+Common::String DetectedGame::updateDesc() const {
 	const bool hasCustomLanguage = (language != Common::UNK_LANG);
 	const bool hasCustomPlatform = (platform != Common::kPlatformUnknown);
 	const bool hasExtraDesc = !extra.empty();
@@ -140,7 +151,7 @@ Common::String DetectionResults::generateUnknownGameReport(bool translate, uint3
 	const char *reportEngineHeader = _s("Matched game IDs for the %s engine:");
 
 	Common::String report = Common::String::format(
-			translate ? _(reportStart) : reportStart, _detectedGames[0].matchedGame.path.c_str(),
+			translate ? _(reportStart) : reportStart, _detectedGames[0].path.c_str(),
 			"https://bugs.scummvm.org/"
 	);
 	report += "\n";
@@ -171,7 +182,7 @@ Common::String DetectionResults::generateUnknownGameReport(bool translate, uint3
 		// Add the gameId to the list of matched games for the engine
 		// TODO: Use the gameId here instead of the preferred target.
 		// This is currently impossible due to the AD singleId feature losing the information.
-		report += game.matchedGame.preferredTarget;
+		report += game.preferredTarget;
 
 		// Consolidate matched files across all engines and detection entries
 		for (FilePropertiesMap::const_iterator it = game.matchedFiles.begin(); it != game.matchedFiles.end(); it++) {
diff --git a/engines/game.h b/engines/game.h
index d675e7b..660a94a 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -71,53 +71,6 @@ enum GameSupportLevel {
 	kUnstableGame // the game is not even ready for public testing yet
 };
 
-/**
- * A hashmap describing details about a given game. In a sense this is a refined
- * version of PlainGameDescriptor, as it also contains a gameid and a description string.
- * But in addition, platform and language settings, as well as arbitrary other settings,
- * can be contained in a GameDescriptor.
- * This is an essential part of the glue between the game engines and the launcher code.
- */
-class GameDescriptor {
-public:
-	GameDescriptor();
-	explicit GameDescriptor(const PlainGameDescriptor &pgd);
-	GameDescriptor(const Common::String &id,
-	              const Common::String &description,
-	              Common::Language language = Common::UNK_LANG,
-	              Common::Platform platform = Common::kPlatformUnknown,
-	              const Common::String &extra = Common::String());
-
-	void setGUIOptions(const Common::String &options);
-	void appendGUIOptions(const Common::String &str);
-	Common::String getGUIOptions() const { return _guiOptions; }
-
-	Common::String gameId;
-	Common::String preferredTarget;
-	Common::String description;
-	Common::Language language;
-	Common::Platform platform;
-	Common::String path;
-	Common::String extra;
-
-	/**
-	 * What level of support is expected of this game
-	 */
-	GameSupportLevel gameSupportLevel;
-
-private:
-	/**
-	 * Update the description string by appending (EXTRA/PLATFORM/LANG) to it.
-	 * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be
-	 * added if no platform has been specified but a language and an extra string.
-	 */
-	Common::String updateDesc() const;
-
-	Common::String _guiOptions;
-};
-
-/** List of games. */
-typedef Common::Array<GameDescriptor> GameList;
 
 /**
  * A record describing the properties of a file. Used on the existing
@@ -135,20 +88,32 @@ struct FileProperties {
  */
 typedef Common::HashMap<Common::String, FileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FilePropertiesMap;
 
+/**
+ * Details about a given game.
+ *
+ * While PlainGameDescriptor refers to a game supported by an engine, this refers to a game copy
+ * that has been detected by an engine's detector.
+ * It contains all the necessary data to add the game to the configuration manager and / or to launch it.
+ */
 struct DetectedGame {
+	DetectedGame();
+	explicit DetectedGame(const PlainGameDescriptor &pgd);
+	DetectedGame(const Common::String &id,
+	               const Common::String &description,
+	               Common::Language language = Common::UNK_LANG,
+	               Common::Platform platform = Common::kPlatformUnknown,
+	               const Common::String &extra = Common::String());
+
+	void setGUIOptions(const Common::String &options);
+	void appendGUIOptions(const Common::String &str);
+	Common::String getGUIOptions() const { return _guiOptions; }
+
 	/**
 	 * The name of the engine supporting the detected game
 	 */
 	const char *engineName;
 
 	/**
-	 * The identifier of the detected game
-	 *
-	 * For engines using the singleId feature, this is the true engine-specific gameId, not the singleId.
-	 */
-	const char *gameId;
-
-	/**
 	 * A game was detected, but some files were not recognized
 	 *
 	 * This can happen when the md5 or size of the detected files did not match the engine's detection tables.
@@ -170,14 +135,31 @@ struct DetectedGame {
 	 */
 	bool canBeAdded;
 
+	Common::String gameId;
+	Common::String preferredTarget;
+	Common::String description;
+	Common::Language language;
+	Common::Platform platform;
+	Common::String path;
+	Common::String extra;
+
+	/**
+	 * What level of support is expected of this game
+	 */
+	GameSupportLevel gameSupportLevel;
+
+private:
 	/**
-	 * Details about the detected game
+	 * Update the description string by appending (EXTRA/PLATFORM/LANG) to it.
+	 * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be
+	 * added if no platform has been specified but a language and an extra string.
 	 */
-	GameDescriptor matchedGame;
+	Common::String updateDesc() const;
 
-	DetectedGame() : engineName(nullptr), gameId(nullptr), hasUnknownFiles(false), canBeAdded(true) {}
+	Common::String _guiOptions;
 };
 
+/** List of games. */
 typedef Common::Array<DetectedGame> DetectedGames;
 
 /**
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 6e8ab16..a95ff15 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -277,7 +277,7 @@ public:
 	 *
 	 * Returns the created target name.
 	 */
-	Common::String createTargetForGame(const GameDescriptor &game);
+	Common::String createTargetForGame(const DetectedGame &game);
 };
 
 /** Convenience shortcut for accessing the engine manager. */
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 1395fc4..fccb30b 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1036,15 +1036,14 @@ DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
 		const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions);
 		assert(g);
 
-		DetectedGame game;
-		game.matchedGame = GameDescriptor(x->game.gameid, g->description, x->language, x->game.platform, x->extra);
+		DetectedGame game = DetectedGame(x->game.gameid, g->description, x->language, x->game.platform, x->extra);
 
 		// Compute and set the preferred target name for this game.
 		// Based on generateComplexID() in advancedDetector.cpp.
-		game.matchedGame.preferredTarget = generatePreferredTarget(*x);
+		game.preferredTarget = generatePreferredTarget(*x);
 
-		game.matchedGame.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
-		game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
+		game.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
+		game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
 
 		detectedGames.push_back(game);
 	}
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index b8abd6b..6beaf02 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -182,18 +182,16 @@ DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
 			++sv;
 		}
 
-		DetectedGame game;
 		if (sv->dinnerTableEntries) {
 			Common::String extra = Common::String::format("v0.0%d %s", sv->version, sv->extraDesc);
 
-			game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown, extra);
-			game.matchedGame.setGUIOptions(sv->guioptions);
+			DetectedGame game = DetectedGame(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown, extra);
+			game.setGUIOptions(sv->guioptions);
 
+			detectedGames.push_back(game);
 		} else {
-			game.matchedGame = GameDescriptor(skySetting.gameId, skySetting.description);
+			detectedGames.push_back(DetectedGame(skySetting.gameId, skySetting.description));
 		}
-
-		detectedGames.push_back(game);
 	}
 
 	return detectedGames;
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 205640a..6504806 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -214,29 +214,29 @@ DetectedGames SwordMetaEngine::detectGames(const Common::FSList &fslist) const {
 
 	DetectedGame game;
 	if (mainFilesFound && pcFilesFound && demoFilesFound)
-		game.matchedGame = GameDescriptor(sword1DemoSettings);
+		game = DetectedGame(sword1DemoSettings);
 	else if (mainFilesFound && pcFilesFound && psxFilesFound)
-		game.matchedGame = GameDescriptor(sword1PSXSettings);
+		game = DetectedGame(sword1PSXSettings);
 	else if (mainFilesFound && pcFilesFound && psxDemoFilesFound)
-		game.matchedGame = GameDescriptor(sword1PSXDemoSettings);
+		game = DetectedGame(sword1PSXDemoSettings);
 	else if (mainFilesFound && pcFilesFound && !psxFilesFound)
-		game.matchedGame = GameDescriptor(sword1FullSettings);
+		game = DetectedGame(sword1FullSettings);
 	else if (mainFilesFound && macFilesFound)
-		game.matchedGame = GameDescriptor(sword1MacFullSettings);
+		game = DetectedGame(sword1MacFullSettings);
 	else if (mainFilesFound && macDemoFilesFound)
-		game.matchedGame = GameDescriptor(sword1MacDemoSettings);
+		game = DetectedGame(sword1MacDemoSettings);
 	else
 		return detectedGames;
 
-	game.matchedGame.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+	game.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
-	game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
 
 	detectedGames.push_back(game);
 
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 2310746..0ec0e3f 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -192,9 +192,8 @@ DetectedGames detectGamesImpl(const Common::FSList &fslist, bool recursion = fal
 						continue;
 
 					// Match found, add to list of candidates, then abort inner loop.
-					DetectedGame game;
-					game.matchedGame = GameDescriptor(g->gameid, g->description);
-					game.matchedGame.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
+					DetectedGame game = DetectedGame(g->gameid, g->description);
+					game.setGUIOptions(GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
 
 					detectedGames.push_back(game);
 					break;
@@ -285,7 +284,7 @@ Common::Error Sword2MetaEngine::createInstance(OSystem *syst, Engine **engine) c
 	DetectedGames detectedGames = detectGames(fslist);
 
 	for (uint i = 0; i < detectedGames.size(); i++) {
-		if (detectedGames[i].matchedGame.gameId == gameid) {
+		if (detectedGames[i].gameId == gameid) {
 			*engine = new Sword2::Sword2Engine(syst);
 			return Common::kNoError;
 		}
diff --git a/gui/editgamedialog.h b/gui/editgamedialog.h
index 06a8514..7c6a08e 100644
--- a/gui/editgamedialog.h
+++ b/gui/editgamedialog.h
@@ -40,8 +40,6 @@ class StaticTextWidget;
 class EditTextWidget;
 class SaveLoadChooser;
 
-Common::String addGameToConf(const GameDescriptor &result);
-
 /*
 * A dialog that allows the user to edit a config game entry.
 * TODO: add widgets for some/all of the following
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 67a62ad..7a37a7d 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -562,14 +562,14 @@ bool LauncherDialog::doGameDetection(const Common::String &path) {
 		// Display the candidates to the user and let her/him pick one
 		StringArray list;
 		for (idx = 0; idx < (int)candidates.size(); idx++)
-			list.push_back(candidates[idx].matchedGame.description);
+			list.push_back(candidates[idx].description);
 
 		ChooserDialog dialog(_("Pick the game:"));
 		dialog.setList(list);
 		idx = dialog.runModal();
 	}
 	if (0 <= idx && idx < (int)candidates.size()) {
-		const GameDescriptor &result = candidates[idx].matchedGame;
+		const DetectedGame &result = candidates[idx];
 
 		Common::String domain = EngineMan.createTargetForGame(result);
 
diff --git a/gui/massadd.cpp b/gui/massadd.cpp
index 56b15ec..8bc5a10 100644
--- a/gui/massadd.cpp
+++ b/gui/massadd.cpp
@@ -117,13 +117,13 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir)
 }
 
 struct GameTargetLess {
-	bool operator()(const GameDescriptor &x, const GameDescriptor &y) const {
+	bool operator()(const DetectedGame &x, const DetectedGame &y) const {
 		return x.preferredTarget.compareToIgnoreCase(y.preferredTarget) < 0;
 	}
 };
 
 struct GameDescLess {
-	bool operator()(const GameDescriptor &x, const GameDescriptor &y) const {
+	bool operator()(const DetectedGame &x, const DetectedGame &y) const {
 		return x.description.compareToIgnoreCase(y.description) < 0;
 	}
 };
@@ -142,7 +142,7 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
 		// people who want to edit their config file by hand after a mass add.
 		Common::sort(_games.begin(), _games.end(), GameTargetLess());
 		// Add all the detected games to the config
-		for (GameList::iterator iter = _games.begin(); iter != _games.end(); ++iter) {
+		for (DetectedGames::iterator iter = _games.begin(); iter != _games.end(); ++iter) {
 			debug(1, "  Added gameid '%s', desc '%s'\n",
 				iter->gameId.c_str(),
 				iter->description.c_str());
@@ -199,7 +199,8 @@ void MassAddDialog::handleTickle() {
 		// However, we only add games which are not already in the config file.
 		DetectedGames candidates = detectionResults.listRecognizedGames();
 		for (DetectedGames::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
-			const GameDescriptor &result = cand->matchedGame;
+			const DetectedGame &result = *cand;
+
 			Common::String path = dir.getPath();
 
 			// Remove trailing slashes
diff --git a/gui/massadd.h b/gui/massadd.h
index b954c87..b81a604 100644
--- a/gui/massadd.h
+++ b/gui/massadd.h
@@ -51,7 +51,7 @@ public:
 
 private:
 	Common::Stack<Common::FSNode>  _scanStack;
-	GameList _games;
+	DetectedGames _games;
 
 	/**
 	 * Map each path occuring in the config file to the target(s) using that path.


Commit: 2fe060e5c9491ddd7c8f639aab5d7c58c7c73092
    https://github.com/scummvm/scummvm/commit/2fe060e5c9491ddd7c8f639aab5d7c58c7c73092
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:04:23+02:00

Commit Message:
ENGINES: Cleanup DetectedGame initialization in the AD

Changed paths:
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/game.cpp


diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 8e7ec48..c7b5c18 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -33,33 +33,16 @@
 #include "engines/advancedDetector.h"
 #include "engines/obsolete.h"
 
-DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const {
-	const char *title;
-	const char *extra;
+static Common::String sanitizeName(const char *name) {
+	Common::String res;
 
-	if (adGame.desc->flags & ADGF_USEEXTRAASTITLE) {
-		title = adGame.desc->extra;
-		extra = "";
-	} else {
-		const PlainGameDescriptor *pgd = findPlainGameDescriptor(adGame.desc->gameId, _gameIds);
-		title = pgd->description;
-		extra = adGame.desc->extra;
+	while (*name) {
+		if (Common::isAlnum(*name))
+			res += tolower(*name);
+		name++;
 	}
 
-	DetectedGame game(adGame.desc->gameId, title, adGame.desc->language, adGame.desc->platform, extra);
-	game.engineName = getName();
-	game.hasUnknownFiles = adGame.hasUnknownFiles;
-	game.matchedFiles = adGame.matchedFiles;
-
-	game.gameSupportLevel = kStableGame;
-	if (adGame.desc->flags & ADGF_UNSTABLE)
-		game.gameSupportLevel = kUnstableGame;
-	else if (adGame.desc->flags & ADGF_TESTING)
-		game.gameSupportLevel = kTestingGame;
-
-	updateGameDescriptor(game, adGame.desc);
-
-	return game;
+	return res;
 }
 
 /**
@@ -68,8 +51,14 @@ DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) co
  * or (if ADGF_DEMO has been set)
  *   GAMEID-demo-PLAFORM-LANG
  */
-static Common::String generatePreferredTarget(const Common::String &id, const ADGameDescription *desc) {
-	Common::String res(id);
+static Common::String generatePreferredTarget(const ADGameDescription *desc) {
+	Common::String res;
+
+	if (desc->flags & ADGF_AUTOGENTARGET && desc->extra && *desc->extra) {
+		res = sanitizeName(desc->extra);
+	} else {
+		res = desc->gameId;
+	}
 
 	if (desc->flags & ADGF_DEMO) {
 		res = res + "-demo";
@@ -90,42 +79,43 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD
 	return res;
 }
 
-static Common::String sanitizeName(const char *name) {
-	Common::String res;
-
-	while (*name) {
-		if (Common::isAlnum(*name))
-			res += tolower(*name);
-		name++;
-	}
+DetectedGame AdvancedMetaEngine::toDetectedGame(const ADDetectedGame &adGame) const {
+	const ADGameDescription *desc = adGame.desc;
 
-	return res;
-}
+	const char *gameId = _singleId ? _singleId : desc->gameId;
 
-void AdvancedMetaEngine::updateGameDescriptor(DetectedGame &desc, const ADGameDescription *realDesc) const {
-	if (_singleId) {
-		desc.preferredTarget = desc.gameId;
-		desc.gameId = _singleId;
+	const char *title;
+	const char *extra;
+	if (desc->flags & ADGF_USEEXTRAASTITLE) {
+		title = desc->extra;
+		extra = "";
+	} else {
+		const PlainGameDescriptor *pgd = findPlainGameDescriptor(desc->gameId, _gameIds);
+		title = pgd->description;
+		extra = desc->extra;
 	}
 
-	if (desc.preferredTarget.empty())
-		desc.preferredTarget = desc.gameId;
+	DetectedGame game(gameId, title, desc->language, desc->platform, extra);
+	game.hasUnknownFiles = adGame.hasUnknownFiles;
+	game.matchedFiles = adGame.matchedFiles;
+	game.preferredTarget = generatePreferredTarget(desc);
 
-	if (realDesc->flags & ADGF_AUTOGENTARGET) {
-		if (*realDesc->extra)
-			desc.preferredTarget = sanitizeName(realDesc->extra);
-	}
+	game.gameSupportLevel = kStableGame;
+	if (desc->flags & ADGF_UNSTABLE)
+		game.gameSupportLevel = kUnstableGame;
+	else if (desc->flags & ADGF_TESTING)
+		game.gameSupportLevel = kTestingGame;
 
-	desc.preferredTarget = generatePreferredTarget(desc.preferredTarget, realDesc);
+	game.setGUIOptions(desc->guiOptions + _guiOptions);
+	game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(desc->language));
 
-	if (_flags & kADFlagUseExtraAsHint)
-		desc.extra = realDesc->extra;
+	if (desc->flags & ADGF_ADDENGLISH)
+		game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
 
-	desc.setGUIOptions(realDesc->guiOptions + _guiOptions);
-	desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language));
+	if (_flags & kADFlagUseExtraAsHint)
+		game.extra = desc->extra;
 
-	if (realDesc->flags & ADGF_ADDENGLISH)
-		desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
+	return game;
 }
 
 bool cleanupPirated(ADDetectedGames &matched) {
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 9f20164..326cb79 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -320,9 +320,6 @@ protected:
 	 */
 	ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const;
 
-	// TODO
-	void updateGameDescriptor(DetectedGame &desc, const ADGameDescription *realDesc) const;
-
 	/**
 	 * Compose a hashmap of all files in fslist.
 	 * Includes nifty stuff like removing trailing dots and ignoring case.
diff --git a/engines/game.cpp b/engines/game.cpp
index d23a006..3823000 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -75,10 +75,7 @@ DetectedGame::DetectedGame(const Common::String &id, const Common::String &d, Co
 }
 
 void DetectedGame::setGUIOptions(const Common::String &guioptions) {
-	if (guioptions.empty())
-		_guiOptions.clear();
-	else
-		_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
+	_guiOptions = Common::getGameGUIOptionsDescription(guioptions);
 }
 
 void DetectedGame::appendGUIOptions(const Common::String &str) {


Commit: 1dcb8076db64420ab28722a73583f89b38314e71
    https://github.com/scummvm/scummvm/commit/1dcb8076db64420ab28722a73583f89b38314e71
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-10T09:26:26+02:00

Commit Message:
ENGINES: Remove usage of C++11 extended initializer lists

Changed paths:
    base/plugins.cpp
    engines/advancedDetector.cpp
    engines/game.cpp
    engines/game.h
    engines/obsolete.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp


diff --git a/base/plugins.cpp b/base/plugins.cpp
index 40a4d77..023f2f3 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -459,11 +459,9 @@ DECLARE_SINGLETON(EngineManager);
  * and only if we can't find it there, we loop through the plugins.
  **/
 PlainGameDescriptor EngineManager::findGame(const Common::String &gameName, const Plugin **plugin) const {
-	PlainGameDescriptor result;
-
 	// First look for the game using the plugins in memory. This is critical
 	// for calls coming from inside games
-	result = findGameInLoadedPlugins(gameName, plugin);
+	PlainGameDescriptor result = findGameInLoadedPlugins(gameName, plugin);
 	if (result.gameId) {
 		return result;
 	}
@@ -497,7 +495,6 @@ PlainGameDescriptor EngineManager::findGame(const Common::String &gameName, cons
 PlainGameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin) const {
 	// Find the GameDescriptor for this target
 	const PluginList &plugins = getPlugins();
-	PlainGameDescriptor result;
 
 	if (plugin)
 		*plugin = 0;
@@ -505,14 +502,15 @@ PlainGameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String
 	PluginList::const_iterator iter;
 
 	for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
-		result = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
-		if (result.gameId) {
+		PlainGameDescriptor pgd = (*iter)->get<MetaEngine>().findGame(gameName.c_str());
+		if (pgd.gameId) {
 			if (plugin)
 				*plugin = *iter;
-			return result;
+			return pgd;
 		}
 	}
-	return result;
+
+	return PlainGameDescriptor::empty();
 }
 
 DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index c7b5c18..3167dd9 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -589,7 +589,7 @@ PlainGameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const {
 		return *g;
 
 	// No match found
-	return PlainGameDescriptor();
+	return PlainGameDescriptor::empty();
 }
 
 AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions)
diff --git a/engines/game.cpp b/engines/game.cpp
index 3823000..ee14acf 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -35,6 +35,20 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const Pla
 	return 0;
 }
 
+PlainGameDescriptor PlainGameDescriptor::empty() {
+	PlainGameDescriptor pgd;
+	pgd.gameId = nullptr;
+	pgd.description = nullptr;
+	return pgd;
+}
+
+PlainGameDescriptor PlainGameDescriptor::of(const char *gameId, const char *description) {
+	PlainGameDescriptor pgd;
+	pgd.gameId = gameId;
+	pgd.description = description;
+	return pgd;
+}
+
 DetectedGame::DetectedGame() :
 		engineName(nullptr),
 		hasUnknownFiles(false),
diff --git a/engines/game.h b/engines/game.h
index 660a94a..14f9962 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -39,8 +39,8 @@ struct PlainGameDescriptor {
 	const char *gameId;
 	const char *description;
 
-	PlainGameDescriptor() : gameId(nullptr), description(nullptr) {}
-	PlainGameDescriptor(const char *id, const char *desc) : gameId(id), description(desc) {}
+	static PlainGameDescriptor empty();
+	static PlainGameDescriptor of(const char *gameId, const char *description);
 };
 
 /**
diff --git a/engines/obsolete.cpp b/engines/obsolete.cpp
index 48809df..ea96cff 100644
--- a/engines/obsolete.cpp
+++ b/engines/obsolete.cpp
@@ -73,16 +73,16 @@ PlainGameDescriptor findGameID(
 			if (0 == scumm_stricmp(gameid, o->from)) {
 				g = findPlainGameDescriptor(o->to, gameids);
 				if (g && g->description)
-					return PlainGameDescriptor(gameid, g->description);
+					return PlainGameDescriptor::of(gameid, g->description);
 				else
-					return PlainGameDescriptor(gameid, "Obsolete game ID");
+					return PlainGameDescriptor::of(gameid, "Obsolete game ID");
 			}
 			o++;
 		}
 	}
 
 	// No match found
-	return PlainGameDescriptor();
+	return PlainGameDescriptor::empty();
 }
 
 } // End of namespace Engines
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index 6beaf02..642e4d3 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -138,7 +138,7 @@ const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &ta
 PlainGameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
 	if (0 == scumm_stricmp(gameid, skySetting.gameId))
 		return skySetting;
-	return PlainGameDescriptor();
+	return PlainGameDescriptor::empty();
 }
 
 DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 6504806..52394ce 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -140,7 +140,7 @@ PlainGameDescriptor SwordMetaEngine::findGame(const char *gameId) const {
 		return sword1PSXSettings;
 	if (0 == scumm_stricmp(gameId, sword1PSXDemoSettings.gameId))
 		return sword1PSXDemoSettings;
-	return PlainGameDescriptor();
+	return PlainGameDescriptor::empty();
 }
 
 void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool recursion = false) {
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 0ec0e3f..4d8399e 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -123,7 +123,7 @@ PlainGameList Sword2MetaEngine::getSupportedGames() const {
 	const Sword2::GameSettings *g = Sword2::sword2_settings;
 	PlainGameList games;
 	while (g->gameid) {
-		games.push_back(PlainGameDescriptor(g->gameid, g->description));
+		games.push_back(PlainGameDescriptor::of(g->gameid, g->description));
 		g++;
 	}
 	return games;
@@ -142,7 +142,7 @@ PlainGameDescriptor Sword2MetaEngine::findGame(const char *gameid) const {
 			break;
 		g++;
 	}
-	return PlainGameDescriptor(g->gameid, g->description);
+	return PlainGameDescriptor::of(g->gameid, g->description);
 }
 
 bool isFullGame(const Common::FSList &fslist) {


Commit: 61f9398b04a4bce397a8be6ae96491a2015a6da2
    https://github.com/scummvm/scummvm/commit/61f9398b04a4bce397a8be6ae96491a2015a6da2
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2018-05-28T18:43:15+02:00

Commit Message:
Merge pull request #1187 from bgK/detection-refactor-unknown

ENGINES: Return unknown game variants with the list of detected games

Changed paths:
    backends/platform/dc/selector.cpp
    base/commandLine.cpp
    base/main.cpp
    base/plugins.cpp
    engines/adl/detection.cpp
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/agi/detection.cpp
    engines/agos/detection.cpp
    engines/cge/detection.cpp
    engines/cge2/detection.cpp
    engines/cine/detection.cpp
    engines/director/detection.cpp
    engines/game.cpp
    engines/game.h
    engines/gob/detection/detection.cpp
    engines/made/detection.cpp
    engines/metaengine.h
    engines/mohawk/detection.cpp
    engines/obsolete.cpp
    engines/obsolete.h
    engines/queen/detection.cpp
    engines/saga/detection.cpp
    engines/sci/detection.cpp
    engines/scumm/detection.cpp
    engines/sky/detection.cpp
    engines/sludge/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    engines/tinsel/detection.cpp
    engines/toon/detection.cpp
    engines/touche/detection.cpp
    engines/tucker/detection.cpp
    engines/unknown-game-dialog.cpp
    engines/unknown-game-dialog.h
    engines/wintermute/detection.cpp
    gui/EventRecorder.cpp
    gui/editgamedialog.cpp
    gui/editgamedialog.h
    gui/launcher.cpp
    gui/launcher.h
    gui/massadd.cpp
    gui/massadd.h
    gui/recorderdialog.cpp







More information about the Scummvm-git-logs mailing list