diff options
Diffstat (limited to 'engines/sci/detection.cpp')
-rw-r--r-- | engines/sci/detection.cpp | 223 |
1 files changed, 163 insertions, 60 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index beea025aea..1ccfc6bf02 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -120,6 +120,154 @@ static const PlainGameDescriptor SciGameTitles[] = { {0, 0} }; +struct OldNewIdTableEntry { + const char *oldId; + const char *newId; + SciVersion version; +}; + +static const OldNewIdTableEntry s_oldNewTable[] = { + { "arthur", "camelot", SCI_VERSION_NONE }, + { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga + { "brain", "castlebrain", SCI_VERSION_1_LATE }, + { "demo", "christmas1988", SCI_VERSION_NONE }, + { "card", "christmas1990", SCI_VERSION_1_EARLY, }, + { "card", "christmas1992", SCI_VERSION_1_1 }, + { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, + // iceman is the same + { "icedemo", "iceman", SCI_VERSION_NONE }, + // longbow is the same + { "eco", "ecoquest", SCI_VERSION_NONE }, + { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo + { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full + { "tales", "fairytales", SCI_VERSION_NONE }, + { "fp", "freddypharkas", SCI_VERSION_NONE }, + { "emc", "funseeker", SCI_VERSION_NONE }, + { "gk", "gk1", SCI_VERSION_NONE }, + // gk2 is the same + { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, + { "cardgames", "hoyle1", SCI_VERSION_NONE }, + { "solitare", "hoyle2", SCI_VERSION_NONE }, + // hoyle3 is the same + // hoyle4 is the same + { "brain", "islandbrain", SCI_VERSION_1_1 }, + { "demo000", "kq1sci", SCI_VERSION_NONE }, + { "kq1", "kq1sci", SCI_VERSION_NONE }, + { "kq4", "kq4sci", SCI_VERSION_NONE }, + // kq5 is the same + // kq6 is the same + // kq7 is the same + { "mm1", "laurabow", SCI_VERSION_NONE }, + { "cb1", "laurabow", SCI_VERSION_NONE }, + { "lb2", "laurabow2", SCI_VERSION_NONE }, + { "rh", "longbow", SCI_VERSION_NONE }, + { "ll1", "lsl1sci", SCI_VERSION_NONE }, + { "lsl1", "lsl1sci", SCI_VERSION_NONE }, + // lsl2 is the same + { "lsl3", "lsl3", SCI_VERSION_NONE }, + { "ll5", "lsl5", SCI_VERSION_NONE }, + // lsl5 is the same + // lsl6 is the same + { "mg", "mothergoose", SCI_VERSION_NONE }, + { "twisty", "pepper", SCI_VERSION_NONE }, + { "scary", "phantasmagoria", SCI_VERSION_NONE }, + // TODO: distinguish the full version of Phantasmagoria from the demo + { "pq1", "pq1sci", SCI_VERSION_NONE }, + { "pq", "pq2", SCI_VERSION_NONE }, + // pq3 is the same + // pq4 is the same + { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA + { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA + { "trial", "qfg2", SCI_VERSION_NONE }, + { "hq2demo", "qfg2", SCI_VERSION_NONE }, + // rama is the same + // TODO: distinguish the full version of rama from the demo + { "thegame", "slater", SCI_VERSION_NONE }, + { "sq1demo", "sq1sci", SCI_VERSION_NONE }, + { "sq1", "sq1sci", SCI_VERSION_NONE }, + // sq3 is the same + // sq4 is the same + // sq5 is the same + // sq6 is the same + // TODO: distinguish the full version of SQ6 from the demo + // torin is the same + + + // TODO: SCI3 IDs + + { "", "", SCI_VERSION_NONE } +}; + +Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, ResourceManager *resMan) { + // Convert the id to lower case, so that we match all upper/lower case variants. + sierraId.toLowercase(); + + // If the game has less than the expected scripts, it's a demo + uint32 demoThreshold = 100; + // ...but there are some exceptions + if (sierraId == "brain" || sierraId == "lsl1" || + sierraId == "mg" || sierraId == "pq" || + sierraId == "jones" || + sierraId == "cardgames" || sierraId == "solitare" || + sierraId == "hoyle3" || sierraId == "hoyle4") + demoThreshold = 40; + if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") + demoThreshold = 150; + + Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1); + if (resources->size() < demoThreshold) { + *gameFlags |= ADGF_DEMO; + + // Crazy Nick's Picks + if (sierraId == "lsl1" && resources->size() == 34) + return "cnick-lsl"; + if (sierraId == "sq4" && resources->size() == 34) + return "cnick-sq"; + + // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) + + // Handle Astrochicken 1 (SQ3) and 2 (SQ4) + if (sierraId == "sq3" && resources->size() == 20) + return "astrochicken"; + if (sierraId == "sq4") + return "msastrochicken"; + } + + if (sierraId == "torin" && resources->size() == 226) // Torin's Passage demo + *gameFlags |= ADGF_DEMO; + + for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { + if (sierraId == cur->oldId) { + // Distinguish same IDs from the SCI version + if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) + continue; + + return cur->newId; + } + } + + if (sierraId == "glory") { + // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), + // or qfg4 full (SCI2) + // qfg1 VGA doesn't have view 1 + if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) + return "qfg1"; + + // qfg4 full is SCI2 + if (getSciVersion() == SCI_VERSION_2) + return "qfg4"; + + // qfg4 demo has less than 50 scripts + if (resources->size() < 50) + return "qfg4"; + + // Otherwise it's qfg3 + return "qfg3"; + } + + return sierraId; +} + #include "sci/detection_tables.h" /** @@ -205,42 +353,6 @@ Common::Language charToScummVMLanguage(const char c) { } } -#define READ_UINT16(ptr) (!resMan->isSci11Mac() ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) - -// Finds the internal ID of the current game from script 0 -Common::String getSierraGameId(ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, 0), false); - // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated - Resource *heap = 0; - byte *seeker = 0; - - // Seek to the name selector of the first export - if (getSciVersion() < SCI_VERSION_1_1) { - const int nameSelector = 3; - int extraSci0EarlyBytes = (getSciVersion() == SCI_VERSION_0_EARLY) ? 2 : 0; - byte *exportPtr = script->data + extraSci0EarlyBytes + 4 + 2; - seeker = script->data + READ_UINT16(script->data + READ_UINT16(exportPtr) + nameSelector * 2); - } else { - const int nameSelector = 5 + 3; - heap = resMan->findResource(ResourceId(kResourceTypeHeap, 0), false); - byte *exportPtr = script->data + 4 + 2 + 2; - seeker = heap->data + READ_UINT16(heap->data + READ_UINT16(exportPtr) + nameSelector * 2); - } - - char sierraId[20]; - int i = 0; - byte curChar = 0; - - do { - curChar = *(seeker + i); - sierraId[i++] = curChar; - } while (curChar != 0); - - return sierraId; -} - -#undef READ_UINT16 - const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const { bool foundResMap = false; bool foundRes000 = false; @@ -261,19 +373,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl filename.toLowercase(); if (filename.contains("resource.map") || filename.contains("resmap.00") || filename.contains("Data1")) { - // HACK: resource.map is located in the same directory as the other resource files, - // therefore add the directory here, so that the game files can be opened later on - // We now add the parent directory temporary to our SearchMan so the engine code - // used in the detection can access all files via Common::File without any problems. - // In all branches returning from this function, we need to have a call to - // SearchMan.remove to remove it from the default directory pool again. - // - // A proper solution to remove this hack would be to have the code, which is needed - // for detection, to operate on Stream objects, so they can be easily called from - // the detection code. This might be easily to achieve through refactoring the - // code needed for detection. - assert(!SearchMan.hasArchive("SCI_detection")); - SearchMan.addDirectory("SCI_detection", file->getParent()); foundResMap = true; } @@ -317,7 +416,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl // If these files aren't found, it can't be SCI if (!foundResMap && !foundRes000) { - SearchMan.remove("SCI_detection"); return 0; } @@ -325,11 +423,10 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl ViewType gameViews = resMan->getViewType(); // Have we identified the game views? If not, stop here + // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files + // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI if (gameViews == kViewUnknown) { - SearchMan.remove("SCI_detection"); delete resMan; - // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files - // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI return 0; } @@ -337,7 +434,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl // Is SCI32 compiled in? If not, and this is a SCI32 game, // stop here if (getSciVersion() >= SCI_VERSION_2) { - SearchMan.remove("SCI_detection"); delete resMan; return (const ADGameDescription *)&s_fallbackDesc; } @@ -352,7 +448,15 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl s_fallbackDesc.platform = Common::kPlatformAmiga; // Determine the game id - Common::String gameId = convertSierraGameId(getSierraGameId(resMan).c_str(), &s_fallbackDesc.flags, resMan); + Common::String sierraGameId = resMan->findSierraGameId(); + + // If we don't have a game id, the game is not SCI + if (sierraGameId.empty()) { + delete resMan; + return 0; + } + + Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated s_fallbackDesc.gameid = s_fallbackGameIdBuf; @@ -386,7 +490,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl } } - delete resMan; // Fill in extras field if (!strcmp(s_fallbackDesc.gameid, "lsl1sci") || @@ -394,14 +497,14 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl !strcmp(s_fallbackDesc.gameid, "sq1sci")) s_fallbackDesc.extra = "VGA Remake"; - if (!strcmp(s_fallbackDesc.gameid, "qfg1") && !Common::File::exists("resource.001")) + if (!strcmp(s_fallbackDesc.gameid, "qfg1") && getSciVersion() == SCI_VERSION_1_1) s_fallbackDesc.extra = "VGA Remake"; // Add "demo" to the description for demos if (s_fallbackDesc.flags & ADGF_DEMO) s_fallbackDesc.extra = "demo"; - SearchMan.remove("SCI_detection"); + delete resMan; return (const ADGameDescription *)&s_fallbackDesc; } @@ -427,8 +530,8 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const { bool SciEngine::hasFeature(EngineFeature f) const { return //(f == kSupportsRTL) || - (f == kSupportsLoadingDuringRuntime); - //(f == kSupportsSavingDuringRuntime); + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); } SaveStateList SciMetaEngine::listSaves(const char *target) const { |