From 4d4a558f7beda9e3732dd30f16a6e73f5e805df2 Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Sat, 4 Jun 2011 18:12:26 +0300 Subject: AGI: Do not try to pass filenames of disk images from the detector to engine --- engines/agi/agi.h | 29 ++++++--- engines/agi/detection.cpp | 136 ++++++++++++++++++++++++++++------------- engines/agi/detection_tables.h | 51 +++++----------- engines/agi/loader_v1.cpp | 17 +++++- 4 files changed, 145 insertions(+), 88 deletions(-) diff --git a/engines/agi/agi.h b/engines/agi/agi.h index bf730fcfb4..2d9432be52 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -631,6 +631,24 @@ struct AgiGame { Common::Rect mouseFence; /**< rectangle set by fence.mouse command */ }; +/** + * Check if a disk image with the given MD5 sum exists in the search path. + * + * @param md5 MD5 sum of the disk image to be searched + * @param filename Filled with a filename in case the image is found + * @return True if found, otherwise false. + */ +bool diskImageExists(Common::String md5, Common::String &filename); + +/** + * Get MD5 sums for a given game from the booter game description table. + * + * @param gid Game ID of the game + * @param md5Disk0 Filled with the MD5 sum of disk 0 + * @param md5Disk1 Filled with the MD5 sum of disk 1 if there are two disks + */ +bool getBooterMD5Sums(AgiGameID gid, Common::String &md5Disk0, Common::String &md5Disk1); + class AgiLoader { public: @@ -649,18 +667,14 @@ public: class AgiLoader_v1 : public AgiLoader { private: AgiEngine *_vm; - Common::String _dsk0Name; - Common::String _dsk1Name; + Common::String _filenameDisk0; + Common::String _filenameDisk1; int loadDir(AgiDir *agid, int offset, int num); uint8 *loadVolRes(AgiDir *agid); public: - AgiLoader_v1(AgiEngine *vm, Common::String dsk0, Common::String dsk1) { - _vm = vm; - _dsk0Name = dsk0; - _dsk1Name = dsk1; - } + AgiLoader_v1(AgiEngine *vm); virtual int init(); virtual int deinit(); @@ -715,6 +729,7 @@ public: virtual int loadWords(const char *); }; + class GfxMgr; class SpritesMgr; class Menu; diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 854658cc57..7c6cf0eaff 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -48,9 +48,6 @@ struct AGIGameDescription { int gameType; uint32 features; uint16 version; - - Common::String dsk0Name; - Common::String dsk1Name; }; uint32 AgiBase::getGameID() const { @@ -97,6 +94,20 @@ void AgiBase::initVersion() { _gameVersion = _gameDescription->version; } +struct AGIBooterDescription { + Common::String md5Disk0; + Common::String md5Disk1; + Common::String id; + Common::String extra; + int gameID; + int gameType; + uint32 features; + uint16 version; +}; + +bool isDiskImage(Common::SeekableReadStream *stream, Common::String filename, Common::String md5); +bool diskImageExistsInFSList(const Common::FSList &fslist, Common::String md5, Common::String &filename); + } static const PlainGameDescriptor agiGames[] = { @@ -294,9 +305,7 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist) const { typedef Common::HashMap IntMap; - typedef Common::HashMap NodeMap; IntMap allFiles; - NodeMap allNodes; bool matchedUsingFilenames = false; bool matchedUsingWag = false; int wagFileCount = 0; @@ -324,7 +333,6 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX Common::String filename = file->getName(); filename.toLowercase(); allFiles[filename] = true; // Save the filename in a hash table - allNodes[filename] = *file; // ...and also the FSNode if (filename.hasSuffix(".wag")) { // Save latest found *.wag file's path (Can be used to open the file, the name can't) @@ -429,41 +437,25 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX // Try to detect disk images of AGI v1 and AGI v2.001 booter games if (!matchedUsingFilenames && !matchedUsingWag) { - int index = -1; - - for (IntMap::const_iterator f = allFiles.begin(); f != allFiles.end(); ++f) { - if (f->_key.hasSuffix(".dsk") || f->_key.hasSuffix(".img")) { - Common::File file; - file.open(allNodes[f->_key]); - Common::String md5str = Common::computeStreamMD5AsString(file, detectionParams.md5Bytes); - debug(3, "Agi::fallbackDetector: disk image (%s) found with md5 sum (%s) ", f->_key.c_str(), md5str.c_str()); - - for (int i = 0; !booterDescription[i].md5str_dsk0.empty(); i++) { - if (booterDescription[i].md5str_dsk0 == md5str) { - index = i; - g_fallbackDesc.dsk0Name = f->_key; - } - if (booterDescription[i].md5str_dsk1 == md5str) { - index = i; - g_fallbackDesc.dsk1Name = f->_key; - } - } - } - } - - if (index >= 0) { - if ((booterDescription[index].md5str_dsk0.empty() == g_fallbackDesc.dsk0Name.empty()) && - (booterDescription[index].md5str_dsk1.empty() == g_fallbackDesc.dsk1Name.empty())) { - g_fallbackDesc.gameID = booterDescription[index].gameID; - g_fallbackDesc.gameType = booterDescription[index].gameType; - g_fallbackDesc.features = booterDescription[index].features; - g_fallbackDesc.version = booterDescription[index].version; - - g_fallbackDesc.desc.gameid = booterDescription[index].id.c_str(); - g_fallbackDesc.desc.extra = booterDescription[index].extra.c_str(); - - return (const ADGameDescription *)&g_fallbackDesc; - } + for (int i = 0; !booterDescription[i].md5Disk0.empty(); i++) { + Common::String filenameDisk0; + Common::String filenameDisk1; + + if (!diskImageExistsInFSList(fslist, booterDescription[i].md5Disk0, filenameDisk0)) + continue; + if (!booterDescription[i].md5Disk1.empty()) + if (!diskImageExistsInFSList(fslist, booterDescription[i].md5Disk1, filenameDisk1)) + continue; + + g_fallbackDesc.gameID = booterDescription[i].gameID; + g_fallbackDesc.gameType = booterDescription[i].gameType; + g_fallbackDesc.features = booterDescription[i].features; + g_fallbackDesc.version = booterDescription[i].version; + + g_fallbackDesc.desc.gameid = booterDescription[i].id.c_str(); + g_fallbackDesc.desc.extra = booterDescription[i].extra.c_str(); + + return (const ADGameDescription *)&g_fallbackDesc; } } @@ -508,6 +500,66 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX namespace Agi { +bool isDiskImage(Common::SeekableReadStream *stream, Common::String filename, Common::String md5) { + if (!(filename.hasSuffix(".dsk") || filename.hasSuffix(".img"))) + return false; + + if (stream->size() == 368640 && computeStreamMD5AsString(*stream, 5000) == md5) + return true; + + return false; +} + +bool diskImageExistsInFSList(const Common::FSList &fslist, Common::String md5, Common::String &filename) { + for (Common::FSList::const_iterator x = fslist.begin(); x != fslist.end(); x++) { + if (x->isDirectory()) continue; + + Common::SeekableReadStream *stream = x->createReadStream(); + if (isDiskImage(stream, x->getName(), md5)) { + filename = x->getName(); + delete stream; + return true; + } + delete stream; + } + + return false; +} + +bool diskImageExists(Common::String md5, Common::String &filename) { + Common::ArchiveMemberList files; + SearchMan.listMembers(files); + + Common::File file; + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); x++) { + file.open((*x)->getName()); + if (isDiskImage(&file, (*x)->getName(), md5)) { + filename = (*x)->getName(); + file.close(); + return true; + } + file.close(); + } + + return false; +} + +bool getBooterMD5Sums(AgiGameID gid, Common::String &md5Disk0, Common::String &md5Disk1) { + AGIBooterDescription *booter = NULL; + int i = 0; + while (!booterDescription[i].md5Disk0.empty()) { + if (booterDescription[i].gameID == gid) + booter = &booterDescription[i]; + i++; + } + if (booter == NULL) + return false; + + md5Disk0 = booter->md5Disk0; + md5Disk1 = booter->md5Disk1; + return true; +} + bool AgiBase::canLoadGameStateCurrently() { return (!(getGameType() == GType_PreAGI) && getflag(fMenusWork) && !_noSaveLoadAllowed); } @@ -525,7 +577,7 @@ int AgiEngine::agiDetectGame() { assert(_gameDescription != NULL); if (getVersion() <= 0x2001) { - _loader = new AgiLoader_v1(this, _gameDescription->dsk0Name, _gameDescription->dsk1Name); + _loader = new AgiLoader_v1(this); } else if (getVersion() <= 0x2999) { _loader = new AgiLoader_v2(this); } else { diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index ade45243f7..c535744ebe 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -37,9 +37,7 @@ using Common::GUIO_NONE; gid, \ interp, \ features, \ - ver, \ - "", \ - "" \ + ver \ } #define GAME_LVFPNF(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \ @@ -55,9 +53,7 @@ using Common::GUIO_NONE; gid, \ interp, \ features, \ - ver, \ - "", \ - "" \ + ver \ } #define GAME(id,extra,md5,ver,gid) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V2) @@ -200,9 +196,7 @@ static const AGIGameDescription gameDescriptions[] = { GID_GOLDRUSH, GType_V3, GF_MACGOLDRUSH, - 0x3149, - "", - "" + 0x3149 }, @@ -520,9 +514,7 @@ static const AGIGameDescription gameDescriptions[] = { GID_SQ2, GType_V2, 0, - 0x2936, - "", - "" + 0x2936 }, @@ -667,9 +659,7 @@ static const AGIGameDescription gameDescriptions[] = { GID_FANMADE, GType_V2, GF_AGDS, - 0x2440, - "", - "" + 0x2440 }, { @@ -686,9 +676,7 @@ static const AGIGameDescription gameDescriptions[] = { GID_GETOUTTASQ, GType_V2, 0, - 0x2440, - "", - "" + 0x2440 }, FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE), @@ -843,9 +831,7 @@ static const AGIGameDescription gameDescriptions[] = { GID_FANMADE, GType_V3, GF_FANMADE, - 0x3149, - "", - "", + 0x3149 }, FANMADE_SVP("V - The Graphical Adventure", "1646eaade74f137a9041eb427a389969", 768, 0x2440, Common::kPlatformCoCo3), @@ -853,7 +839,7 @@ static const AGIGameDescription gameDescriptions[] = { FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Mar 29)", "11d0417b7b886f963d0b36789dac4c8f"), FANMADE("Wizaro (v0.1)", "abeec1eda6eaf8dbc52443ea97ff140c"), - { AD_TABLE_END_MARKER, 0, 0, 0, 0, "", "" } + { AD_TABLE_END_MARKER, 0, 0, 0, 0 } }; /** @@ -873,25 +859,16 @@ static AGIGameDescription g_fallbackDesc = { GID_FANMADE, GType_V2, GF_FANMADE, - 0x2917, - "", - "" + 0x2917 }; /** - * Detection table for booter games. + * Descriptor table for booter games */ -static const struct { - Common::String md5str_dsk0; - Common::String md5str_dsk1; - Common::String id; - Common::String extra; - int gameID; - int gameType; - uint32 features; - uint16 version; -} booterDescription[] = { - { "f323f10abf8140ffb2668b09af2e7b87", "", "ddp", "booter", GID_DDP, GType_V2, ADGF_NO_FLAGS, 0x2001 }, +#define BOOTER_V2(id, extra, md50, md51, gid) { md50, md51, id, extra, gid, GType_V2, ADGF_NO_FLAGS, 0x2001 }, + +static AGIBooterDescription booterDescription[] = { + BOOTER_V2("ddp", "booter", "f323f10abf8140ffb2668b09af2e7b87", "", GID_DDP) { "", "", "", "", 0, 0, 0, 0 } }; diff --git a/engines/agi/loader_v1.cpp b/engines/agi/loader_v1.cpp index 40f797286f..90655ec735 100644 --- a/engines/agi/loader_v1.cpp +++ b/engines/agi/loader_v1.cpp @@ -21,6 +21,7 @@ */ #include "agi/agi.h" +#include "common/md5.h" #define offsetTHS(track,head,sector) (512 * ((((track) * 2 + (head)) * 9) + (sector))) #define offset(sector) offsetTHS(sector / 18, (sector % 18) / 9, (sector % 18) % 9) @@ -41,6 +42,18 @@ namespace Agi { + +AgiLoader_v1::AgiLoader_v1(AgiEngine *vm) { + _vm = vm; + + // Find filenames for the disk images + Common::String md5Disk0, md5Disk1; + getBooterMD5Sums((AgiGameID)_vm->getGameID(), md5Disk0, md5Disk1); + diskImageExists(md5Disk0, _filenameDisk0); + if (!md5Disk1.empty()) + diskImageExists(md5Disk1, _filenameDisk1); +} + int AgiLoader_v1::detectGame() { return _vm->setupV2Game(_vm->getVersion()); } @@ -48,7 +61,7 @@ int AgiLoader_v1::detectGame() { int AgiLoader_v1::loadDir(AgiDir *agid, int offset, int num) { Common::File fp; - if (!fp.open(_dsk0Name)) + if (!fp.open(_filenameDisk0)) return errBadFileOpen; // Cleanup @@ -108,7 +121,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { int sec = agid->offset >> 16; int off = agid->offset & 0xFFFF; - fp.open(_dsk0Name); + fp.open(_filenameDisk0); fp.seek(offset(sec) + off, SEEK_SET); int signature = fp.readUint16BE(); -- cgit v1.2.3