aboutsummaryrefslogtreecommitdiff
path: root/engines/agi/detection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi/detection.cpp')
-rw-r--r--engines/agi/detection.cpp217
1 files changed, 143 insertions, 74 deletions
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index b7f8c12730..1ab59c0806 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -31,6 +31,7 @@
#include "common/file.h"
#include "agi/agi.h"
+#include "agi/wagparser.h"
namespace Agi {
@@ -1612,8 +1613,8 @@ static const AGIGameDescription gameDescriptions[] = {
FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"),
FANMADE_I("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a"),
FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440),
- FANMADE("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5"),
- FANMADE("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5"),
+ FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256),
+ FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2),
FANMADE_LF("Abrah: L'orphelin de l'espace (v1.2)", "b7b6d1539e14d5a26fa3088288e1badc", Common::FR_FRA, GF_AGIPAL),
FANMADE("Acidopolis", "7017db1a4b726d0d59e65e9020f7d9f7"),
FANMADE("Agent 0055 (v1.0)", "c2b34a0c77acb05482781dda32895f24"),
@@ -1648,7 +1649,7 @@ static const AGIGameDescription gameDescriptions[] = {
FANMADE("DG: The Adventure Game (English v1.1)", "0d6376d493fa7a21ec4da1a063e12b25"),
FANMADE_L("DG: The Adventure Game (French v1.1)", "258bdb3bb8e61c92b71f2f456cc69e23", Common::FR_FRA),
FANMADE("Dashiki (16 Colors)", "9b2c7b9b0283ab9f12bedc0cb6770a07"),
- FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE),
+ FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE|GF_AGI256),
FANMADE("Date Quest 1 (v1.0)", "ba3dcb2600645be53a13170aa1a12e69"),
FANMADE("Date Quest 2 (v1.0 Demo)", "1602d6a2874856e928d9a8c8d2d166e9"),
FANMADE("Date Quest 2 (v1.0)", "f13f6fc85aa3e6e02b0c20408fb63b47"),
@@ -1825,112 +1826,178 @@ 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<Common::String, int32, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> IntMap;
IntMap allFiles;
- Common::ADGameDescList matched;
- int matchedNum = -1;
+ bool matchedUsingFilenames = false;
+ bool matchedUsingWag = false;
+ int wagFileCount = 0;
+ WagFileParser wagFileParser;
+ Common::String wagFilePath;
+ 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
+ // First grab all filenames and at the same time count the number of *.wag files
for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) {
if (file->isDirectory()) continue;
- tstr = file->getName();
- tstr.toLowercase();
-
- allFiles[tstr] = true;
+ Common::String filename = file->getName();
+ filename.toLowercase();
+ allFiles[filename] = true; // Save the filename in a hash table
+
+ if (filename.hasSuffix(".wag")) {
+ // Save latest found *.wag file's path (Can be used to open the file, the name can't)
+ wagFilePath = file->getPath();
+ wagFileCount++; // Count found *.wag files
+ }
}
- // 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];
for (IntMap::const_iterator f = allFiles.begin(); f != allFiles.end(); ++f) {
if (f->_key.hasSuffix("vol.0")) {
memset(name, 0, 8);
- strncpy(name, f->_key.c_str(), f->_key.size() > 5 ? f->_key.size() - 5 : f->_key.size());
+ strncpy(name, f->_key.c_str(), MIN((uint)8, f->_key.size() > 5 ? f->_key.size() - 5 : f->_key.size()));
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);
+
+ // WinAGI produces *.wag file with interpreter version, game name and other parameters.
+ // If there's exactly one *.wag file and it parses successfully then we'll use its information.
+ if (wagFileCount == 1 && wagFileParser.parse(wagFilePath.c_str())) {
+ matchedUsingWag = true;
+
+ const WagProperty *wagAgiVer = wagFileParser.getProperty(WagProperty::PC_INTVERSION);
+ const WagProperty *wagGameID = wagFileParser.getProperty(WagProperty::PC_GAMEID);
+ const WagProperty *wagGameDesc = wagFileParser.getProperty(WagProperty::PC_GAMEDESC);
+ const WagProperty *wagGameVer = wagFileParser.getProperty(WagProperty::PC_GAMEVERSION);
+ const WagProperty *wagGameLastEdit = wagFileParser.getProperty(WagProperty::PC_GAMELAST);
+
+ // If there is an AGI version number in the *.wag file then let's use it
+ if (wagAgiVer != NULL && wagFileParser.checkAgiVersionProperty(*wagAgiVer)) {
+ // TODO/FIXME: Check that version number is something we support before trying to use it.
+ // If the version number is unsupported then it'll get switched to 0x2917 later.
+ // But there's the possibility that file based detection has detected something else
+ // than a v2 AGI game. So there's a possibility for conflicting information.
+ g_fallbackDesc.version = wagFileParser.convertToAgiVersionNumber(*wagAgiVer);
+ }
+
+ // Set gameid according to *.wag file information if it's present and it doesn't contain whitespace.
+ if (wagGameID != NULL && !Common::String(wagGameID->getData()).contains(" ")) {
+ gameid = wagGameID->getData();
+ debug(3, "Agi::fallbackDetector: Using game id (%s) from WAG file", gameid.c_str());
+ }
+
+ // Set game description and extra according to *.wag file information if they're present
+ if (wagGameDesc != NULL) {
+ description = wagGameDesc->getData();
+ debug(3, "Agi::fallbackDetector: Game description (%s) from WAG file", wagGameDesc->getData());
+
+ // If there's game version in the *.wag file, set extra to it
+ if (wagGameVer != NULL) {
+ extra = wagGameVer->getData();
+ debug(3, "Agi::fallbackDetector: Game version (%s) from WAG file", wagGameVer->getData());
+ }
+
+ // If there's game last edit date in the *.wag file, add it to extra
+ if (wagGameLastEdit != NULL) {
+ if (!extra.empty() ) extra += " ";
+ extra += wagGameLastEdit->getData();
+ debug(3, "Agi::fallbackDetector: Game's last edit date (%s) from WAG file", wagGameLastEdit->getData());
+ }
+ }
+ } else if (wagFileCount > 1) { // More than one *.wag file, confusing! So let's not use them.
+ warning("More than one (%d) *.wag files found. WAG files ignored", wagFileCount);
+ }
+
+ // 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 (matchedUsingWag || 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 +2027,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);
}