ENGINES: Add unknown game variants to the game detector results
This commit is contained in:
parent
9587dd5c21
commit
cf1ebf2951
31 changed files with 597 additions and 428 deletions
|
@ -864,12 +864,20 @@ static GameList getGameList(const Common::FSNode &dir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect Games
|
// detect Games
|
||||||
GameList candidates(EngineMan.detectGames(files));
|
DetectionResults detectionResults = EngineMan.detectGames(files);
|
||||||
Common::String dataPath = dir.getPath();
|
|
||||||
// add game data path
|
if (detectionResults.foundUnknownGames()) {
|
||||||
for (GameList::iterator v = candidates.begin(); v != candidates.end(); ++v) {
|
Common::String report = detectionResults.generateUnknownGameReport(false, 80);
|
||||||
(*v)["path"] = dataPath;
|
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 candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,7 +1022,14 @@ static void runDetectorTest() {
|
||||||
continue;
|
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;
|
bool gameidDiffers = false;
|
||||||
GameList::iterator x;
|
GameList::iterator x;
|
||||||
for (x = candidates.begin(); x != candidates.end(); ++x) {
|
for (x = candidates.begin(); x != candidates.end(); ++x) {
|
||||||
|
@ -1092,7 +1107,14 @@ void upgradeTargets() {
|
||||||
Common::Platform plat = Common::parsePlatform(dom.getVal("platform"));
|
Common::Platform plat = Common::parsePlatform(dom.getVal("platform"));
|
||||||
Common::String desc(dom.getVal("description"));
|
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;
|
GameDescriptor *g = 0;
|
||||||
|
|
||||||
// We proceed as follows:
|
// We proceed as follows:
|
||||||
|
@ -1100,7 +1122,7 @@ void upgradeTargets() {
|
||||||
// * If there is a unique detector match, trust it.
|
// * If there is a unique detector match, trust it.
|
||||||
// * If there are multiple match, run over them comparing gameid, language and platform.
|
// * 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 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");
|
printf(" ... failed to detect game, skipping\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "base/plugins.h"
|
#include "base/plugins.h"
|
||||||
|
|
||||||
|
#include "common/translation.h"
|
||||||
#include "common/func.h"
|
#include "common/func.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/config-manager.h"
|
#include "common/config-manager.h"
|
||||||
|
@ -514,8 +515,9 @@ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &game
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList EngineManager::detectGames(const Common::FSList &fslist, bool useUnknownGameDialog) const {
|
DetectionResults EngineManager::detectGames(const Common::FSList &fslist) const {
|
||||||
GameList candidates;
|
DetectedGames candidates;
|
||||||
|
Common::String path = fslist.begin()->getParent().getPath();
|
||||||
PluginList plugins;
|
PluginList plugins;
|
||||||
PluginList::const_iterator iter;
|
PluginList::const_iterator iter;
|
||||||
PluginManager::instance().loadFirstPlugin();
|
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
|
// Iterate over all known games and for each check if it might be
|
||||||
// the game in the presented directory.
|
// the game in the presented directory.
|
||||||
for (iter = plugins.begin(); iter != plugins.end(); ++iter) {
|
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());
|
} while (PluginManager::instance().loadNextPlugin());
|
||||||
return candidates;
|
|
||||||
|
return DetectionResults(candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PluginList &EngineManager::getPlugins() const {
|
const PluginList &EngineManager::getPlugins() const {
|
||||||
return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
|
return PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Music plugins
|
// Music plugins
|
||||||
|
|
||||||
#include "audio/musicplugin.h"
|
#include "audio/musicplugin.h"
|
||||||
|
|
|
@ -332,9 +332,9 @@ public:
|
||||||
int getMaximumSaveSlot() const { return 'O' - 'A'; }
|
int getMaximumSaveSlot() const { return 'O' - 'A'; }
|
||||||
SaveStateList listSaves(const char *target) const;
|
SaveStateList listSaves(const char *target) const;
|
||||||
void removeSaveState(const char *target, int slot) 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;
|
bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
|
||||||
};
|
};
|
||||||
|
@ -492,14 +492,14 @@ Common::Platform getPlatform(const AdlGameDescription &adlDesc) {
|
||||||
return adlDesc.desc.platform;
|
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))
|
if (filePropsMap.contains(fname))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!allFiles.contains(fname))
|
if (!allFiles.contains(fname))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ADFileProperties fileProps;
|
FileProperties fileProps;
|
||||||
fileProps.size = computeMD5(allFiles[fname], fileProps.md5, 16384);
|
fileProps.size = computeMD5(allFiles[fname], fileProps.md5, 16384);
|
||||||
|
|
||||||
if (fileProps.size != -1) {
|
if (fileProps.size != -1) {
|
||||||
|
@ -511,42 +511,39 @@ bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on AdvancedMetaEngine::detectGame
|
// 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
|
// 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());
|
debug(3, "Starting disk image detection in dir '%s'", parent.getPath().c_str());
|
||||||
|
|
||||||
ADFilePropertiesMap filesProps;
|
FilePropertiesMap filesProps;
|
||||||
ADGameIdList matchedGameIds;
|
|
||||||
bool gotAnyMatchesWithAllFiles = false;
|
bool gotAnyMatchesWithAllFiles = false;
|
||||||
|
|
||||||
for (uint g = 0; gameDiskDescriptions[g].desc.gameId != 0; ++g) {
|
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
|
// Skip games that don't meet the language/platform/extra criteria
|
||||||
if (language != Common::UNK_LANG && desc.language != Common::UNK_LANG) {
|
if (language != Common::UNK_LANG && game.desc->language != Common::UNK_LANG) {
|
||||||
if (desc.language != language && !(language == Common::EN_ANY && (desc.flags & ADGF_ADDENGLISH)))
|
if (game.desc->language != language && !(language == Common::EN_ANY && (game.desc->flags & ADGF_ADDENGLISH)))
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
|
||||||
if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && desc.extra != extra)
|
if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && game.desc->extra != extra)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool fileMissing = false;
|
|
||||||
bool allFilesPresent = true;
|
bool allFilesPresent = true;
|
||||||
bool hashOrSizeMismatch = false;
|
|
||||||
|
|
||||||
for (uint f = 0; desc.filesDescriptions[f].fileName; ++f) {
|
for (uint f = 0; game.desc->filesDescriptions[f].fileName; ++f) {
|
||||||
const ADGameFileDescription &fDesc = desc.filesDescriptions[f];
|
const ADGameFileDescription &fDesc = game.desc->filesDescriptions[f];
|
||||||
Common::String fileName;
|
Common::String fileName;
|
||||||
bool foundDiskImage = false;
|
bool foundDiskImage = false;
|
||||||
|
|
||||||
for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) {
|
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);
|
Common::String testFileName(fDesc.fileName);
|
||||||
testFileName += diskImageExts[e].extension;
|
testFileName += diskImageExts[e].extension;
|
||||||
|
|
||||||
|
@ -563,49 +560,41 @@ ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const Fil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundDiskImage) {
|
if (!foundDiskImage) {
|
||||||
fileMissing = true;
|
|
||||||
allFilesPresent = false;
|
allFilesPresent = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashOrSizeMismatch)
|
game.matchedFiles[fileName] = filesProps[fileName];
|
||||||
|
|
||||||
|
if (game.hasUnknownFiles)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) {
|
if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) {
|
||||||
debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str());
|
debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str());
|
||||||
fileMissing = true;
|
game.hasUnknownFiles = true;
|
||||||
hashOrSizeMismatch = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) {
|
if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) {
|
||||||
debug(3, "Size Mismatch. Skipping");
|
debug(3, "Size Mismatch. Skipping");
|
||||||
fileMissing = true;
|
game.hasUnknownFiles = true;
|
||||||
hashOrSizeMismatch = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(3, "Matched file: %s", fileName.c_str());
|
debug(3, "Matched file: %s", fileName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileMissing) {
|
if (allFilesPresent && !game.hasUnknownFiles) {
|
||||||
debug(2, "Found game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
|
debug(2, "Found game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g);
|
||||||
matched.push_back(&desc);
|
gotAnyMatchesWithAllFiles = true;
|
||||||
|
matched.push_back(game);
|
||||||
} else {
|
} else {
|
||||||
if (allFilesPresent) {
|
if (allFilesPresent && !gotAnyMatchesWithAllFiles) {
|
||||||
gotAnyMatchesWithAllFiles = true;
|
if (matched.empty() || strcmp(matched.back().desc->gameId, game.desc->gameId) != 0)
|
||||||
if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), desc.gameId) != 0)
|
matched.push_back(game);
|
||||||
matchedGameIds.push_back(desc.gameId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(5, "Skipping game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
|
debug(5, "Skipping game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
#include "common/textconsole.h"
|
#include "common/textconsole.h"
|
||||||
#include "common/translation.h"
|
#include "common/translation.h"
|
||||||
#include "gui/EventRecorder.h"
|
#include "gui/EventRecorder.h"
|
||||||
#include "gui/gui-manager.h"
|
|
||||||
#include "engines/unknown-game-dialog.h"
|
|
||||||
#include "engines/advancedDetector.h"
|
#include "engines/advancedDetector.h"
|
||||||
#include "engines/obsolete.h"
|
#include "engines/obsolete.h"
|
||||||
|
|
||||||
|
@ -63,6 +61,17 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa
|
||||||
return gd;
|
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
|
* Generate a preferred target value as
|
||||||
* GAMEID-PLAFORM-LANG
|
* GAMEID-PLAFORM-LANG
|
||||||
|
@ -129,11 +138,11 @@ void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGame
|
||||||
desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
|
desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cleanupPirated(ADGameDescList &matched) {
|
bool cleanupPirated(ADDetectedGames &matched) {
|
||||||
// OKay, now let's sense presence of pirated games
|
// OKay, now let's sense presence of pirated games
|
||||||
if (!matched.empty()) {
|
if (!matched.empty()) {
|
||||||
for (uint j = 0; j < matched.size();) {
|
for (uint j = 0; j < matched.size();) {
|
||||||
if (matched[j]->flags & ADGF_PIRATED)
|
if (matched[j].desc->flags & ADGF_PIRATED)
|
||||||
matched.remove_at(j);
|
matched.remove_at(j);
|
||||||
else
|
else
|
||||||
++j;
|
++j;
|
||||||
|
@ -150,35 +159,46 @@ bool cleanupPirated(ADGameDescList &matched) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist, bool useUnknownGameDialog) const {
|
DetectedGames AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
|
||||||
ADGameDescList matches;
|
|
||||||
GameList detectedGames;
|
|
||||||
FileMap allFiles;
|
FileMap allFiles;
|
||||||
|
|
||||||
if (fslist.empty())
|
if (fslist.empty())
|
||||||
return detectedGames;
|
return DetectedGames();
|
||||||
|
|
||||||
// Compose a hashmap of all files in fslist.
|
// Compose a hashmap of all files in fslist.
|
||||||
composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
|
composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
|
||||||
|
|
||||||
// Run the detector on this
|
// 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()) {
|
cleanupPirated(matches);
|
||||||
// Use fallback detector if there were no matches by other means
|
|
||||||
const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist);
|
DetectedGames detectedGames;
|
||||||
if (fallbackDesc != nullptr) {
|
for (uint i = 0; i < matches.size(); i++) {
|
||||||
GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds));
|
DetectedGame game = toDetectedGame(matches[i]);
|
||||||
updateGameDescriptor(desc, fallbackDesc);
|
|
||||||
detectedGames.push_back(desc);
|
if (game.hasUnknownFiles) {
|
||||||
|
// Non fallback games with unknown files cannot be added/launched
|
||||||
|
game.canBeAdded = false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Otherwise use the found matches
|
detectedGames.push_back(game);
|
||||||
cleanupPirated(matches);
|
}
|
||||||
for (uint i = 0; i < matches.size(); i++) {
|
|
||||||
GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds));
|
bool foundKnownGames = false;
|
||||||
updateGameDescriptor(desc, matches[i]);
|
for (uint i = 0; i < detectedGames.size(); i++) {
|
||||||
detectedGames.push_back(desc);
|
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 {
|
Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
|
||||||
assert(engine);
|
assert(engine);
|
||||||
|
|
||||||
const ADGameDescription *agdDesc = 0;
|
|
||||||
Common::Language language = Common::UNK_LANG;
|
Common::Language language = Common::UNK_LANG;
|
||||||
Common::Platform platform = Common::kPlatformUnknown;
|
Common::Platform platform = Common::kPlatformUnknown;
|
||||||
Common::String extra;
|
Common::String extra;
|
||||||
|
@ -266,46 +285,43 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
|
||||||
composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
|
composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
|
||||||
|
|
||||||
// Run the detector on this
|
// 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))
|
if (cleanupPirated(matches))
|
||||||
return Common::kNoGameDataFoundError;
|
return Common::kNoGameDataFoundError;
|
||||||
|
|
||||||
if (_singleId == NULL) {
|
ADDetectedGame agdDesc;
|
||||||
// Find the first match with correct gameid.
|
for (uint i = 0; i < matches.size(); i++) {
|
||||||
for (uint i = 0; i < matches.size(); i++) {
|
if ((_singleId || matches[i].desc->gameId == gameid) && !matches[i].hasUnknownFiles) {
|
||||||
if (matches[i]->gameId == gameid) {
|
agdDesc = matches[i];
|
||||||
agdDesc = matches[i];
|
break;
|
||||||
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
|
// Use fallback detector if there were no matches by other means
|
||||||
agdDesc = fallbackDetect(allFiles, files);
|
ADDetectedGame fallbackDetectedGame = fallbackDetect(allFiles, files);
|
||||||
if (agdDesc != 0) {
|
agdDesc = fallbackDetectedGame;
|
||||||
|
if (agdDesc.desc) {
|
||||||
// Seems we found a fallback match. But first perform a basic
|
// Seems we found a fallback match. But first perform a basic
|
||||||
// sanity check: the gameid must match.
|
// sanity check: the gameid must match.
|
||||||
if (_singleId == NULL && agdDesc->gameId != gameid)
|
if (!_singleId && agdDesc.desc->gameId != gameid)
|
||||||
agdDesc = 0;
|
agdDesc = ADDetectedGame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agdDesc == 0)
|
if (!agdDesc.desc)
|
||||||
return Common::kNoGameDataFoundError;
|
return Common::kNoGameDataFoundError;
|
||||||
|
|
||||||
// If the GUI options were updated, we catch this here and update them in the users config
|
// If the GUI options were updated, we catch this here and update them in the users config
|
||||||
// file transparently.
|
// file transparently.
|
||||||
Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc->language);
|
Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc.desc->language);
|
||||||
if (agdDesc->flags & ADGF_ADDENGLISH)
|
if (agdDesc.desc->flags & ADGF_ADDENGLISH)
|
||||||
lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY);
|
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;
|
bool showTestingWarning = false;
|
||||||
|
|
||||||
|
@ -320,65 +336,13 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
|
||||||
return Common::kUserCanceled;
|
return Common::kUserCanceled;
|
||||||
|
|
||||||
debug(2, "Running %s", gameDescriptor.description().c_str());
|
debug(2, "Running %s", gameDescriptor.description().c_str());
|
||||||
initSubSystems(agdDesc);
|
initSubSystems(agdDesc.desc);
|
||||||
if (!createInstance(syst, engine, agdDesc))
|
if (!createInstance(syst, engine, agdDesc.desc))
|
||||||
return Common::kNoGameDataFoundError;
|
return Common::kNoGameDataFoundError;
|
||||||
else
|
else
|
||||||
return Common::kNoError;
|
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 {
|
void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const {
|
||||||
if (depth <= 0)
|
if (depth <= 0)
|
||||||
return;
|
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
|
// FIXME/TODO: We don't handle the case that a file is listed as a regular
|
||||||
// file and as one with resource fork.
|
// file and as one with resource fork.
|
||||||
|
|
||||||
|
@ -449,8 +413,9 @@ bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const F
|
||||||
return true;
|
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 {
|
ADDetectedGames AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
|
||||||
ADFilePropertiesMap filesProps;
|
FilePropertiesMap filesProps;
|
||||||
|
ADDetectedGames matched;
|
||||||
|
|
||||||
const ADGameFileDescription *fileDesc;
|
const ADGameFileDescription *fileDesc;
|
||||||
const ADGameDescription *g;
|
const ADGameDescription *g;
|
||||||
|
@ -465,7 +430,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
|
||||||
|
|
||||||
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
|
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
|
||||||
Common::String fname = fileDesc->fileName;
|
Common::String fname = fileDesc->fileName;
|
||||||
ADFileProperties tmp;
|
FileProperties tmp;
|
||||||
|
|
||||||
if (filesProps.contains(fname))
|
if (filesProps.contains(fname))
|
||||||
continue;
|
continue;
|
||||||
|
@ -477,8 +442,6 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ADGameDescList matched;
|
|
||||||
ADGameIdList matchedGameIds;
|
|
||||||
int maxFilesMatched = 0;
|
int maxFilesMatched = 0;
|
||||||
bool gotAnyMatchesWithAllFiles = false;
|
bool gotAnyMatchesWithAllFiles = false;
|
||||||
|
|
||||||
|
@ -486,7 +449,6 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
|
||||||
uint i;
|
uint i;
|
||||||
for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize, ++i) {
|
for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != nullptr; descPtr += _descItemSize, ++i) {
|
||||||
g = (const ADGameDescription *)descPtr;
|
g = (const ADGameDescription *)descPtr;
|
||||||
bool fileMissing = false;
|
|
||||||
|
|
||||||
// Do not even bother to look at entries which do not have matching
|
// Do not even bother to look at entries which do not have matching
|
||||||
// language and platform (if specified).
|
// 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)
|
if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
ADDetectedGame game(g);
|
||||||
bool allFilesPresent = true;
|
bool allFilesPresent = true;
|
||||||
int curFilesMatched = 0;
|
int curFilesMatched = 0;
|
||||||
bool hashOrSizeMismatch = false;
|
|
||||||
|
|
||||||
// Try to match all files for this game
|
// 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;
|
Common::String tstr = fileDesc->fileName;
|
||||||
|
|
||||||
if (!filesProps.contains(tstr)) {
|
if (!filesProps.contains(tstr)) {
|
||||||
fileMissing = true;
|
|
||||||
allFilesPresent = false;
|
allFilesPresent = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashOrSizeMismatch)
|
game.matchedFiles[tstr] = filesProps[tstr];
|
||||||
|
|
||||||
|
if (game.hasUnknownFiles)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fileDesc->md5 != nullptr && 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());
|
debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str());
|
||||||
fileMissing = true;
|
game.hasUnknownFiles = true;
|
||||||
hashOrSizeMismatch = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) {
|
if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) {
|
||||||
debug(3, "Size Mismatch. Skipping");
|
debug(3, "Size Mismatch. Skipping");
|
||||||
fileMissing = true;
|
game.hasUnknownFiles = true;
|
||||||
hashOrSizeMismatch = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,13 +504,12 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
|
||||||
// Potentially this could rule out variants where some particular file
|
// Potentially this could rule out variants where some particular file
|
||||||
// is really missing, but the developers should better know about such
|
// is really missing, but the developers should better know about such
|
||||||
// cases.
|
// cases.
|
||||||
if (allFilesPresent) {
|
if (allFilesPresent && !gotAnyMatchesWithAllFiles) {
|
||||||
gotAnyMatchesWithAllFiles = true;
|
if (matched.empty() || strcmp(matched.back().desc->gameId, g->gameId) != 0)
|
||||||
if (matchedGameIds.empty() || strcmp(matchedGameIds.back(), g->gameId) != 0)
|
matched.push_back(game);
|
||||||
matchedGameIds.push_back(g->gameId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileMissing) {
|
if (allFilesPresent && !game.hasUnknownFiles) {
|
||||||
debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
|
debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
|
||||||
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
|
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
|
||||||
|
|
||||||
|
@ -558,37 +518,29 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
|
||||||
maxFilesMatched = curFilesMatched;
|
maxFilesMatched = curFilesMatched;
|
||||||
|
|
||||||
matched.clear(); // Remove any prior, lower ranked matches.
|
matched.clear(); // Remove any prior, lower ranked matches.
|
||||||
matched.push_back(g);
|
matched.push_back(game);
|
||||||
} else if (curFilesMatched == maxFilesMatched) {
|
} else if (curFilesMatched == maxFilesMatched) {
|
||||||
matched.push_back(g);
|
matched.push_back(game);
|
||||||
} else {
|
} else {
|
||||||
debug(2, " ... skipped");
|
debug(2, " ... skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gotAnyMatchesWithAllFiles = true;
|
||||||
} else {
|
} else {
|
||||||
debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
|
debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
|
||||||
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
|
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;
|
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 ADFileBasedFallback *ptr;
|
||||||
const char* const* filenames;
|
const char* const* filenames;
|
||||||
|
|
||||||
int maxNumMatchedFiles = 0;
|
int maxNumMatchedFiles = 0;
|
||||||
const ADGameDescription *matchedDesc = 0;
|
ADDetectedGame result;
|
||||||
|
|
||||||
for (ptr = fileBasedFallback; ptr->desc; ++ptr) {
|
for (ptr = fileBasedFallback; ptr->desc; ++ptr) {
|
||||||
const ADGameDescription *agdesc = ptr->desc;
|
const ADGameDescription *agdesc = ptr->desc;
|
||||||
|
@ -609,25 +561,26 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &
|
||||||
debug(4, "Matched: %s", agdesc->gameId);
|
debug(4, "Matched: %s", agdesc->gameId);
|
||||||
|
|
||||||
if (numMatchedFiles > maxNumMatchedFiles) {
|
if (numMatchedFiles > maxNumMatchedFiles) {
|
||||||
matchedDesc = agdesc;
|
|
||||||
maxNumMatchedFiles = numMatchedFiles;
|
maxNumMatchedFiles = numMatchedFiles;
|
||||||
|
|
||||||
debug(4, "and overridden");
|
debug(4, "and overridden");
|
||||||
|
|
||||||
if (filesProps) {
|
ADDetectedGame game(agdesc);
|
||||||
for (filenames = ptr->filenames; *filenames; ++filenames) {
|
game.hasUnknownFiles = true;
|
||||||
ADFileProperties tmp;
|
|
||||||
|
|
||||||
if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp))
|
for (filenames = ptr->filenames; *filenames; ++filenames) {
|
||||||
(*filesProps)[*filenames] = tmp;
|
FileProperties tmp;
|
||||||
}
|
|
||||||
|
if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp))
|
||||||
|
game.matchedFiles[*filenames] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = game;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchedDesc;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList AdvancedMetaEngine::getSupportedGames() const {
|
GameList AdvancedMetaEngine::getSupportedGames() const {
|
||||||
|
|
|
@ -47,20 +47,6 @@ struct ADGameFileDescription {
|
||||||
int32 fileSize; ///< Size of the described file. Set to -1 to ignore.
|
int32 fileSize; ///< Size of the described file. Set to -1 to ignore.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* A shortcut to produce an empty ADGameFileDescription record. Used to mark
|
||||||
* the end of a list of these.
|
* 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;
|
||||||
|
|
||||||
/**
|
ADDetectedGame() : desc(nullptr), hasUnknownFiles(false) {}
|
||||||
* A list of raw game ID strings.
|
explicit ADDetectedGame(const ADGameDescription *d) : desc(d), hasUnknownFiles(false) {}
|
||||||
*/
|
};
|
||||||
typedef Common::Array<const char *> ADGameIdList;
|
|
||||||
|
/** A list of games detected by the AD */
|
||||||
|
typedef Common::Array<ADDetectedGame> ADDetectedGames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End marker for a table of ADGameDescription structs. Use this to
|
* End marker for a table of ADGameDescription structs. Use this to
|
||||||
|
@ -278,7 +269,7 @@ public:
|
||||||
|
|
||||||
virtual GameDescriptor findGame(const char *gameId) 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;
|
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
|
||||||
|
|
||||||
|
@ -294,8 +285,8 @@ protected:
|
||||||
* An (optional) generic fallback detect function which is invoked
|
* An (optional) generic fallback detect function which is invoked
|
||||||
* if the regular MD5 based detection failed to detect anything.
|
* if the regular MD5 based detection failed to detect anything.
|
||||||
*/
|
*/
|
||||||
virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -313,7 +304,7 @@ protected:
|
||||||
* @param extra restrict results to specified extra string (only if kADFlagUseExtraAsHint is set)
|
* @param extra restrict results to specified extra string (only if kADFlagUseExtraAsHint is set)
|
||||||
* @return list of ADGameDescription pointers corresponding to matched games
|
* @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.
|
* Iterates over all ADFileBasedFallback records inside fileBasedFallback.
|
||||||
|
@ -327,13 +318,7 @@ protected:
|
||||||
* @param fileBasedFallback a list of ADFileBasedFallback records, zero-terminated
|
* @param fileBasedFallback a list of ADFileBasedFallback records, zero-terminated
|
||||||
* @param filesProps if not 0, return a map of properties for all detected files here
|
* @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;
|
ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) 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;
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const;
|
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;
|
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. */
|
/** 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
|
#endif
|
||||||
|
|
|
@ -220,7 +220,7 @@ public:
|
||||||
virtual void removeSaveState(const char *target, int slot) const;
|
virtual void removeSaveState(const char *target, int slot) const;
|
||||||
SaveStateDescriptor querySaveMetaInfos(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 {
|
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;
|
typedef Common::HashMap<Common::String, int32> IntMap;
|
||||||
IntMap allFiles;
|
IntMap allFiles;
|
||||||
bool matchedUsingFilenames = false;
|
bool matchedUsingFilenames = false;
|
||||||
|
@ -584,10 +584,10 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
|
||||||
|
|
||||||
g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str());
|
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)
|
#if PLUGIN_ENABLED_DYNAMIC(AGI)
|
||||||
|
|
|
@ -126,7 +126,7 @@ public:
|
||||||
return "Soltys (C) 1994-1996 L.K. Avalon";
|
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 hasFeature(MetaEngineFeature f) const;
|
||||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
|
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
|
||||||
virtual int getMaximumSaveSlot() const;
|
virtual int getMaximumSaveSlot() const;
|
||||||
|
@ -135,13 +135,8 @@ public:
|
||||||
virtual void removeSaveState(const char *target, int slot) const;
|
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 = {
|
static ADGameDescription s_fallbackDesc = {
|
||||||
"Soltys",
|
"soltys",
|
||||||
"Unknown version",
|
"Unknown version",
|
||||||
AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
||||||
Common::UNK_LANG,
|
Common::UNK_LANG,
|
||||||
|
@ -150,28 +145,29 @@ static ADGameDescription s_fallbackDesc = {
|
||||||
GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
|
GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ADGameDescription *CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
static const ADFileBasedFallback fileBasedFallback[] = {
|
||||||
ADFilePropertiesMap filesProps;
|
{ &s_fallbackDesc, { "vol.cat", "vol.dat", 0 } },
|
||||||
|
{ 0, { 0 } }
|
||||||
|
};
|
||||||
|
|
||||||
const ADGameDescription *game;
|
ADDetectedGame CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
||||||
game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback, &filesProps);
|
ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback);
|
||||||
|
|
||||||
if (!game)
|
if (!game.desc)
|
||||||
return nullptr;
|
return ADDetectedGame();
|
||||||
|
|
||||||
SearchMan.addDirectory("CGEMetaEngine::fallbackDetect", fslist.begin()->getParent());
|
SearchMan.addDirectory("CGEMetaEngine::fallbackDetect", fslist.begin()->getParent());
|
||||||
ResourceManager *resman;
|
ResourceManager *resman;
|
||||||
resman = new ResourceManager();
|
resman = new ResourceManager();
|
||||||
bool result = resman->exist("CGE.SAY");
|
bool sayFileFound = resman->exist("CGE.SAY");
|
||||||
delete resman;
|
delete resman;
|
||||||
|
|
||||||
SearchMan.remove("CGEMetaEngine::fallbackDetect");
|
SearchMan.remove("CGEMetaEngine::fallbackDetect");
|
||||||
|
|
||||||
if (!result)
|
if (!sayFileFound)
|
||||||
return nullptr;
|
return ADDetectedGame();
|
||||||
|
|
||||||
reportUnknown(fslist.begin()->getParent(), filesProps);
|
return game;
|
||||||
return &s_fallbackDesc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const {
|
bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||||
|
|
|
@ -132,7 +132,7 @@ public:
|
||||||
return "Sfinx (C) 1994-1997 Janus B. Wisniewski and L.K. Avalon";
|
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 createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
|
||||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual int getMaximumSaveSlot() const;
|
virtual int getMaximumSaveSlot() const;
|
||||||
|
@ -141,13 +141,8 @@ public:
|
||||||
virtual void removeSaveState(const char *target, int slot) const;
|
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 = {
|
static ADGameDescription s_fallbackDesc = {
|
||||||
"Sfinx",
|
"sfinx",
|
||||||
"Unknown version",
|
"Unknown version",
|
||||||
AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
|
||||||
Common::UNK_LANG,
|
Common::UNK_LANG,
|
||||||
|
@ -156,30 +151,31 @@ static ADGameDescription s_fallbackDesc = {
|
||||||
GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF)
|
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
|
// 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.
|
// 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 {
|
ADDetectedGame CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
||||||
ADFilePropertiesMap filesProps;
|
ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback);
|
||||||
|
|
||||||
const ADGameDescription *game;
|
if (!game.desc)
|
||||||
game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback, &filesProps);
|
return ADDetectedGame();
|
||||||
|
|
||||||
if (!game)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
SearchMan.addDirectory("CGE2MetaEngine::fallbackDetect", fslist.begin()->getParent());
|
SearchMan.addDirectory("CGE2MetaEngine::fallbackDetect", fslist.begin()->getParent());
|
||||||
ResourceManager *resman;
|
ResourceManager *resman;
|
||||||
resman = new ResourceManager();
|
resman = new ResourceManager();
|
||||||
bool result = resman->exist("CGE.SAY");
|
bool sayFileFound = resman->exist("CGE.SAY");
|
||||||
delete resman;
|
delete resman;
|
||||||
|
|
||||||
SearchMan.remove("CGE2MetaEngine::fallbackDetect");
|
SearchMan.remove("CGE2MetaEngine::fallbackDetect");
|
||||||
|
|
||||||
if (!result)
|
if (!sayFileFound)
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
|
|
||||||
reportUnknown(fslist.begin()->getParent(), filesProps);
|
return game;
|
||||||
return &s_fallbackDesc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||||
|
|
|
@ -112,7 +112,7 @@ public:
|
||||||
return "Macromedia Director (C) Macromedia";
|
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;
|
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];
|
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
|
// TODO: Handle Mac fallback
|
||||||
|
|
||||||
// reset fallback description
|
// reset fallback description
|
||||||
|
@ -230,10 +230,10 @@ const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFi
|
||||||
|
|
||||||
warning("Director fallback detection D%d", desc->version);
|
warning("Director fallback detection D%d", desc->version);
|
||||||
|
|
||||||
return (ADGameDescription *)desc;
|
return ADDetectedGame(&desc->desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PLUGIN_ENABLED_DYNAMIC(DIRECTOR)
|
#if PLUGIN_ENABLED_DYNAMIC(DIRECTOR)
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "engines/game.h"
|
#include "engines/game.h"
|
||||||
#include "common/gui_options.h"
|
#include "common/gui_options.h"
|
||||||
|
#include "common/translation.h"
|
||||||
|
|
||||||
|
|
||||||
const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) {
|
const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) {
|
||||||
|
@ -124,3 +125,89 @@ void GameDescriptor::setSupportLevel(GameSupportLevel gsl) {
|
||||||
erase("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;
|
||||||
|
}
|
||||||
|
|
104
engines/game.h
104
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
|
#endif
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
|
|
||||||
virtual GameDescriptor findGame(const char *gameId) const;
|
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 *getName() const;
|
||||||
virtual const char *getOriginalCopyright() const;
|
virtual const char *getOriginalCopyright() const;
|
||||||
|
@ -63,25 +63,22 @@ GameDescriptor GobMetaEngine::findGame(const char *gameId) const {
|
||||||
return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
|
return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
ADDetectedGame GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
|
||||||
ADFilePropertiesMap filesProps;
|
ADDetectedGame detectedGame = detectGameFilebased(allFiles, fslist, Gob::fileBased);
|
||||||
|
if (!detectedGame.desc) {
|
||||||
|
return ADDetectedGame();
|
||||||
|
}
|
||||||
|
|
||||||
const Gob::GOBGameDescription *game;
|
const Gob::GOBGameDescription *game = (const Gob::GOBGameDescription *)detectedGame.desc;
|
||||||
game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps);
|
|
||||||
if (!game)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (game->gameType == Gob::kGameTypeOnceUponATime) {
|
if (game->gameType == Gob::kGameTypeOnceUponATime) {
|
||||||
game = detectOnceUponATime(fslist);
|
game = detectOnceUponATime(fslist);
|
||||||
if (!game)
|
if (game) {
|
||||||
return 0;
|
detectedGame.desc = &game->desc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ADGameIdList gameIds;
|
return detectedGame;
|
||||||
gameIds.push_back(game->desc.gameId);
|
|
||||||
|
|
||||||
reportUnknown(fslist.begin()->getParent(), filesProps, gameIds);
|
|
||||||
return (const ADGameDescription *)game;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) {
|
const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) {
|
||||||
|
|
|
@ -535,7 +535,7 @@ public:
|
||||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) 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;
|
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.
|
// Set the default values for the fallback descriptor's ADGameDescription part.
|
||||||
Made::g_fallbackDesc.desc.language = Common::UNK_LANG;
|
Made::g_fallbackDesc.desc.language = Common::UNK_LANG;
|
||||||
Made::g_fallbackDesc.desc.platform = Common::kPlatformDOS;
|
Made::g_fallbackDesc.desc.platform = Common::kPlatformDOS;
|
||||||
|
@ -569,7 +569,7 @@ const ADGameDescription *MadeMetaEngine::fallbackDetect(const FileMap &allFiles,
|
||||||
Made::g_fallbackDesc.version = 3;
|
Made::g_fallbackDesc.version = 3;
|
||||||
|
|
||||||
//return (const ADGameDescription *)&Made::g_fallbackDesc;
|
//return (const ADGameDescription *)&Made::g_fallbackDesc;
|
||||||
return NULL;
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PLUGIN_ENABLED_DYNAMIC(MADE)
|
#if PLUGIN_ENABLED_DYNAMIC(MADE)
|
||||||
|
|
|
@ -79,7 +79,7 @@ public:
|
||||||
* (possibly empty) list of games supported by the engine which it was able
|
* (possibly empty) list of games supported by the engine which it was able
|
||||||
* to detect amongst the given files.
|
* 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
|
* Tries to instantiate an engine instance based on the settings of
|
||||||
|
@ -269,7 +269,7 @@ class EngineManager : public Common::Singleton<EngineManager> {
|
||||||
public:
|
public:
|
||||||
GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const;
|
GameDescriptor findGameInLoadedPlugins(const Common::String &gameName, const Plugin **plugin = NULL) const;
|
||||||
GameDescriptor findGame(const Common::String &gameName, const Plugin **plugin = NULL) const;
|
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;
|
const PluginList &getPlugins() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ public:
|
||||||
_directoryGlobs = directoryGlobs;
|
_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);
|
return detectGameFilebased(allFiles, fslist, Mohawk::fileBased);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -447,7 +447,7 @@ public:
|
||||||
virtual int getMaximumSaveSlot() const { return 99; }
|
virtual int getMaximumSaveSlot() const { return 99; }
|
||||||
virtual void removeSaveState(const char *target, int slot) const;
|
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 {
|
bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||||
|
@ -457,7 +457,7 @@ bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||||
(f == kSupportsDeleteSave);
|
(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;
|
static ADGameDescription desc;
|
||||||
|
|
||||||
// Iterate over all files in the given directory
|
// Iterate over all files in the given directory
|
||||||
|
@ -492,11 +492,13 @@ const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles
|
||||||
desc.extra = "Talkie";
|
desc.extra = "Talkie";
|
||||||
desc.guiOptions = GAMEOPTION_ALT_INTRO;
|
desc.guiOptions = GAMEOPTION_ALT_INTRO;
|
||||||
}
|
}
|
||||||
return (const ADGameDescription *)&desc;
|
|
||||||
|
return ADDetectedGame(&desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveStateList QueenMetaEngine::listSaves(const char *target) const {
|
SaveStateList QueenMetaEngine::listSaves(const char *target) const {
|
||||||
|
|
|
@ -562,7 +562,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
|
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 bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual SaveStateList listSaves(const char *target) const;
|
virtual SaveStateList listSaves(const char *target) const;
|
||||||
virtual int getMaximumSaveSlot() 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 foundResMap = false;
|
||||||
bool foundRes000 = 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 these files aren't found, it can't be SCI
|
||||||
if (!foundResMap && !foundRes000)
|
if (!foundResMap && !foundRes000)
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
|
|
||||||
ResourceManager resMan(true);
|
ResourceManager resMan(true);
|
||||||
resMan.addAppropriateSourcesForDetection(fslist);
|
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,
|
// Is SCI32 compiled in? If not, and this is a SCI32 game,
|
||||||
// stop here
|
// stop here
|
||||||
if (getSciVersionForDetection() >= SCI_VERSION_2)
|
if (getSciVersionForDetection() >= SCI_VERSION_2)
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ViewType gameViews = resMan.getViewType();
|
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
|
// 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
|
// but doesn't share SCI format at all
|
||||||
if (gameViews == kViewUnknown)
|
if (gameViews == kViewUnknown)
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
|
|
||||||
// Set the platform to Amiga if the game is using Amiga views
|
// Set the platform to Amiga if the game is using Amiga views
|
||||||
if (gameViews == kViewAmiga)
|
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 we don't have a game id, the game is not SCI
|
||||||
if (sierraGameId.empty())
|
if (sierraGameId.empty())
|
||||||
return 0;
|
return ADDetectedGame();
|
||||||
|
|
||||||
Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
|
Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
|
||||||
strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
|
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";
|
s_fallbackDesc.extra = "CD";
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s_fallbackDesc;
|
return ADDetectedGame(&s_fallbackDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||||
|
|
|
@ -961,7 +961,7 @@ public:
|
||||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual GameList getSupportedGames() const;
|
virtual GameList getSupportedGames() const;
|
||||||
virtual GameDescriptor findGame(const char *gameid) 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;
|
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
|
||||||
|
|
||||||
|
@ -1026,29 +1026,30 @@ static Common::String generatePreferredTarget(const DetectorResult &x) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList ScummMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
|
DetectedGames ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
|
||||||
GameList detectedGames;
|
DetectedGames detectedGames;
|
||||||
Common::List<DetectorResult> results;
|
Common::List<DetectorResult> results;
|
||||||
|
|
||||||
::detectGames(fslist, results, 0);
|
::detectGames(fslist, results, 0);
|
||||||
|
|
||||||
for (Common::List<DetectorResult>::iterator
|
for (Common::List<DetectorResult>::iterator
|
||||||
x = results.begin(); x != results.end(); ++x) {
|
x = results.begin(); x != results.end(); ++x) {
|
||||||
const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions);
|
const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions);
|
||||||
assert(g);
|
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.
|
// 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.
|
// Compute and set the preferred target name for this game.
|
||||||
// Based on generateComplexID() in advancedDetector.cpp.
|
// 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));
|
game.matchedGame.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi));
|
||||||
dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
|
||||||
|
|
||||||
detectedGames.push_back(dg);
|
detectedGames.push_back(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
return detectedGames;
|
return detectedGames;
|
||||||
|
|
|
@ -79,7 +79,7 @@ public:
|
||||||
virtual GameList getSupportedGames() const;
|
virtual GameList getSupportedGames() const;
|
||||||
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
|
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
|
||||||
virtual GameDescriptor findGame(const char *gameid) 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;
|
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
|
||||||
|
|
||||||
|
@ -141,8 +141,8 @@ GameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
|
||||||
return GameDescriptor();
|
return GameDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList SkyMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
|
DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
|
||||||
GameList detectedGames;
|
DetectedGames detectedGames;
|
||||||
bool hasSkyDsk = false;
|
bool hasSkyDsk = false;
|
||||||
bool hasSkyDnr = false;
|
bool hasSkyDnr = false;
|
||||||
int dinnerTableEntries = -1;
|
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.
|
// Match found, add to list of candidates, then abort inner loop.
|
||||||
// The game detector uses US English by default. We want British
|
// The game detector uses US English by default. We want British
|
||||||
// English to match the recorded voices better.
|
// 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;
|
const SkyVersion *sv = skyVersions;
|
||||||
while (sv->dinnerTableEntries) {
|
while (sv->dinnerTableEntries) {
|
||||||
if (dinnerTableEntries == sv->dinnerTableEntries &&
|
if (dinnerTableEntries == sv->dinnerTableEntries &&
|
||||||
(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
|
(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
|
||||||
dg.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
|
game.matchedGame.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
|
||||||
dg.setGUIOptions(sv->guioptions);
|
game.matchedGame.setGUIOptions(sv->guioptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++sv;
|
++sv;
|
||||||
}
|
}
|
||||||
detectedGames.push_back(dg);
|
detectedGames.push_back(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
return detectedGames;
|
return detectedGames;
|
||||||
|
|
|
@ -100,10 +100,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// for fall back detection
|
// 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
|
// reset fallback description
|
||||||
s_fallbackDesc.desc.gameId = "sludge";
|
s_fallbackDesc.desc.gameId = "sludge";
|
||||||
s_fallbackDesc.desc.extra = "";
|
s_fallbackDesc.desc.extra = "";
|
||||||
|
@ -147,9 +147,19 @@ const ADGameDescription *SludgeMetaEngine::fallbackDetect(const FileMap &allFile
|
||||||
s_fallbackFileNameBuffer[50] = '\0';
|
s_fallbackFileNameBuffer[50] = '\0';
|
||||||
s_fallbackDesc.desc.filesDescriptions[0].fileName = s_fallbackFileNameBuffer;
|
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)
|
#if PLUGIN_ENABLED_DYNAMIC(SLUDGE)
|
||||||
|
|
|
@ -89,7 +89,7 @@ public:
|
||||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual GameList getSupportedGames() const;
|
virtual GameList getSupportedGames() const;
|
||||||
virtual GameDescriptor findGame(const char *gameid) 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 SaveStateList listSaves(const char *target) const;
|
||||||
virtual int getMaximumSaveSlot() const;
|
virtual int getMaximumSaveSlot() const;
|
||||||
virtual void removeSaveState(const char *target, int slot) 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;
|
int i, j;
|
||||||
GameList detectedGames;
|
DetectedGames detectedGames;
|
||||||
bool filesFound[NUM_FILES_TO_CHECK];
|
bool filesFound[NUM_FILES_TO_CHECK];
|
||||||
for (i = 0; i < NUM_FILES_TO_CHECK; i++)
|
for (i = 0; i < NUM_FILES_TO_CHECK; i++)
|
||||||
filesFound[i] = false;
|
filesFound[i] = false;
|
||||||
|
@ -212,31 +212,31 @@ GameList SwordMetaEngine::detectGames(const Common::FSList &fslist, bool /*useUn
|
||||||
if (!filesFound[i] || psxFilesFound)
|
if (!filesFound[i] || psxFilesFound)
|
||||||
psxDemoFilesFound = false;
|
psxDemoFilesFound = false;
|
||||||
|
|
||||||
GameDescriptor gd;
|
DetectedGame game;
|
||||||
if (mainFilesFound && pcFilesFound && demoFilesFound)
|
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)
|
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)
|
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)
|
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)
|
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)
|
else if (mainFilesFound && macDemoFilesFound)
|
||||||
gd = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
|
game.matchedGame = GameDescriptor(sword1MacDemoSettings, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT));
|
||||||
else
|
else
|
||||||
return detectedGames;
|
return detectedGames;
|
||||||
|
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::PT_BRA));
|
||||||
gd.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
|
game.matchedGame.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::CZ_CZE));
|
||||||
|
|
||||||
detectedGames.push_back(gd);
|
detectedGames.push_back(game);
|
||||||
|
|
||||||
return detectedGames;
|
return detectedGames;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ public:
|
||||||
virtual GameList getSupportedGames() const;
|
virtual GameList getSupportedGames() const;
|
||||||
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
|
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
|
||||||
virtual GameDescriptor findGame(const char *gameid) 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 SaveStateList listSaves(const char *target) const;
|
||||||
virtual int getMaximumSaveSlot() const;
|
virtual int getMaximumSaveSlot() const;
|
||||||
virtual void removeSaveState(const char *target, int slot) 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) {
|
bool isFullGame(const Common::FSList &fslist) {
|
||||||
Common::FSList::const_iterator file;
|
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) {
|
for (file = fslist.begin(); file != fslist.end(); ++file) {
|
||||||
if (!file->isDirectory()) {
|
if (!file->isDirectory()) {
|
||||||
if (file->getName().equalsIgnoreCase("paris.clu"))
|
if (file->getName().equalsIgnoreCase("paris.clu"))
|
||||||
|
@ -159,8 +159,8 @@ bool isFullGame(const Common::FSList &fslist) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
|
DetectedGames detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
|
||||||
GameList detectedGames;
|
DetectedGames detectedGames;
|
||||||
const Sword2::GameSettings *g;
|
const Sword2::GameSettings *g;
|
||||||
Common::FSList::const_iterator file;
|
Common::FSList::const_iterator file;
|
||||||
bool isFullVersion = isFullGame(fslist);
|
bool isFullVersion = isFullGame(fslist);
|
||||||
|
@ -192,7 +192,10 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Match found, add to list of candidates, then abort inner loop.
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +211,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
|
||||||
if (file->getName().equalsIgnoreCase("clusters")) {
|
if (file->getName().equalsIgnoreCase("clusters")) {
|
||||||
Common::FSList recList;
|
Common::FSList recList;
|
||||||
if (file->getChildren(recList, Common::FSNode::kListAll)) {
|
if (file->getChildren(recList, Common::FSNode::kListAll)) {
|
||||||
GameList recGames(detectGamesImpl(recList, true));
|
DetectedGames recGames = detectGamesImpl(recList, true);
|
||||||
if (!recGames.empty()) {
|
if (!recGames.empty()) {
|
||||||
detectedGames.push_back(recGames);
|
detectedGames.push_back(recGames);
|
||||||
break;
|
break;
|
||||||
|
@ -223,7 +226,7 @@ GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) {
|
||||||
return detectedGames;
|
return detectedGames;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist, bool /*useUnknownGameDialog*/) const {
|
DetectedGames Sword2MetaEngine::detectGames(const Common::FSList &fslist) const {
|
||||||
return detectGamesImpl(fslist);
|
return detectGamesImpl(fslist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,10 +281,10 @@ Common::Error Sword2MetaEngine::createInstance(OSystem *syst, Engine **engine) c
|
||||||
|
|
||||||
// Invoke the detector
|
// Invoke the detector
|
||||||
Common::String gameid = ConfMan.get("gameid");
|
Common::String gameid = ConfMan.get("gameid");
|
||||||
GameList detectedGames = detectGames(fslist);
|
DetectedGames detectedGames = detectGames(fslist);
|
||||||
|
|
||||||
for (uint i = 0; i < detectedGames.size(); i++) {
|
for (uint i = 0; i < detectedGames.size(); i++) {
|
||||||
if (detectedGames[i].gameid() == gameid) {
|
if (detectedGames[i].matchedGame.gameid() == gameid) {
|
||||||
*engine = new Sword2::Sword2Engine(syst);
|
*engine = new Sword2::Sword2Engine(syst);
|
||||||
return Common::kNoError;
|
return Common::kNoError;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) 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;
|
||||||
|
|
||||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||||
virtual SaveStateList listSaves(const char *target) 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
|
* 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)
|
* 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;
|
Common::String extra;
|
||||||
FileMap allFiles;
|
FileMap allFiles;
|
||||||
SizeMD5Map filesSizeMD5;
|
SizeMD5Map filesSizeMD5;
|
||||||
|
@ -194,7 +194,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
|
||||||
const Tinsel::TinselGameDescription *g;
|
const Tinsel::TinselGameDescription *g;
|
||||||
|
|
||||||
if (fslist.empty())
|
if (fslist.empty())
|
||||||
return NULL;
|
return ADDetectedGame();
|
||||||
|
|
||||||
// TODO: The following code is essentially a slightly modified copy of the
|
// TODO: The following code is essentially a slightly modified copy of the
|
||||||
// complete code of function detectGame() in engines/advancedDetector.cpp.
|
// 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;
|
int maxFilesMatched = 0;
|
||||||
|
|
||||||
// MD5 based matching
|
// MD5 based matching
|
||||||
|
@ -310,22 +310,15 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
|
||||||
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
|
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
|
||||||
curFilesMatched++;
|
curFilesMatched++;
|
||||||
|
|
||||||
if (curFilesMatched > maxFilesMatched) {
|
if (curFilesMatched >= maxFilesMatched) {
|
||||||
maxFilesMatched = curFilesMatched;
|
maxFilesMatched = curFilesMatched;
|
||||||
|
|
||||||
matched.clear(); // Remove any prior, lower ranked matches.
|
matched = ADDetectedGame(&g->desc);
|
||||||
matched.push_back((const ADGameDescription *)g);
|
|
||||||
} else if (curFilesMatched == maxFilesMatched) {
|
|
||||||
matched.push_back((const ADGameDescription *)g);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We didn't find a match
|
return matched;
|
||||||
if (matched.empty())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return *matched.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
|
int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
|
||||||
|
|
|
@ -132,7 +132,7 @@ public:
|
||||||
_directoryGlobs = directoryGlobs;
|
_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);
|
return detectGameFilebased(allFiles, fslist, Toon::fileBasedFallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,15 +133,8 @@ public:
|
||||||
_directoryGlobs = directoryGlobs;
|
_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 {
|
||||||
ADFilePropertiesMap filesProps;
|
return detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback);
|
||||||
|
|
||||||
const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback, &filesProps);
|
|
||||||
if (!matchedDesc)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
reportUnknown(fslist.begin()->getParent(), filesProps);
|
|
||||||
return matchedDesc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const char *getName() const {
|
virtual const char *getName() const {
|
||||||
|
|
|
@ -149,18 +149,19 @@ public:
|
||||||
return desc != 0;
|
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) {
|
for (Common::FSList::const_iterator d = fslist.begin(); d != fslist.end(); ++d) {
|
||||||
Common::FSList audiofslist;
|
Common::FSList audiofslist;
|
||||||
if (d->isDirectory() && d->getName().equalsIgnoreCase("audio") && d->getChildren(audiofslist, Common::FSNode::kListFilesOnly)) {
|
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) {
|
for (Common::FSList::const_iterator f = audiofslist.begin(); f != audiofslist.end(); ++f) {
|
||||||
if (!f->isDirectory() && f->getName().equalsIgnoreCase("demorolc.raw")) {
|
if (!f->isDirectory() && f->getName().equalsIgnoreCase("demorolc.raw")) {
|
||||||
return &tuckerDemoGameDescription;
|
return ADDetectedGame(&tuckerDemoGameDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SaveStateList listSaves(const char *target) const {
|
virtual SaveStateList listSaves(const char *target) const {
|
||||||
|
|
|
@ -20,14 +20,16 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "engines/unknown-game-dialog.h"
|
||||||
|
|
||||||
#include "common/translation.h"
|
#include "common/translation.h"
|
||||||
#include "common/str-array.h"
|
#include "common/str-array.h"
|
||||||
#include "common/system.h"
|
#include "common/system.h"
|
||||||
|
|
||||||
#include "gui/gui-manager.h"
|
#include "gui/gui-manager.h"
|
||||||
#include "gui/message.h"
|
#include "gui/message.h"
|
||||||
#include "gui/ThemeEval.h"
|
#include "gui/ThemeEval.h"
|
||||||
#include "gui/widgets/popup.h"
|
#include "gui/widgets/popup.h"
|
||||||
#include "engines/unknown-game-dialog.h"
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
kCopyToClipboard = 'cpcl',
|
kCopyToClipboard = 'cpcl',
|
||||||
|
@ -35,36 +37,33 @@ enum {
|
||||||
kClose = 'clse'
|
kClose = 'clse'
|
||||||
};
|
};
|
||||||
|
|
||||||
UnknownGameDialog::UnknownGameDialog(const Common::String &reportData, const Common::String &reportTranslated, const Common::String &bugtrackerAffectedEngine)
|
UnknownGameDialog::UnknownGameDialog(const DetectionResults &detectionResults) :
|
||||||
: Dialog(30, 20, 260, 124) {
|
Dialog(30, 20, 260, 124),
|
||||||
|
_detectionResults(detectionResults) {
|
||||||
|
Common::String reportTranslated = _detectionResults.generateUnknownGameReport(true);
|
||||||
|
|
||||||
_reportData = reportData;
|
// Check if we have clipboard functionality and expand the reportTranslated message if needed...
|
||||||
_reportTranslated = reportTranslated;
|
|
||||||
_bugtrackerAffectedEngine = bugtrackerAffectedEngine;
|
|
||||||
|
|
||||||
//Check if we have clipboard functionality and expand the reportTranslated message if needed...
|
|
||||||
if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
|
if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
|
||||||
_reportTranslated += "\n";
|
reportTranslated += "\n";
|
||||||
_reportTranslated += _("Use the button below to copy the required game information into your clipboard.");
|
reportTranslated += _("Use the button below to copy the required game information into your clipboard.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#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)) {
|
if (g_system->hasFeature(OSystem::kFeatureOpenUrl)) {
|
||||||
_reportTranslated += "\n";
|
reportTranslated += "\n";
|
||||||
_reportTranslated += _("You can also directly report your game to the Bug Tracker!");
|
reportTranslated += _("You can also directly report your game to the Bug Tracker.");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int screenW = g_system->getOverlayWidth();
|
const int screenW = g_system->getOverlayWidth();
|
||||||
const int screenH = g_system->getOverlayHeight();
|
|
||||||
|
|
||||||
int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0);
|
int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0);
|
||||||
int buttonHeight = g_gui.xmlEval()->getVar("Globals.Button.Height", 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;
|
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;
|
int lineCount = lines.size() + 1;
|
||||||
|
|
||||||
_h = 3 * kLineHeight + lineCount * kLineHeight;
|
_h = 3 * kLineHeight + lineCount * kLineHeight;
|
||||||
|
@ -84,7 +83,7 @@ UnknownGameDialog::UnknownGameDialog(const Common::String &reportData, const Com
|
||||||
int buttonPos = _w - closeButtonWidth - 10;
|
int buttonPos = _w - closeButtonWidth - 10;
|
||||||
new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, buttonWidth, buttonHeight, _("Close"), 0, kClose);
|
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)) {
|
if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
|
||||||
buttonPos -= copyToClipboardButtonWidth + 5;
|
buttonPos -= copyToClipboardButtonWidth + 5;
|
||||||
new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, copyToClipboardButtonWidth, buttonHeight, _("Copy to clipboard"), 0, kCopyToClipboard);
|
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
|
// https://www.scummvm.org/unknowngame?engine=Foo&description=Bar) that would
|
||||||
// redirect to whatever our bugtracker system is.
|
// 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)) {
|
if (g_system->hasFeature(OSystem::kFeatureOpenUrl)) {
|
||||||
buttonPos -= openBugtrackerURLButtonWidth + 5;
|
buttonPos -= openBugtrackerURLButtonWidth + 5;
|
||||||
new GUI::ButtonWidget(this, buttonPos, _h - buttonHeight - 8, openBugtrackerURLButtonWidth, buttonHeight, _("Report game"), 0, kOpenBugtrackerURL);
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -126,27 +120,39 @@ void UnknownGameDialog::reflowLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::String UnknownGameDialog::generateBugtrackerURL() {
|
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?"
|
"https://bugs.scummvm.org/newticket?"
|
||||||
"summary=[UNK] Unknown game for engine %s:"
|
|
||||||
"&description=%s"
|
"&description=%s"
|
||||||
"&component=Engine%%3A%s"
|
|
||||||
"&type=enhancement"
|
"&type=enhancement"
|
||||||
"&keywords=unknown-game,%s"),
|
"&keywords=unknown-game",
|
||||||
_bugtrackerAffectedEngine.c_str(), _bugtrackerGameData.c_str(), _bugtrackerAffectedEngine.c_str(), _bugtrackerAffectedEngine.c_str());
|
report.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnknownGameDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
|
void UnknownGameDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
case kCopyToClipboard:
|
case kCopyToClipboard: {
|
||||||
g_system->setTextInClipboard(_reportData);
|
// TODO: Remove the filesystem path from the report
|
||||||
if (g_system->setTextInClipboard(_reportData)) {
|
Common::String report = _detectionResults.generateUnknownGameReport(false);
|
||||||
g_system->displayMessageOnOSD(_("All necessary information about your game has been copied into the clipboard"));
|
|
||||||
|
if (g_system->setTextInClipboard(report)) {
|
||||||
|
g_system->displayMessageOnOSD(
|
||||||
|
_("All necessary information about your game has been copied into the clipboard"));
|
||||||
} else {
|
} else {
|
||||||
g_system->displayMessageOnOSD(_("Copying the game information to the clipboard has failed!"));
|
g_system->displayMessageOnOSD(_("Copying the game information to the clipboard has failed!"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case kClose:
|
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();
|
close();
|
||||||
break;
|
break;
|
||||||
case kOpenBugtrackerURL:
|
case kOpenBugtrackerURL:
|
||||||
|
|
|
@ -22,16 +22,18 @@
|
||||||
|
|
||||||
#include "gui/dialog.h"
|
#include "gui/dialog.h"
|
||||||
|
|
||||||
|
#include "engines/metaengine.h"
|
||||||
|
|
||||||
class UnknownGameDialog : public GUI::Dialog {
|
class UnknownGameDialog : public GUI::Dialog {
|
||||||
public:
|
public:
|
||||||
UnknownGameDialog(const Common::String &reportData, const Common::String &reportTranslated, const Common::String &bugtrackerAffectedEngine);
|
UnknownGameDialog(const DetectionResults &detectionResults);
|
||||||
void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
|
|
||||||
virtual Common::String generateBugtrackerURL();
|
|
||||||
virtual void reflowLayout();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::String _reportData;
|
// Dialog API
|
||||||
Common::String _reportTranslated;
|
void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
|
||||||
Common::String _bugtrackerGameData;
|
void reflowLayout() override;
|
||||||
Common::String _bugtrackerAffectedEngine;
|
|
||||||
|
Common::String generateBugtrackerURL();
|
||||||
|
|
||||||
|
const DetectionResults &_detectionResults;
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,7 @@ public:
|
||||||
return "Copyright (C) 2011 Jan Nedoma";
|
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
|
// Set some defaults
|
||||||
s_fallbackDesc.extra = "";
|
s_fallbackDesc.extra = "";
|
||||||
s_fallbackDesc.language = Common::UNK_LANG;
|
s_fallbackDesc.language = Common::UNK_LANG;
|
||||||
|
@ -130,10 +130,12 @@ public:
|
||||||
s_fallbackDesc.extra = offset;
|
s_fallbackDesc.extra = offset;
|
||||||
s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE;
|
s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE;
|
||||||
}
|
}
|
||||||
return &s_fallbackDesc;
|
|
||||||
|
return ADDetectedGame(&s_fallbackDesc);
|
||||||
} // Fall through to return 0;
|
} // Fall through to return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return ADDetectedGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "gui/EventRecorder.h"
|
#include "gui/EventRecorder.h"
|
||||||
#endif
|
#endif
|
||||||
#include "gui/saveload.h"
|
#include "gui/saveload.h"
|
||||||
|
#include "engines/unknown-game-dialog.h"
|
||||||
#include "gui/widgets/edittext.h"
|
#include "gui/widgets/edittext.h"
|
||||||
#include "gui/widgets/list.h"
|
#include "gui/widgets/list.h"
|
||||||
#include "gui/widgets/tab.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
|
// ...so let's determine a list of candidates, games that
|
||||||
// could be contained in the specified directory.
|
// 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;
|
int idx;
|
||||||
if (candidates.empty()) {
|
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
|
// Display the candidates to the user and let her/him pick one
|
||||||
StringArray list;
|
StringArray list;
|
||||||
for (idx = 0; idx < (int)candidates.size(); idx++)
|
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:"));
|
ChooserDialog dialog(_("Pick the game:"));
|
||||||
dialog.setList(list);
|
dialog.setList(list);
|
||||||
idx = dialog.runModal();
|
idx = dialog.runModal();
|
||||||
}
|
}
|
||||||
if (0 <= idx && idx < (int)candidates.size()) {
|
if (0 <= idx && idx < (int)candidates.size()) {
|
||||||
GameDescriptor result = candidates[idx];
|
const GameDescriptor &result = candidates[idx].matchedGame;
|
||||||
|
|
||||||
// TODO: Change the detectors to set "path" !
|
|
||||||
result["path"] = dir.getPath();
|
|
||||||
|
|
||||||
Common::String domain = addGameToConf(result);
|
Common::String domain = addGameToConf(result);
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,12 @@ void MassAddDialog::handleTickle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the detector on the dir
|
// 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,
|
// Just add all detected games / game variants. If we get more than one,
|
||||||
// that either means the directory contains multiple games, or the detector
|
// 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.
|
// case, let the user choose which entries he wants to keep.
|
||||||
//
|
//
|
||||||
// However, we only add games which are not already in the config file.
|
// However, we only add games which are not already in the config file.
|
||||||
for (GameList::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
|
DetectedGames candidates = detectionResults.listRecognizedGames();
|
||||||
GameDescriptor result = *cand;
|
for (DetectedGames::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
|
||||||
|
const GameDescriptor &result = cand->matchedGame;
|
||||||
Common::String path = dir.getPath();
|
Common::String path = dir.getPath();
|
||||||
|
|
||||||
// Remove trailing slashes
|
// Remove trailing slashes
|
||||||
|
@ -224,7 +230,6 @@ void MassAddDialog::handleTickle() {
|
||||||
break; // Skip duplicates
|
break; // Skip duplicates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result["path"] = path;
|
|
||||||
_games.push_back(result);
|
_games.push_back(result);
|
||||||
|
|
||||||
_list->append(result.description());
|
_list->append(result.description());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue