Ticket #8690: fallback-alternate2.patch

File fallback-alternate2.patch, 20.1 KB (added by fingolfin, 13 years ago)
  • common/advancedDetector.h

     
    6464};
    6565
    6666/**
     67 * Encapsulates ADGameDescription and makes gameid and extra strings dynamic.
     68 * Used in fallback detection when dynamically creating string content.
     69 */
     70struct EncapsulatedADGameDesc {
     71        Common::String gameid;
     72        Common::String extra;
     73        const ADGameDescription *realDesc;
     74
     75        // Constructor for the EncapsulatedADGameDesc
     76        EncapsulatedADGameDesc() : realDesc(0) {}
     77        EncapsulatedADGameDesc(const ADGameDescription *paramRealDesc,
     78                Common::String paramGameID = Common::String(""),
     79                Common::String paramExtra = Common::String(""))
     80                : realDesc(paramRealDesc), gameid(paramGameID), extra(paramExtra) {
     81                assert(paramRealDesc != NULL);
     82        }
     83
     84        // Functions for getting the correct gameid and extra values from the struct
     85        const char *getGameID() const { return (gameid.empty() && !realDesc) ? realDesc->gameid : gameid.c_str(); }
     86        const char *getExtra() const { return (extra.empty() && !realDesc) ? realDesc->extra : extra.c_str(); }
     87};
     88
     89/**
    6790 * A list of pointers to ADGameDescription structs (or subclasses thereof).
    6891 */
    6992typedef Array<const ADGameDescription*> ADGameDescList;
     
    175198         * @note The fslist parameter may be 0 -- in that case, it is assumed
    176199         *       that the callback searchs the current directory.
    177200         *
     201         * @note You need to handle deleting the returned pointer if it's not NULL.
     202         *       You don't need to delete the memory pointed to by the realDesc
     203         *       pointer inside the struct though because it should be statically
     204         *       allocated.
     205         *
     206         * @return A pointer to a single EncapsulatedADGameDesc if there was a
     207         *         match, otherwise NULL.
     208         *
    178209         * @todo
    179210         */
    180         ADGameDescList (*fallbackDetectFunc)(const FSList *fslist);
     211        EncapsulatedADGameDesc (*fallbackDetectFunc)(const FSList *fslist);
    181212
    182213        /**
    183214         * A bitmask of flags which can be used to configure the behavior
     
    206237// FIXME/TODO: Rename this function to something more sensible.
    207238GameList detectAllGames(const FSList &fslist, const Common::ADParams &params);
    208239
    209 // FIXME/TODO: Rename this function to something more sensible.
    210 const ADGameDescription *detectBestMatchingGame(const Common::ADParams &params);
     240/**
     241 * Detect the best matching game.
     242 *
     243 * FIXME/TODO: Rename this function to something more sensible.
     244 *
     245 * @note You need to handle deleting the returned pointer if it's not NULL.
     246 *       You don't need to delete the memory pointed to by the realDesc
     247 *       pointer inside the struct though because it should be statically
     248 *       allocated.
     249 */
     250EncapsulatedADGameDesc detectBestMatchingGame(const Common::ADParams &params);
    211251
    212252// FIXME/TODO: Rename this function to something more sensible.
    213253// Only used by ADVANCED_DETECTOR_DEFINE_PLUGIN_WITH_FUNC
  • common/advancedDetector.cpp

     
    110110        return gd;
    111111}
    112112
     113// Almost identical to the toGameDescriptor function that takes a ADGameDescription and PlainGameDescriptor.
     114// Just a little fine tuning about accessing variables.
     115// Used because of fallback detection and the dynamic string content it needs.
     116static GameDescriptor toGameDescriptor(const EncapsulatedADGameDesc &g, const PlainGameDescriptor *sg) {
     117        const char *title = 0;
     118
     119        while (sg->gameid) {
     120                if (!scumm_stricmp(g.getGameID(), sg->gameid))
     121                        title = sg->description;
     122                sg++;
     123        }
     124
     125        assert(g.realDesc);
     126        GameDescriptor gd(g.getGameID(), title, g.realDesc->language, g.realDesc->platform);
     127        gd.updateDesc(g.getExtra());
     128        return gd;
     129}
     130
    113131/**
    114132 * Generate a preferred target value as
    115133 *   GAMEID-PLAFORM-LANG
     
    134152        return res;
    135153}
    136154
     155static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc, const Common::ADParams &params) {
     156        if (params.singleid != NULL) {
     157                desc["preferredtarget"] = desc["gameid"];
     158                desc["gameid"] = params.singleid;
     159        }
     160
     161        if (params.flags & kADFlagAugmentPreferredTarget) {
     162                if (!desc.contains("preferredtarget"))
     163                        desc["preferredtarget"] = desc["gameid"];
     164
     165                desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc);
     166        }
     167}
     168
    137169GameList detectAllGames(
    138170        const FSList &fslist,
    139171        const Common::ADParams &params
    140172        ) {
    141173        ADGameDescList matches = detectGame(&fslist, params, Common::UNK_LANG, Common::kPlatformUnknown);
    142 
    143174        GameList detectedGames;
    144         for (uint i = 0; i < matches.size(); i++) {
    145                 GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
    146175
    147                 if (params.singleid != NULL) {
    148                         desc["preferredtarget"] = desc["gameid"];
    149                         desc["gameid"] = params.singleid;
     176        // Use fallback detector if there were no matches by other means
     177        if (matches.empty() && params.fallbackDetectFunc != NULL) {
     178                EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(&fslist);
     179                if (fallbackDesc.realDesc != 0) {
     180                        GameDescriptor desc(toGameDescriptor(fallbackDesc, params.list));
     181                        updateGameDescriptor(desc, fallbackDesc.realDesc, params);
     182                        detectedGames.push_back(desc);
    150183                }
    151 
    152                 if (params.flags & kADFlagAugmentPreferredTarget) {
    153                         if (!desc.contains("preferredtarget"))
    154                                 desc["preferredtarget"] = desc["gameid"];
    155 
    156                         desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], matches[i]);
    157                 }
    158 
     184        } else for (uint i = 0; i < matches.size(); i++) { // Otherwise use the found matches
     185                GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
     186                updateGameDescriptor(desc, matches[i], params);
    159187                detectedGames.push_back(desc);
    160188        }
    161189
    162190        return detectedGames;
    163191}
    164192
    165 const ADGameDescription *detectBestMatchingGame(
     193EncapsulatedADGameDesc detectBestMatchingGame(
    166194        const Common::ADParams &params
    167195        ) {
    168196        const ADGameDescription *agdDesc = 0;
     197        EncapsulatedADGameDesc result;
    169198        Common::Language language = Common::UNK_LANG;
    170199        Common::Platform platform = Common::kPlatformUnknown;
    171200
     
    189218                agdDesc = matches[0];
    190219        }
    191220
     221        // Use fallback detector if there were no matches by other means
     222        if (matches.empty() && params.fallbackDetectFunc != NULL) {
     223                EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(NULL);
     224                if (fallbackDesc.realDesc != 0 && (params.singleid != NULL || fallbackDesc.getGameID() == gameid)) {
     225                        result = fallbackDesc; // Found a fallback match
     226                }
     227        }
     228
     229        // If we found a match without fallback detection
     230        // then let's convert it to this function's return format
    192231        if (agdDesc != 0) {
    193                 debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str());
     232                assert(result.realDesc == 0); // Should be NULL at the moment
     233                // FIXME: However, could be non-null if there are matches, but none has the correct gameid,
     234                // *and* the fallback detector detected something.
     235                result = EncapsulatedADGameDesc(agdDesc);
    194236        }
    195237
    196         return agdDesc;
     238        if (result.realDesc != 0) {
     239                debug(2, "Running %s", toGameDescriptor(result, params.list).description().c_str());
     240        }
     241
     242        return result;
    197243}
    198244
    199245PluginError detectGameForEngineCreation(
     
    236282                }
    237283        }
    238284
     285        // Use fallback detector if there were no matches by other means
     286        if (matches.empty() && params.fallbackDetectFunc != NULL) {
     287                EncapsulatedADGameDesc fallbackDesc = (*params.fallbackDetectFunc)(&fslist);
     288                if (fallbackDesc.realDesc != 0 && (params.singleid != NULL || fallbackDesc.getGameID() == gameid)) {
     289                        return kNoError;
     290                }
     291        }
     292
    239293        return kNoGameDataFoundError;
    240294}
    241295
     
    486540                }
    487541        }
    488542
    489         // If we still haven't got a match, try to use the fallback callback :-)
    490         if (matched.empty() && params.fallbackDetectFunc != 0) {
    491                 matched = (*params.fallbackDetectFunc)(fslist);
    492         }
    493 
    494543        return matched;
    495544}
    496545
  • engines/touche/detection.cpp

     
    124124namespace Touche {
    125125
    126126bool ToucheEngine::detectGame() {
    127         const Common::ADGameDescription *gd = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     127        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     128        const Common::ADGameDescription *gd = encapsulatedDesc.realDesc;
     129
    128130        if (gd == 0)
    129131                return false;
    130132
  • engines/agos/detection.cpp

     
    117117        assert(engine);
    118118        const char *gameid = ConfMan.get("gameid").c_str();
    119119       
    120         //const AGOSGameDescription gd = (const AGOSGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     120        //Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     121        //const AGOSGameDescription *gd = (const AGOSGameDescription *)(encapsulatedDesc.realDesc);
    121122        //if (gd == 0) {
    122123        //      return kNoGameDataFoundError;
    123124        //}
     
    154155namespace AGOS {
    155156
    156157bool AGOSEngine::initGame() {
    157         _gameDescription = (const AGOSGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     158        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     159        _gameDescription = (const AGOSGameDescription *)(encapsulatedDesc.realDesc);
     160
    158161        return (_gameDescription != 0);
    159162}
    160163
  • engines/cruise/detection.cpp

     
    118118namespace Cruise {
    119119
    120120bool CruiseEngine::initGame() {
    121         _gameDescription = (const CRUISEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     121        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     122        _gameDescription = (const CRUISEGameDescription *)(encapsulatedDesc.realDesc);
    122123
    123124        return (_gameDescription != 0);
    124125}
  • engines/agi/detection.cpp

     
    18251825        { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
    18261826};
    18271827
    1828 static const AGIGameDescription fallbackDescs[] = {
     1828/**
     1829 * The fallback game descriptor used by the AGI engine's fallbackDetector.
     1830 * Contents of this struct are to be overwritten by the fallbackDetector.
     1831 */
     1832static AGIGameDescription g_fallbackDesc = {
    18291833        {
    1830                 {
    1831                         "agi-fanmade",
    1832                         "Unknown v2 Game",
    1833                         AD_ENTRY1(0, 0),
    1834                         Common::UNK_LANG,
    1835                         Common::kPlatformPC,
    1836                         Common::ADGF_NO_FLAGS
    1837                 },
    1838                 GID_FANMADE,
    1839                 GType_V2,
    1840                 GF_FANMADE,
    1841                 0x2917,
     1834                "", // Not used by the fallback descriptor, it uses the EncapsulatedADGameDesc's gameid
     1835                "", // Not used by the fallback descriptor, it uses the EncapsulatedADGameDesc's extra
     1836                AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
     1837                Common::UNK_LANG,
     1838                Common::kPlatformPC,
     1839                Common::ADGF_NO_FLAGS
    18421840        },
    1843         {
    1844                 {
    1845                         "agi-fanmade",
    1846                         "Unknown v2 AGIPAL Game",
    1847                         AD_ENTRY1(0, 0),
    1848                         Common::UNK_LANG,
    1849                         Common::kPlatformPC,
    1850                         Common::ADGF_NO_FLAGS
    1851                 },
    1852                 GID_FANMADE,
    1853                 GType_V2,
    1854                 GF_FANMADE | GF_AGIPAL,
    1855                 0x2917,
    1856         },
    1857         {
    1858                 {
    1859                         "agi-fanmade",
    1860                         "Unknown v3 Game",
    1861                         AD_ENTRY1(0, 0),
    1862                         Common::UNK_LANG,
    1863                         Common::kPlatformPC,
    1864                         Common::ADGF_NO_FLAGS
    1865                 },
    1866                 GID_FANMADE,
    1867                 GType_V3,
    1868                 GF_FANMADE,
    1869                 0x3149,
    1870         },
     1841        GID_FANMADE,
     1842        GType_V2,
     1843        GF_FANMADE,
     1844        0x2917,
    18711845};
    18721846
    1873 Common::ADGameDescList fallbackDetector(const FSList *fslist) {
    1874         Common::String tstr;
     1847Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) {
    18751848        typedef Common::HashMap<Common::String, int32, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> IntMap;
    18761849        IntMap allFiles;
    1877         Common::ADGameDescList matched;
    1878         int matchedNum = -1;
     1850        bool matchedUsingFilenames = false;
     1851        Common::String gameid("agi-fanmade"), description, extra; // Set the defaults for gameid, description and extra
     1852        FSList fslistCurrentDir; // Only used if fslist == NULL
    18791853
    1880         // TODO:
    1881         // WinAGI produces *.wag file with interpreter version, game name
    1882         // and other parameters. Add support for this once specs are known
     1854        // Use the current directory for searching if fslist == NULL
     1855        if (fslist == NULL) {
     1856                FilesystemNode fsCurrentDir(".");
     1857                fslistCurrentDir.push_back(fsCurrentDir);
     1858                fslist = &fslistCurrentDir;
     1859        }
    18831860
     1861        // Set the default values for the fallback descriptor's ADGameDescription part.
     1862        g_fallbackDesc.desc.language = Common::UNK_LANG;
     1863        g_fallbackDesc.desc.platform = Common::kPlatformPC;
     1864        g_fallbackDesc.desc.flags = Common::ADGF_NO_FLAGS;
     1865        // g_fallbackDesc.desc.gameid can't be modified so let it be
     1866        // g_fallbackDesc.desc.extra can't be modified so let it be
     1867        // g_fallbackDesc.desc.filesDescriptions shouldn't be modified here, it should always be AD_ENTRY1(0, 0)
    18841868
     1869        // Set default values for the fallback descriptor's AGIGameDescription part.
     1870        g_fallbackDesc.gameID = GID_FANMADE;
     1871        g_fallbackDesc.features = GF_FANMADE;
     1872        g_fallbackDesc.version = 0x2917;
     1873        // g_fallbackDesc.gameType is set later according to the AGI interpreter version number
     1874
    18851875        // First grab all filenames
    18861876        for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) {
    18871877                if (file->isDirectory()) continue;
    1888                 tstr = file->name();
    1889                 tstr.toLowercase();
    1890 
    1891                 allFiles[tstr] = true;
     1878                Common::String filename = file->name();
     1879                filename.toLowercase();
     1880                allFiles[filename] = true; // Save the filename in a hash table
    18921881        }
    18931882
    1894         // Now check for v2
    18951883        if (allFiles.contains("logdir") && allFiles.contains("object") &&
    18961884                allFiles.contains("picdir") && allFiles.contains("snddir") &&
    18971885                allFiles.contains("viewdir") && allFiles.contains("vol.0") &&
    1898                 allFiles.contains("words.tok")) {
    1899                 matchedNum = 0;
     1886                allFiles.contains("words.tok")) { // Check for v2
     1887                // The default AGI interpreter version 0x2917 is okay for v2 games
     1888                // so we don't have to change it here.
     1889                matchedUsingFilenames = true;
    19001890
    1901                 // Check if it is AGIPAL
    1902                 if (allFiles.contains("pal.101"))
    1903                         matchedNum = 1;
     1891                if (allFiles.contains("pal.101")) { // Check if it is AGIPAL
     1892                        description = "Unknown v2 AGIPAL Game";
     1893                        g_fallbackDesc.features |= GF_AGIPAL; // Add AGIPAL feature flag
     1894                } else { // Not AGIPAL so just plain v2
     1895                        description = "Unknown v2 Game";
     1896                }               
    19041897        } else { // Try v3
    19051898                char name[8];
    19061899
     
    19111904
    19121905                                if (allFiles.contains("object") && allFiles.contains("words.tok") &&
    19131906                                        allFiles.contains(Common::String(name) + "dir")) {
    1914                                         matchedNum = 2;
     1907                                        matchedUsingFilenames = true;
     1908                                        description = "Unknown v3 Game";
     1909                                        g_fallbackDesc.version = 0x3149; // Set the default AGI version for an AGI v3 game
    19151910                                        break;
    19161911                                }
    19171912                        }
    19181913                }
    19191914        }
    19201915       
    1921         if (matchedNum != -1) {
    1922                 matched.push_back(&fallbackDescs[matchedNum].desc);
     1916        // Check that the AGI interpreter version is a supported one
     1917        if (!(g_fallbackDesc.version >= 0x2000 && g_fallbackDesc.version < 0x4000)) {
     1918                warning("Unsupported AGI interpreter version 0x%x in AGI's fallback detection. Using default 0x2917", g_fallbackDesc.version);
     1919                g_fallbackDesc.version = 0x2917;
     1920        }
    19231921
     1922        // Set game type (v2 or v3) according to the AGI interpreter version number
     1923        if (g_fallbackDesc.version >= 0x2000 && g_fallbackDesc.version < 0x3000)
     1924                g_fallbackDesc.gameType = GType_V2;
     1925        else if (g_fallbackDesc.version >= 0x3000 && g_fallbackDesc.version < 0x4000)
     1926                g_fallbackDesc.gameType = GType_V3;
     1927
     1928        // Check if we found a match with any of the fallback methods
     1929        Common::EncapsulatedADGameDesc result;
     1930        if (matchedUsingFilenames) {
     1931                extra = description + " " + extra; // Let's combine the description and extra
     1932                result = Common::EncapsulatedADGameDesc((const Common::ADGameDescription *)&g_fallbackDesc, gameid, extra);
     1933
    19241934                printf("Your game version has been detected using fallback matching as a\n");
    1925                 printf("variant of %s (%s).\n", fallbackDescs[matchedNum].desc.gameid, fallbackDescs[matchedNum].desc.extra);
     1935                printf("variant of %s (%s).\n", result.getGameID(), result.getExtra());
    19261936                printf("If this is an original and unmodified version or new made Fanmade game,\n");
    19271937                printf("please report any, information previously printed by ScummVM to the team.\n");
     1938
    19281939        }
    19291940
    1930         return matched;
     1941        return result;
    19311942}
    19321943
    1933 }
     1944} // End of namespace Agi
    19341945
    19351946static const Common::ADParams detectionParams = {
    19361947        // Pointer to ADGameDescription or its superset structure
     
    19601971namespace Agi {
    19611972
    19621973bool AgiEngine::initGame() {
    1963         _gameDescription = (const AGIGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1974        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1975        _gameDescription = (const AGIGameDescription *)(encapsulatedDesc.realDesc);
     1976
    19641977        return (_gameDescription != 0);
    19651978}
    19661979
  • engines/kyra/detection.cpp

     
    133133        assert(engine);
    134134        const char *gameid = ConfMan.get("gameid").c_str();
    135135       
    136         const KYRAGameDescription *gd = (const KYRAGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     136        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     137        const KYRAGameDescription *gd = (const KYRAGameDescription *)(encapsulatedDesc.realDesc);
     138
    137139        if (gd == 0) {
    138140                // maybe add non md5 based detection again?
    139141                return kNoGameDataFoundError;
  • engines/gob/detection.cpp

     
    11501150namespace Gob {
    11511151
    11521152bool GobEngine::detectGame() {
    1153         const GOBGameDescription *gd = (const GOBGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1153        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1154        const GOBGameDescription *gd = (const GOBGameDescription *)(encapsulatedDesc.realDesc);
     1155
    11541156        if (gd == 0)
    11551157                return false;
    11561158
  • engines/parallaction/detection.cpp

     
    131131namespace Parallaction {
    132132
    133133bool Parallaction::detectGame() {
    134         _gameDescription = (const PARALLACTIONGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     134        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     135        _gameDescription = (const PARALLACTIONGameDescription *)(encapsulatedDesc.realDesc);
     136
    135137        return (_gameDescription != 0);
    136138}
    137139
  • engines/saga/detection.cpp

     
    124124namespace Saga {
    125125
    126126bool SagaEngine::initGame() {
    127         _gameDescription = (const SAGAGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     127        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     128        _gameDescription = (const SAGAGameDescription *)(encapsulatedDesc.realDesc);
     129
    128130        if (_gameDescription == 0)
    129131                return false;
    130132
  • engines/cine/detection.cpp

     
    494494namespace Cine {
    495495
    496496bool CineEngine::initGame() {
    497         _gameDescription = (const CINEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     497        Common::EncapsulatedADGameDesc encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     498        _gameDescription = (const CINEGameDescription *)(encapsulatedDesc.realDesc);
     499
    498500        return (_gameDescription != 0);
    499501}
    500502