2006-10-02 22:21:57 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
|
|
|
* Copyright (C) 2004-2006 The ScummVM project
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/stdafx.h"
|
|
|
|
|
2006-11-12 03:23:29 +00:00
|
|
|
#include "base/plugins.h"
|
|
|
|
|
2006-10-02 22:21:57 +00:00
|
|
|
#include "common/util.h"
|
|
|
|
#include "common/hash-str.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
#include "common/md5.h"
|
|
|
|
#include "common/advancedDetector.h"
|
2006-11-12 03:23:29 +00:00
|
|
|
#include "common/config-manager.h"
|
2006-10-02 22:21:57 +00:00
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
2006-11-12 03:23:29 +00:00
|
|
|
PluginError real_ADVANCED_DETECTOR_ENGINE_CREATE(
|
|
|
|
DetectedGameList (*detectFunc)(const FSList &fslist),
|
|
|
|
const Common::ADObsoleteGameID *obsoleteList
|
|
|
|
) {
|
|
|
|
const char *gameid = ConfMan.get("gameid").c_str();
|
|
|
|
|
|
|
|
if (obsoleteList != 0) {
|
|
|
|
for (const Common::ADObsoleteGameID *o = 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);
|
|
|
|
ConfMan.flushToDisk();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FSList fslist;
|
|
|
|
FilesystemNode dir(ConfMan.get("path"));
|
|
|
|
if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) {
|
|
|
|
return kInvalidPathError;
|
|
|
|
}
|
|
|
|
|
|
|
|
DetectedGameList detectedGames = detectFunc(fslist);
|
|
|
|
|
|
|
|
for (uint i = 0; i < detectedGames.size(); i++) {
|
|
|
|
if (detectedGames[i].gameid == gameid) {
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return kNoGameDataFoundError;
|
|
|
|
}
|
|
|
|
|
|
|
|
GameDescriptor real_ADVANCED_DETECTOR_FIND_GAMEID(
|
|
|
|
const char *gameid,
|
|
|
|
const PlainGameDescriptor *list,
|
|
|
|
const Common::ADObsoleteGameID *obsoleteList
|
|
|
|
) {
|
|
|
|
const PlainGameDescriptor *g = list;
|
|
|
|
while (g->gameid) {
|
|
|
|
if (0 == scumm_stricmp(gameid, g->gameid))
|
|
|
|
return *g;
|
|
|
|
g++;
|
|
|
|
}
|
|
|
|
|
|
|
|
GameDescriptor gs;
|
|
|
|
if (obsoleteList != 0) {
|
|
|
|
const Common::ADObsoleteGameID *o = obsoleteList;
|
|
|
|
while (o->from) {
|
|
|
|
if (0 == scumm_stricmp(gameid, o->from)) {
|
|
|
|
gs.gameid = gameid;
|
|
|
|
gs.description = "Obsolete game ID";
|
|
|
|
return gs;
|
|
|
|
}
|
|
|
|
o++;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
return *g;
|
|
|
|
return gs;
|
|
|
|
}
|
|
|
|
|
2006-12-19 22:43:15 +00:00
|
|
|
static DetectedGame toDetectedGame(const ADGameDescription &g, const PlainGameDescriptor *sg) {
|
2006-11-12 03:23:29 +00:00
|
|
|
const char *title = 0;
|
|
|
|
|
|
|
|
while (sg->gameid) {
|
|
|
|
if (!scumm_stricmp(g.name, sg->gameid))
|
|
|
|
title = sg->description;
|
|
|
|
sg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
DetectedGame dg(g.name, title, g.language, g.platform);
|
|
|
|
dg.updateDesc(g.extra);
|
|
|
|
return dg;
|
2006-11-10 22:43:10 +00:00
|
|
|
}
|
|
|
|
|
2006-11-12 03:23:29 +00:00
|
|
|
DetectedGameList real_ADVANCED_DETECTOR_DETECT_GAMES_FUNCTION(
|
|
|
|
const FSList &fslist,
|
|
|
|
const byte *descs,
|
|
|
|
const int descItemSize,
|
|
|
|
const int md5Bytes,
|
|
|
|
const PlainGameDescriptor *list
|
|
|
|
) {
|
|
|
|
DetectedGameList detectedGames;
|
2006-12-19 22:43:15 +00:00
|
|
|
Common::AdvancedDetector ad;
|
2006-11-12 03:23:29 +00:00
|
|
|
Common::ADList matches;
|
|
|
|
Common::ADGameDescList descList;
|
2006-12-19 03:52:04 +00:00
|
|
|
const byte *descPtr;
|
2006-11-12 03:23:29 +00:00
|
|
|
|
2006-12-19 12:21:23 +00:00
|
|
|
for (descPtr = descs; *descPtr != 0; descPtr += descItemSize)
|
2006-12-19 03:52:04 +00:00
|
|
|
descList.push_back((const ADGameDescription *)descPtr);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
2006-12-19 22:43:15 +00:00
|
|
|
ad.registerGameDescriptions(descList);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
2006-12-19 03:52:04 +00:00
|
|
|
debug(3, "%s: cnt: %d", ((const ADGameDescription *)descs)->name, descList.size());
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
matches = ad.detectGame(&fslist, md5Bytes, Common::UNK_LANG, Common::kPlatformUnknown);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
|
|
|
for (uint i = 0; i < matches.size(); i++)
|
|
|
|
detectedGames.push_back(toDetectedGame(*(const ADGameDescription *)(descs + matches[i] * descItemSize), list));
|
|
|
|
|
|
|
|
return detectedGames;
|
|
|
|
}
|
|
|
|
|
|
|
|
int real_ADVANCED_DETECTOR_DETECT_INIT_GAME(
|
|
|
|
const byte *descs,
|
|
|
|
const int descItemSize,
|
|
|
|
const int md5Bytes,
|
|
|
|
const PlainGameDescriptor *list
|
|
|
|
) {
|
|
|
|
int gameNumber = -1;
|
|
|
|
|
|
|
|
DetectedGameList detectedGames;
|
2006-12-19 22:43:15 +00:00
|
|
|
Common::AdvancedDetector ad;
|
2006-11-12 03:23:29 +00:00
|
|
|
Common::ADList matches;
|
|
|
|
Common::ADGameDescList descList;
|
2006-12-19 03:52:04 +00:00
|
|
|
const byte *descPtr;
|
2006-11-12 03:23:29 +00:00
|
|
|
|
|
|
|
Common::Language language = Common::UNK_LANG;
|
|
|
|
Common::Platform platform = Common::kPlatformUnknown;
|
|
|
|
|
|
|
|
if (ConfMan.hasKey("language"))
|
|
|
|
language = Common::parseLanguage(ConfMan.get("language"));
|
|
|
|
if (ConfMan.hasKey("platform"))
|
|
|
|
platform = Common::parsePlatform(ConfMan.get("platform"));
|
|
|
|
|
|
|
|
Common::String gameid = ConfMan.get("gameid");
|
|
|
|
|
2006-12-19 12:21:23 +00:00
|
|
|
for (descPtr = descs; *descPtr != 0; descPtr += descItemSize)
|
2006-12-19 03:52:04 +00:00
|
|
|
descList.push_back((const ADGameDescription *)descPtr);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
2006-12-19 22:43:15 +00:00
|
|
|
ad.registerGameDescriptions(descList);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
matches = ad.detectGame(0, md5Bytes, language, platform);
|
2006-11-12 03:23:29 +00:00
|
|
|
|
|
|
|
for (uint i = 0; i < matches.size(); i++) {
|
|
|
|
if (toDetectedGame(*(const ADGameDescription *)(descs + matches[i] * descItemSize), list).gameid == gameid) {
|
|
|
|
gameNumber = matches[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-19 03:52:04 +00:00
|
|
|
if (gameNumber >= (int)descList.size() || gameNumber == -1) {
|
|
|
|
error("TODO invalid gameNumber %d (max. expected value: %d)", gameNumber, descList.size());
|
2006-11-12 03:23:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
debug(2, "Running %s", toDetectedGame(*(const ADGameDescription *)(descs + gameNumber * descItemSize), list).description.c_str());
|
|
|
|
|
|
|
|
return gameNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-03 21:01:49 +00:00
|
|
|
String AdvancedDetector::getDescription(int num) const {
|
2006-10-02 22:21:57 +00:00
|
|
|
char tmp[256];
|
2006-10-24 09:44:20 +00:00
|
|
|
const ADGameDescription *g = _gameDescriptions[num];
|
2006-10-02 22:21:57 +00:00
|
|
|
|
|
|
|
snprintf(tmp, 256, "%s (%s %s/%s)", g->name, g->extra,
|
|
|
|
getPlatformDescription(g->platform), getLanguageDescription(g->language));
|
|
|
|
|
|
|
|
return String(tmp);
|
|
|
|
}
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
ADList AdvancedDetector::detectGame(const FSList *fslist, int md5Bytes, Language language, Platform platform) {
|
2006-10-02 22:21:57 +00:00
|
|
|
typedef HashMap<String, bool, CaseSensitiveString_Hash, CaseSensitiveString_EqualTo> StringSet;
|
|
|
|
StringSet filesList;
|
|
|
|
|
|
|
|
typedef StringMap StringMap;
|
|
|
|
StringMap filesMD5;
|
|
|
|
|
|
|
|
String tstr, tstr2;
|
|
|
|
|
|
|
|
uint i;
|
|
|
|
int j;
|
|
|
|
char md5str[32+1];
|
|
|
|
uint8 md5sum[16];
|
|
|
|
|
|
|
|
bool fileMissing;
|
2006-10-08 00:20:44 +00:00
|
|
|
const ADGameFileDescription *fileDesc;
|
2006-10-02 22:21:57 +00:00
|
|
|
|
|
|
|
assert(_gameDescriptions.size());
|
|
|
|
|
|
|
|
// First we compose list of files which we need MD5s for
|
|
|
|
for (i = 0; i < _gameDescriptions.size(); i++) {
|
2006-12-19 04:35:08 +00:00
|
|
|
for (j = 0; _gameDescriptions[i]->filesDescriptions[j].fileName; j++) {
|
2006-10-02 22:21:57 +00:00
|
|
|
tstr = String(_gameDescriptions[i]->filesDescriptions[j].fileName);
|
|
|
|
tstr.toLowercase();
|
|
|
|
tstr2 = tstr + ".";
|
|
|
|
filesList[tstr] = true;
|
|
|
|
filesList[tstr2] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-12 03:23:29 +00:00
|
|
|
if (fslist != 0) {
|
2006-10-02 22:21:57 +00:00
|
|
|
for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) {
|
|
|
|
if (file->isDirectory()) continue;
|
|
|
|
tstr = file->name();
|
|
|
|
tstr.toLowercase();
|
|
|
|
tstr2 = tstr + ".";
|
|
|
|
|
|
|
|
if (!filesList.contains(tstr) && !filesList.contains(tstr2)) continue;
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
if (!md5_file(*file, md5sum, md5Bytes)) continue;
|
2006-10-02 22:21:57 +00:00
|
|
|
for (j = 0; j < 16; j++) {
|
|
|
|
sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
|
|
|
|
}
|
|
|
|
filesMD5[tstr] = String(md5str);
|
|
|
|
filesMD5[tstr2] = String(md5str);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
File testFile;
|
|
|
|
|
|
|
|
for (StringSet::const_iterator file = filesList.begin(); file != filesList.end(); ++file) {
|
|
|
|
tstr = file->_key;
|
|
|
|
tstr.toLowercase();
|
|
|
|
|
2006-10-02 23:31:14 +00:00
|
|
|
debug(3, "+ %s", tstr.c_str());
|
2006-10-02 22:21:57 +00:00
|
|
|
if (!filesMD5.contains(tstr)) {
|
|
|
|
if (testFile.open(file->_key)) {
|
|
|
|
testFile.close();
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
if (md5_file(file->_key.c_str(), md5sum, md5Bytes)) {
|
2006-10-02 22:21:57 +00:00
|
|
|
for (j = 0; j < 16; j++) {
|
|
|
|
sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
|
|
|
|
}
|
|
|
|
filesMD5[tstr] = String(md5str);
|
2006-10-02 23:31:14 +00:00
|
|
|
debug(3, "> %s: %s", tstr.c_str(), md5str);
|
2006-10-02 22:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
ADList matched;
|
|
|
|
int maxFilesMatched = 0;
|
|
|
|
|
2006-10-02 22:21:57 +00:00
|
|
|
for (i = 0; i < _gameDescriptions.size(); i++) {
|
|
|
|
fileMissing = false;
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
// Do not even bother to look at entries which do not have matching
|
|
|
|
// language and platform (if specified).
|
|
|
|
if ((_gameDescriptions[i]->language != language && language != UNK_LANG) ||
|
|
|
|
(_gameDescriptions[i]->platform != platform && platform != kPlatformUnknown)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-10-02 22:21:57 +00:00
|
|
|
// Try to open all files for this game
|
2006-12-19 04:35:08 +00:00
|
|
|
for (j = 0; _gameDescriptions[i]->filesDescriptions[j].fileName; j++) {
|
2006-10-02 22:21:57 +00:00
|
|
|
fileDesc = &_gameDescriptions[i]->filesDescriptions[j];
|
|
|
|
tstr = fileDesc->fileName;
|
|
|
|
tstr.toLowercase();
|
|
|
|
tstr2 = tstr + ".";
|
|
|
|
|
|
|
|
if (!filesMD5.contains(tstr) && !filesMD5.contains(tstr2)) {
|
|
|
|
fileMissing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (strcmp(fileDesc->md5, filesMD5[tstr].c_str()) && strcmp(fileDesc->md5, filesMD5[tstr2].c_str())) {
|
|
|
|
fileMissing = true;
|
|
|
|
break;
|
|
|
|
}
|
2006-11-03 21:01:49 +00:00
|
|
|
debug(3, "Matched file: %s", tstr.c_str());
|
2006-10-02 22:21:57 +00:00
|
|
|
}
|
|
|
|
if (!fileMissing) {
|
2006-12-19 03:52:04 +00:00
|
|
|
debug(2, "Found game: %s (%d)", getDescription(i).c_str(), i);
|
2006-12-19 23:14:15 +00:00
|
|
|
|
|
|
|
// Count the number of matching files. Then, only keep those
|
|
|
|
// entries which match a maximal amount of files.
|
|
|
|
int curFilesMatched = 0;
|
|
|
|
for (j = 0; _gameDescriptions[i]->filesDescriptions[j].fileName; j++)
|
|
|
|
curFilesMatched++;
|
|
|
|
|
|
|
|
if (curFilesMatched > maxFilesMatched) {
|
|
|
|
debug(2, " ... new best match, removing all previous candidates");
|
|
|
|
maxFilesMatched = curFilesMatched;
|
|
|
|
matched.clear();
|
|
|
|
matched.push_back(i);
|
|
|
|
} else if (curFilesMatched == maxFilesMatched) {
|
|
|
|
matched.push_back(i);
|
|
|
|
} else {
|
|
|
|
debug(2, " ... skipped");
|
|
|
|
}
|
|
|
|
|
2006-12-19 03:52:04 +00:00
|
|
|
} else {
|
|
|
|
debug(5, "Skipping game: %s (%d)", getDescription(i).c_str(), i);
|
2006-10-02 22:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
if (!filesMD5.empty() && matched.empty()) {
|
2006-10-02 22:21:57 +00:00
|
|
|
printf("MD5s of your game version are unknown. Please, report following data to\n");
|
|
|
|
printf("ScummVM team along with your game name and version:\n");
|
|
|
|
|
|
|
|
for (StringMap::const_iterator file = filesMD5.begin(); file != filesMD5.end(); ++file)
|
|
|
|
printf("%s: %s\n", file->_key.c_str(), file->_value.c_str());
|
|
|
|
}
|
|
|
|
|
2006-12-19 23:14:15 +00:00
|
|
|
return matched;
|
2006-10-02 22:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Common
|