From 6e5b70f5e9f8690b467ea8837e727e1048838788 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Tue, 12 Jun 2007 12:22:25 +0000 Subject: Patch #1733764: "Fallback detection patch". GSoC student. svn-id: r27375 --- common/advancedDetector.cpp | 136 +++++++++++++++++++++++----------- common/advancedDetector.h | 27 ++++++- engines/agi/detection.cpp | 146 ++++++++++++++++++++----------------- engines/agos/detection.cpp | 7 +- engines/cine/detection.cpp | 4 +- engines/cruise/detection.cpp | 3 +- engines/gob/detection.cpp | 4 +- engines/kyra/detection.cpp | 4 +- engines/parallaction/detection.cpp | 4 +- engines/saga/detection.cpp | 4 +- engines/touche/detection.cpp | 4 +- 11 files changed, 220 insertions(+), 123 deletions(-) diff --git a/common/advancedDetector.cpp b/common/advancedDetector.cpp index f0f77ee26e..7d7cadbade 100644 --- a/common/advancedDetector.cpp +++ b/common/advancedDetector.cpp @@ -72,6 +72,32 @@ GameList gameIDList(const Common::ADParams ¶ms) { return GameList(params.list); } +static void upgradeTargetIfNecessary(const Common::ADParams ¶ms) { + if (params.obsoleteList == 0) + return; + + const char *gameid = ConfMan.get("gameid").c_str(); + + for (const Common::ADObsoleteGameID *o = params.obsoleteList; o->from; ++o) { + if (!scumm_stricmp(gameid, o->from)) { + gameid = o->to; + ConfMan.set("gameid", o->to); + + if (o->platform != Common::kPlatformUnknown) + ConfMan.set("platform", Common::getPlatformCode(o->platform)); + + warning("Target upgraded from %s to %s", o->from, o->to); + + if (ConfMan.hasKey("id_came_from_command_line")) { + warning("Target came from command line. Skipping save"); + } else { + ConfMan.flushToDisk(); + } + break; + } + } +} + GameDescriptor findGameID( const char *gameid, const Common::ADParams ¶ms @@ -110,6 +136,24 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa return gd; } +// Almost identical to the toGameDescriptor function that takes a ADGameDescription and PlainGameDescriptor. +// Just a little fine tuning about accessing variables. +// Used because of fallback detection and the dynamic string content it needs. +static GameDescriptor toGameDescriptor(const EncapsulatedADGameDesc &g, const PlainGameDescriptor *sg) { + const char *title = 0; + + while (sg->gameid) { + if (!scumm_stricmp(g.getGameID(), sg->gameid)) + title = sg->description; + sg++; + } + + assert(g.realDesc); + GameDescriptor gd(g.getGameID(), title, g.realDesc->language, g.realDesc->platform); + gd.updateDesc(g.getExtra()); + return gd; +} + /** * Generate a preferred target value as * GAMEID-PLAFORM-LANG @@ -134,38 +178,49 @@ static String generatePreferredTarget(const String &id, const ADGameDescription return res; } +static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc, const Common::ADParams ¶ms) { + if (params.singleid != NULL) { + desc["preferredtarget"] = desc["gameid"]; + desc["gameid"] = params.singleid; + } + + if (params.flags & kADFlagAugmentPreferredTarget) { + if (!desc.contains("preferredtarget")) + desc["preferredtarget"] = desc["gameid"]; + + desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc); + } +} + GameList detectAllGames( const FSList &fslist, const Common::ADParams ¶ms ) { ADGameDescList matches = detectGame(&fslist, params, Common::UNK_LANG, Common::kPlatformUnknown); - GameList detectedGames; - for (uint i = 0; i < matches.size(); i++) { - GameDescriptor desc(toGameDescriptor(*matches[i], params.list)); - - if (params.singleid != NULL) { - desc["preferredtarget"] = desc["gameid"]; - desc["gameid"] = params.singleid; - } - if (params.flags & kADFlagAugmentPreferredTarget) { - if (!desc.contains("preferredtarget")) - desc["preferredtarget"] = desc["gameid"]; - - desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], matches[i]); + // Use fallback detector if there were no matches by other means + if (matches.empty() && params.fallbackDetectFunc != NULL) { + EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(&fslist); + if (fallbackDesc.realDesc != 0) { + GameDescriptor desc(toGameDescriptor(fallbackDesc, params.list)); + updateGameDescriptor(desc, fallbackDesc.realDesc, params); + detectedGames.push_back(desc); } - + } else for (uint i = 0; i < matches.size(); i++) { // Otherwise use the found matches + GameDescriptor desc(toGameDescriptor(*matches[i], params.list)); + updateGameDescriptor(desc, matches[i], params); detectedGames.push_back(desc); } return detectedGames; } -const ADGameDescription *detectBestMatchingGame( +EncapsulatedADGameDesc detectBestMatchingGame( const Common::ADParams ¶ms ) { const ADGameDescription *agdDesc = 0; + EncapsulatedADGameDesc result; Common::Language language = Common::UNK_LANG; Common::Platform platform = Common::kPlatformUnknown; @@ -189,39 +244,29 @@ const ADGameDescription *detectBestMatchingGame( agdDesc = matches[0]; } - if (agdDesc != 0) { - debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str()); + if (agdDesc != 0) { // Check if we found a match without fallback detection + result = EncapsulatedADGameDesc(agdDesc); + } else if (params.fallbackDetectFunc != NULL) { // Use fallback detector if there were no matches by other means + EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(NULL); + if (fallbackDesc.realDesc != 0 && (params.singleid != NULL || fallbackDesc.getGameID() == gameid)) { + result = fallbackDesc; // Found a fallback match + } + } + + if (result.realDesc != 0) { + debug(2, "Running %s", toGameDescriptor(result, params.list).description().c_str()); } - return agdDesc; + return result; } PluginError detectGameForEngineCreation( const Common::ADParams ¶ms ) { - Common::String gameid = ConfMan.get("gameid"); + upgradeTargetIfNecessary(params); - if (params.obsoleteList != 0) { - for (const Common::ADObsoleteGameID *o = params.obsoleteList; o->from; ++o) { - if (!scumm_stricmp(gameid.c_str(), o->from)) { - gameid = o->to; - ConfMan.set("gameid", o->to); - - if (o->platform != Common::kPlatformUnknown) - ConfMan.set("platform", Common::getPlatformCode(o->platform)); - - warning("Target upgraded from %s to %s", o->from, o->to); - - if (ConfMan.hasKey("id_came_from_command_line")) { - warning("Target came from command line. Skipping save"); - } else { - ConfMan.flushToDisk(); - } - break; - } - } - } + Common::String gameid = ConfMan.get("gameid"); FSList fslist; FilesystemNode dir(ConfMan.get("path")); @@ -241,6 +286,14 @@ PluginError detectGameForEngineCreation( } } + // Use fallback detector if there were no matches by other means + if (params.fallbackDetectFunc != NULL) { + EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(&fslist); + if (fallbackDesc.realDesc != 0 && (params.singleid != NULL || fallbackDesc.getGameID() == gameid)) { + return kNoError; + } + } + return kNoGameDataFoundError; } @@ -491,11 +544,6 @@ static ADGameDescList detectGame(const FSList *fslist, const Common::ADParams &p } } - // If we still haven't got a match, try to use the fallback callback :-) - if (matched.empty() && params.fallbackDetectFunc != 0) { - matched = (*params.fallbackDetectFunc)(fslist); - } - return matched; } diff --git a/common/advancedDetector.h b/common/advancedDetector.h index fab847e671..2031f001a9 100644 --- a/common/advancedDetector.h +++ b/common/advancedDetector.h @@ -63,6 +63,29 @@ struct ADGameDescription { uint32 flags; }; +/** + * Encapsulates ADGameDescription and makes gameid and extra strings dynamic. + * Used in fallback detection when dynamically creating string content. + */ +struct EncapsulatedADGameDesc { + Common::String gameid; + Common::String extra; + const ADGameDescription *realDesc; + + // Constructor for the EncapsulatedADGameDesc + EncapsulatedADGameDesc() : realDesc(0) {} + EncapsulatedADGameDesc(const ADGameDescription *paramRealDesc, + Common::String paramGameID = Common::String(""), + Common::String paramExtra = Common::String("")) + : realDesc(paramRealDesc), gameid(paramGameID), extra(paramExtra) { + assert(paramRealDesc != NULL); + } + + // Functions for getting the correct gameid and extra values from the struct + const char *getGameID() const { return (gameid.empty() && realDesc != 0) ? realDesc->gameid : gameid.c_str(); } + const char *getExtra() const { return (extra.empty() && realDesc != 0) ? realDesc->extra : extra.c_str(); } +}; + /** * A list of pointers to ADGameDescription structs (or subclasses thereof). */ @@ -177,7 +200,7 @@ struct ADParams { * * @todo */ - ADGameDescList (*fallbackDetectFunc)(const FSList *fslist); + EncapsulatedADGameDesc (*fallbackDetectFunc)(const FSList *fslist); /** * A bitmask of flags which can be used to configure the behavior @@ -207,7 +230,7 @@ GameDescriptor findGameID(const char *gameid, const Common::ADParams ¶ms); GameList detectAllGames(const FSList &fslist, const Common::ADParams ¶ms); // FIXME/TODO: Rename this function to something more sensible. -const ADGameDescription *detectBestMatchingGame(const Common::ADParams ¶ms); +EncapsulatedADGameDesc detectBestMatchingGame(const Common::ADParams ¶ms); // FIXME/TODO: Rename this function to something more sensible. // Only used by ADVANCED_DETECTOR_DEFINE_PLUGIN_WITH_FUNC diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index ff59084ffe..6e7e8df30c 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -1825,82 +1825,72 @@ static const AGIGameDescription gameDescriptions[] = { { AD_TABLE_END_MARKER, 0, 0, 0, 0 } }; -static const AGIGameDescription fallbackDescs[] = { - { - { - "agi-fanmade", - "Unknown v2 Game", - AD_ENTRY1(0, 0), - Common::UNK_LANG, - Common::kPlatformPC, - Common::ADGF_NO_FLAGS - }, - GID_FANMADE, - GType_V2, - GF_FANMADE, - 0x2917, - }, - { - { - "agi-fanmade", - "Unknown v2 AGIPAL Game", - AD_ENTRY1(0, 0), - Common::UNK_LANG, - Common::kPlatformPC, - Common::ADGF_NO_FLAGS - }, - GID_FANMADE, - GType_V2, - GF_FANMADE | GF_AGIPAL, - 0x2917, - }, - { - { - "agi-fanmade", - "Unknown v3 Game", - AD_ENTRY1(0, 0), - Common::UNK_LANG, - Common::kPlatformPC, - Common::ADGF_NO_FLAGS - }, - GID_FANMADE, - GType_V3, - GF_FANMADE, - 0x3149, - }, +/** + * The fallback game descriptor used by the AGI engine's fallbackDetector. + * Contents of this struct are to be overwritten by the fallbackDetector. + */ +static AGIGameDescription g_fallbackDesc = { + { + "", // Not used by the fallback descriptor, it uses the EncapsulatedADGameDesc's gameid + "", // Not used by the fallback descriptor, it uses the EncapsulatedADGameDesc's extra + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + GID_FANMADE, + GType_V2, + GF_FANMADE, + 0x2917, }; -Common::ADGameDescList fallbackDetector(const FSList *fslist) { - Common::String tstr; +Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) { typedef Common::HashMap IntMap; IntMap allFiles; - Common::ADGameDescList matched; - int matchedNum = -1; + bool matchedUsingFilenames = false; + Common::String gameid("agi-fanmade"), description, extra; // Set the defaults for gameid, description and extra + FSList fslistCurrentDir; // Only used if fslist == NULL + + // Use the current directory for searching if fslist == NULL + if (fslist == NULL) { + FilesystemNode fsCurrentDir("."); + fslistCurrentDir.push_back(fsCurrentDir); + fslist = &fslistCurrentDir; + } - // TODO: - // WinAGI produces *.wag file with interpreter version, game name - // and other parameters. Add support for this once specs are known + // Set the default values for the fallback descriptor's ADGameDescription part. + g_fallbackDesc.desc.language = Common::UNK_LANG; + g_fallbackDesc.desc.platform = Common::kPlatformPC; + g_fallbackDesc.desc.flags = Common::ADGF_NO_FLAGS; + // Set default values for the fallback descriptor's AGIGameDescription part. + g_fallbackDesc.gameID = GID_FANMADE; + g_fallbackDesc.features = GF_FANMADE; + g_fallbackDesc.version = 0x2917; // First grab all filenames for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) { if (file->isDirectory()) continue; - tstr = file->name(); - tstr.toLowercase(); - - allFiles[tstr] = true; + Common::String filename = file->name(); + filename.toLowercase(); + allFiles[filename] = true; // Save the filename in a hash table } - // Now check for v2 if (allFiles.contains("logdir") && allFiles.contains("object") && allFiles.contains("picdir") && allFiles.contains("snddir") && allFiles.contains("viewdir") && allFiles.contains("vol.0") && - allFiles.contains("words.tok")) { - matchedNum = 0; - - // Check if it is AGIPAL - if (allFiles.contains("pal.101")) - matchedNum = 1; + allFiles.contains("words.tok")) { // Check for v2 + + // The default AGI interpreter version 0x2917 is okay for v2 games + // so we don't have to change it here. + matchedUsingFilenames = true; + + if (allFiles.contains("pal.101")) { // Check if it is AGIPAL + description = "Unknown v2 AGIPAL Game"; + g_fallbackDesc.features |= GF_AGIPAL; // Add AGIPAL feature flag + } else { // Not AGIPAL so just plain v2 + description = "Unknown v2 Game"; + } } else { // Try v3 char name[8]; @@ -1911,26 +1901,44 @@ Common::ADGameDescList fallbackDetector(const FSList *fslist) { if (allFiles.contains("object") && allFiles.contains("words.tok") && allFiles.contains(Common::String(name) + "dir")) { - matchedNum = 2; + matchedUsingFilenames = true; + description = "Unknown v3 Game"; + g_fallbackDesc.version = 0x3149; // Set the default AGI version for an AGI v3 game break; } } } } - if (matchedNum != -1) { - matched.push_back(&fallbackDescs[matchedNum].desc); + // Check that the AGI interpreter version is a supported one + if (!(g_fallbackDesc.version >= 0x2000 && g_fallbackDesc.version < 0x4000)) { + warning("Unsupported AGI interpreter version 0x%x in AGI's fallback detection. Using default 0x2917", g_fallbackDesc.version); + g_fallbackDesc.version = 0x2917; + } + + // Set game type (v2 or v3) according to the AGI interpreter version number + if (g_fallbackDesc.version >= 0x2000 && g_fallbackDesc.version < 0x3000) + g_fallbackDesc.gameType = GType_V2; + else if (g_fallbackDesc.version >= 0x3000 && g_fallbackDesc.version < 0x4000) + g_fallbackDesc.gameType = GType_V3; + + // Check if we found a match with any of the fallback methods + Common::EncapsulatedADGameDesc result; + if (matchedUsingFilenames) { + extra = description + " " + extra; // Let's combine the description and extra + result = Common::EncapsulatedADGameDesc((const Common::ADGameDescription *)&g_fallbackDesc, gameid, extra); printf("Your game version has been detected using fallback matching as a\n"); - printf("variant of %s (%s).\n", fallbackDescs[matchedNum].desc.gameid, fallbackDescs[matchedNum].desc.extra); + printf("variant of %s (%s).\n", result.getGameID(), result.getExtra()); printf("If this is an original and unmodified version or new made Fanmade game,\n"); printf("please report any, information previously printed by ScummVM to the team.\n"); + } - return matched; + return result; } -} +} // End of namespace Agi static const Common::ADParams detectionParams = { // Pointer to ADGameDescription or its superset structure @@ -1960,7 +1968,9 @@ REGISTER_PLUGIN(AGI, "AGI v2 + v3 Engine", "Sierra AGI Engine (C) Sierra On-Line namespace Agi { bool AgiEngine::initGame() { - _gameDescription = (const AGIGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const AGIGameDescription *)(encapsulatedDesc.realDesc); + return (_gameDescription != 0); } diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp index ab5bb4a2de..138be5b1d2 100644 --- a/engines/agos/detection.cpp +++ b/engines/agos/detection.cpp @@ -117,7 +117,8 @@ PluginError Engine_AGOS_create(OSystem *syst, Engine **engine) { assert(engine); const char *gameid = ConfMan.get("gameid").c_str(); - //const AGOSGameDescription gd = (const AGOSGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + //Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + //const AGOSGameDescription *gd = (const AGOSGameDescription *)(encapsulatedDesc.realDesc); //if (gd == 0) { // return kNoGameDataFoundError; //} @@ -154,7 +155,9 @@ REGISTER_PLUGIN(AGOS, "AGOS", "AGOS (C) Adventure Soft"); namespace AGOS { bool AGOSEngine::initGame() { - _gameDescription = (const AGOSGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const AGOSGameDescription *)(encapsulatedDesc.realDesc); + return (_gameDescription != 0); } diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index f24fe488d1..690644a0fa 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -494,7 +494,9 @@ REGISTER_PLUGIN(CINE, "Cinematique evo 1 engine", "Future Wars & Operation Steal namespace Cine { bool CineEngine::initGame() { - _gameDescription = (const CINEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const CINEGameDescription *)(encapsulatedDesc.realDesc); + return (_gameDescription != 0); } diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index 7962f7a3f1..2ca83f4046 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -118,7 +118,8 @@ REGISTER_PLUGIN(CRUISE, "Cinematique evo 2 engine", "Cruise for a Corpse (C) Del namespace Cruise { bool CruiseEngine::initGame() { - _gameDescription = (const CRUISEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const CRUISEGameDescription *)(encapsulatedDesc.realDesc); return (_gameDescription != 0); } diff --git a/engines/gob/detection.cpp b/engines/gob/detection.cpp index 6b37d05e98..9dfbf534d4 100644 --- a/engines/gob/detection.cpp +++ b/engines/gob/detection.cpp @@ -1150,7 +1150,9 @@ REGISTER_PLUGIN(GOB, "Gob Engine", "Goblins Games (C) Coktel Vision"); namespace Gob { bool GobEngine::detectGame() { - const GOBGameDescription *gd = (const GOBGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + const GOBGameDescription *gd = (const GOBGameDescription *)(encapsulatedDesc.realDesc); + if (gd == 0) return false; diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 7e02271920..00d6cae81b 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -133,7 +133,9 @@ PluginError Engine_KYRA_create(OSystem *syst, Engine **engine) { assert(engine); const char *gameid = ConfMan.get("gameid").c_str(); - const KYRAGameDescription *gd = (const KYRAGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + const KYRAGameDescription *gd = (const KYRAGameDescription *)(encapsulatedDesc.realDesc); + if (gd == 0) { // maybe add non md5 based detection again? return kNoGameDataFoundError; diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp index 63ab893641..7620c8b5a8 100644 --- a/engines/parallaction/detection.cpp +++ b/engines/parallaction/detection.cpp @@ -131,7 +131,9 @@ REGISTER_PLUGIN(PARALLACTION, "Parallaction engine", "Nippon Safes Inc. (C) Dyna namespace Parallaction { bool Parallaction::detectGame() { - _gameDescription = (const PARALLACTIONGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const PARALLACTIONGameDescription *)(encapsulatedDesc.realDesc); + return (_gameDescription != 0); } diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index 6b73c6c91e..2096ca9af1 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -124,7 +124,9 @@ REGISTER_PLUGIN(SAGA, "SAGA Engine", "Inherit the Earth (C) Wyrmkeep Entertainme namespace Saga { bool SagaEngine::initGame() { - _gameDescription = (const SAGAGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + _gameDescription = (const SAGAGameDescription *)(encapsulatedDesc.realDesc); + if (_gameDescription == 0) return false; diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index e88da4dab4..8e8de71e9c 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -124,7 +124,9 @@ REGISTER_PLUGIN(TOUCHE, "Touche Engine", "Touche: The Adventures of the 5th Musk namespace Touche { bool ToucheEngine::detectGame() { - const Common::ADGameDescription *gd = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams); + const Common::ADGameDescription *gd = encapsulatedDesc.realDesc; + if (gd == 0) return false; -- cgit v1.2.3