Ticket #8690: fallbackDetector.patch

File fallbackDetector.patch, 23.3 KB (added by SF/buddha_, 14 years ago)

Fallback detector patch

  • engines/touche/detection.cpp

     
    124124namespace Touche {
    125125
    126126bool ToucheEngine::detectGame() {
    127         const Common::ADGameDescription *gd = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    128         if (gd == 0)
     127        const Common::ADGameDescription *gd = NULL;
     128        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     129        if (encapsulatedDesc != NULL) {
     130                gd = encapsulatedDesc->realDesc;
     131                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     132        }
     133
     134        if (gd == NULL)
    129135                return false;
    130136
    131137        _language = gd->language;
  • 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);
    121         //if (gd == 0) {
     120        //const AGOSGameDescription *gd = NULL;
     121        //const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     122        //if (encapsulatedDesc != NULL) {
     123        //      gd = (const AGOSGameDescription *)(encapsulatedDesc->realDesc);
     124        //      delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     125        //}
     126        //if (gd == NULL) {
    122127        //      return kNoGameDataFoundError;
    123128        //}
    124129
     
    154159namespace AGOS {
    155160
    156161bool AGOSEngine::initGame() {
    157         _gameDescription = (const AGOSGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    158         return (_gameDescription != 0);
     162        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     163        if (encapsulatedDesc != NULL) {
     164                _gameDescription = (const AGOSGameDescription *)(encapsulatedDesc->realDesc);
     165                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     166        } else {
     167                _gameDescription = NULL;
     168        }
     169
     170        return (_gameDescription != NULL);
    159171}
    160172
    161173
  • engines/cruise/detection.cpp

     
    118118namespace Cruise {
    119119
    120120bool CruiseEngine::initGame() {
    121         _gameDescription = (const CRUISEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     121        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     122        if (encapsulatedDesc != NULL) {
     123                _gameDescription = (const CRUISEGameDescription *)(encapsulatedDesc->realDesc);
     124                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     125        } else {
     126                _gameDescription = NULL;
     127        }
    122128
    123         return (_gameDescription != 0);
     129        return (_gameDescription != NULL);
    124130}
    125131
    126132} // End of namespace Cruise
  • 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;
     1847const Common::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        if (matchedUsingFilenames) {
     1930                extra = description + " " + extra; // Let's combine the description and extra
     1931                const Common::EncapsulatedADGameDesc *result = new Common::EncapsulatedADGameDesc((const Common::ADGameDescription *)&g_fallbackDesc, gameid, extra);
     1932
    19241933                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);
     1934                printf("variant of %s (%s).\n", result->getGameID(), result->getExtra());
    19261935                printf("If this is an original and unmodified version or new made Fanmade game,\n");
    19271936                printf("please report any, information previously printed by ScummVM to the team.\n");
    1928         }
    19291937
    1930         return matched;
     1938                return result;
     1939        } else // If we found no match then let's return NULL
     1940                return NULL;
    19311941}
    19321942
    1933 }
     1943} // End of namespace Agi
    19341944
    19351945static const Common::ADParams detectionParams = {
    19361946        // Pointer to ADGameDescription or its superset structure
     
    19601970namespace Agi {
    19611971
    19621972bool AgiEngine::initGame() {
    1963         _gameDescription = (const AGIGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    1964         return (_gameDescription != 0);
     1973        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1974        if (encapsulatedDesc != NULL) {
     1975                _gameDescription = (const AGIGameDescription *)(encapsulatedDesc->realDesc);
     1976                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     1977        } else {
     1978                _gameDescription = NULL;
     1979        }
     1980
     1981        return (_gameDescription != NULL);
    19651982}
    19661983
    19671984} // End of namespace Agi
  • 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);
    137         if (gd == 0) {
     136        const KYRAGameDescription *gd = NULL;
     137        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     138        if (encapsulatedDesc != NULL) {
     139                gd = (const KYRAGameDescription *)(encapsulatedDesc->realDesc);
     140                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     141        }
     142
     143        if (gd == NULL) {
    138144                // maybe add non md5 based detection again?
    139145                return kNoGameDataFoundError;
    140146        }
  • engines/gob/detection.cpp

     
    11501150namespace Gob {
    11511151
    11521152bool GobEngine::detectGame() {
    1153         const GOBGameDescription *gd = (const GOBGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    1154         if (gd == 0)
     1153        const GOBGameDescription *gd = NULL;
     1154        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     1155        if (encapsulatedDesc != NULL) {
     1156                gd = (const GOBGameDescription *)(encapsulatedDesc->realDesc);
     1157                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     1158        }
     1159
     1160        if (gd == NULL)
    11551161                return false;
    11561162
    1157         if (gd->startTotBase == 0) {
     1163        if (gd->startTotBase == NULL) {
    11581164                _startTot = new char[10];
    11591165                _startTot0 = new char[11];
    11601166                strcpy(_startTot, "intro.tot");
  • engines/parallaction/detection.cpp

     
    131131namespace Parallaction {
    132132
    133133bool Parallaction::detectGame() {
    134         _gameDescription = (const PARALLACTIONGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    135         return (_gameDescription != 0);
     134        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     135        if (encapsulatedDesc != NULL) {
     136                _gameDescription = (const PARALLACTIONGameDescription *)(encapsulatedDesc->realDesc);
     137                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     138        } else {
     139                _gameDescription = NULL;
     140        }
     141
     142        return (_gameDescription != NULL);
    136143}
    137144
    138145} // End of namespace Parallaction
  • engines/saga/detection.cpp

     
    124124namespace Saga {
    125125
    126126bool SagaEngine::initGame() {
    127         _gameDescription = (const SAGAGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    128         if (_gameDescription == 0)
     127        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     128        if (encapsulatedDesc != NULL) {
     129                _gameDescription = (const SAGAGameDescription *)(encapsulatedDesc->realDesc);
     130                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     131        } else {
     132                _gameDescription = NULL;
     133        }
     134
     135        if (_gameDescription == NULL)
    129136                return false;
    130137
    131138        _displayClip.right = getDisplayInfo().logicalWidth;
  • engines/cine/detection.cpp

     
    494494namespace Cine {
    495495
    496496bool CineEngine::initGame() {
    497         _gameDescription = (const CINEGameDescription *)Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
    498         return (_gameDescription != 0);
     497        const Common::EncapsulatedADGameDesc *encapsulatedDesc = Common::AdvancedDetector::detectBestMatchingGame(detectionParams);
     498        if (encapsulatedDesc != NULL) {
     499                _gameDescription = (const CINEGameDescription *)(encapsulatedDesc->realDesc);
     500                delete encapsulatedDesc; // This is on the detectBestMatchingGame's caller's responsibility.
     501        } else {
     502                _gameDescription = NULL;
     503        }
     504
     505        return (_gameDescription != NULL);
    499506}
    500507
    501508} // End of namespace Cine
  • 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(const ADGameDescription *paramRealDesc = NULL,
     77                Common::String paramGameID = Common::String(""),
     78                Common::String paramExtra = Common::String(""))
     79                : realDesc(paramRealDesc), gameid(paramGameID), extra(paramExtra) {
     80                assert(paramRealDesc != NULL);
     81        }
     82
     83        // Functions for getting the correct gameid and extra values from the struct
     84        const char *getGameID() const { return gameid.empty() ? realDesc->gameid : gameid.c_str(); }
     85        const char *getExtra() const { return extra.empty() ? realDesc->extra : extra.c_str(); }
     86};
     87
     88/**
    6789 * A list of pointers to ADGameDescription structs (or subclasses thereof).
    6890 */
    6991typedef Array<const ADGameDescription*> ADGameDescList;
     
    175197         * @note The fslist parameter may be 0 -- in that case, it is assumed
    176198         *       that the callback searchs the current directory.
    177199         *
     200         * @note You need to handle deleting the returned pointer if it's not NULL.
     201         *       You don't need to delete the memory pointed to by the realDesc
     202         *       pointer inside the struct though because it should be statically
     203         *       allocated.
     204         *
     205         * @return A pointer to a single EncapsulatedADGameDesc if there was a
     206         *         match, otherwise NULL.
     207         *
    178208         * @todo
    179209         */
    180         ADGameDescList (*fallbackDetectFunc)(const FSList *fslist);
     210        const EncapsulatedADGameDesc *(*fallbackDetectFunc)(const FSList *fslist);
    181211
    182212        /**
    183213         * A bitmask of flags which can be used to configure the behavior
     
    206236// FIXME/TODO: Rename this function to something more sensible.
    207237GameList detectAllGames(const FSList &fslist, const Common::ADParams &params);
    208238
    209 // FIXME/TODO: Rename this function to something more sensible.
    210 const ADGameDescription *detectBestMatchingGame(const Common::ADParams &params);
     239/**
     240 * Detect the best matching game.
     241 *
     242 * FIXME/TODO: Rename this function to something more sensible.
     243 *
     244 * @note You need to handle deleting the returned pointer if it's not NULL.
     245 *       You don't need to delete the memory pointed to by the realDesc
     246 *       pointer inside the struct though because it should be statically
     247 *       allocated.
     248 */
     249const EncapsulatedADGameDesc *detectBestMatchingGame(const Common::ADParams &params);
    211250
    212251// FIXME/TODO: Rename this function to something more sensible.
    213252// 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        GameDescriptor gd(g.getGameID(), title, g.realDesc->language, g.realDesc->platform);
     126        gd.updateDesc(g.getExtra());
     127        return gd;
     128}
     129
    113130/**
    114131 * Generate a preferred target value as
    115132 *   GAMEID-PLAFORM-LANG
     
    139156        const Common::ADParams &params
    140157        ) {
    141158        ADGameDescList matches = detectGame(&fslist, params, Common::UNK_LANG, Common::kPlatformUnknown);
    142 
    143159        GameList detectedGames;
    144         for (uint i = 0; i < matches.size(); i++) {
    145                 GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
     160        const EncapsulatedADGameDesc *fallbackDesc = NULL;
    146161
     162        // Use fallback detector if there were no matches by other means
     163        if (matches.empty() && params.fallbackDetectFunc != NULL) {
     164                fallbackDesc = (*params.fallbackDetectFunc)(&fslist);
     165                if (fallbackDesc != NULL) detectedGames.push_back(toGameDescriptor(*fallbackDesc, params.list));
     166        } else for (uint i = 0; i < matches.size(); i++) // Otherwise use the found matches
     167                detectedGames.push_back(toGameDescriptor(*matches[i], params.list));
     168
     169        for (uint i = 0; i < detectedGames.size(); i++) {
    147170                if (params.singleid != NULL) {
    148                         desc["preferredtarget"] = desc["gameid"];
    149                         desc["gameid"] = params.singleid;
     171                        detectedGames[i]["preferredtarget"] = detectedGames[i]["gameid"];
     172                        detectedGames[i]["gameid"] = params.singleid;
    150173                }
    151174
    152175                if (params.flags & kADFlagAugmentPreferredTarget) {
    153                         if (!desc.contains("preferredtarget"))
    154                                 desc["preferredtarget"] = desc["gameid"];
     176                        if (!detectedGames[i].contains("preferredtarget"))
     177                                detectedGames[i]["preferredtarget"] = detectedGames[i]["gameid"];
    155178
    156                         desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], matches[i]);
     179                        if (!matches.empty()) { // If found a match or matches without fallback detection
     180                                detectedGames[i]["preferredtarget"] = generatePreferredTarget(detectedGames[i]["preferredtarget"], matches[i]);
     181                        } else if (fallbackDesc != NULL) { // If found match with the fallback detection
     182                                // This assumes that generatePreferredTarget doesn't need gameid or extra fields from the fallbackDesc,
     183                                // so we can just give it the ADGameDescription from inside the *fallbackDesc.
     184                                detectedGames[i]["preferredtarget"] = generatePreferredTarget(detectedGames[i]["preferredtarget"], fallbackDesc->realDesc);
     185                        }
    157186                }
    158 
    159                 detectedGames.push_back(desc);
    160187        }
    161188
     189        if (fallbackDesc != NULL)
     190                delete fallbackDesc; // Delete the fallback descriptor! Must be done to avoid memory leakage!
     191
    162192        return detectedGames;
    163193}
    164194
    165 const ADGameDescription *detectBestMatchingGame(
     195const EncapsulatedADGameDesc *detectBestMatchingGame(
    166196        const Common::ADParams &params
    167197        ) {
    168         const ADGameDescription *agdDesc = 0;
     198        const ADGameDescription *agdDesc = NULL;
     199        const EncapsulatedADGameDesc *result = NULL;
    169200        Common::Language language = Common::UNK_LANG;
    170201        Common::Platform platform = Common::kPlatformUnknown;
    171202
     
    176207
    177208        Common::String gameid = ConfMan.get("gameid");
    178209
    179         ADGameDescList matches = detectGame(0, params, language, platform);
     210        ADGameDescList matches = detectGame(NULL, params, language, platform);
    180211
    181212        if (params.singleid == NULL) {
    182213                for (uint i = 0; i < matches.size(); i++) {
     
    189220                agdDesc = matches[0];
    190221        }
    191222
    192         if (agdDesc != 0) {
    193                 debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str());
     223        // Use fallback detector if there were no matches by other means
     224        if (matches.empty() && params.fallbackDetectFunc != NULL) {
     225                const EncapsulatedADGameDesc *fallbackDesc = (*params.fallbackDetectFunc)(NULL);
     226                if (fallbackDesc != NULL && (params.singleid != NULL || fallbackDesc->getGameID() == gameid)) {
     227                        result = fallbackDesc; // Found a fallback match
     228                }
    194229        }
    195230
    196         return agdDesc;
     231        // If we found a match without fallback detection
     232        // then let's convert it to this function's return format
     233        if (agdDesc != NULL) {
     234                assert(result == NULL); // Should be NULL at the moment
     235                result = new EncapsulatedADGameDesc(agdDesc);
     236        }
     237
     238        if (result != NULL) {
     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                const EncapsulatedADGameDesc *fallbackDesc = (*params.fallbackDetectFunc)(&fslist);
     288                if (fallbackDesc != NULL) {
     289                        bool foundFallbackMatch = (params.singleid != NULL || fallbackDesc->getGameID() == gameid);
     290                        delete fallbackDesc; // Delete the fallback descriptor! Must be done to avoid memory leakage!
     291                        if (foundFallbackMatch) return kNoError;
     292                }
     293        }
     294
    239295        return kNoGameDataFoundError;
    240296}
    241297
     
    486542                }
    487543        }
    488544
    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 
    494545        return matched;
    495546}
    496547