AGI: Do not try to pass filenames of disk images from the detector to engine

This commit is contained in:
Jussi Pitkanen 2011-06-04 18:12:26 +03:00 committed by Eugene Sandulenko
parent 1f680ecbc8
commit 4d4a558f7b
4 changed files with 142 additions and 85 deletions

View File

@ -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;

View File

@ -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<Common::String, int32> IntMap;
typedef Common::HashMap<Common::String, Common::FSNode> 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].md5Disk0.empty(); i++) {
Common::String filenameDisk0;
Common::String filenameDisk1;
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;
if (!diskImageExistsInFSList(fslist, booterDescription[i].md5Disk0, filenameDisk0))
continue;
if (!booterDescription[i].md5Disk1.empty())
if (!diskImageExistsInFSList(fslist, booterDescription[i].md5Disk1, filenameDisk1))
continue;
g_fallbackDesc.desc.gameid = booterDescription[index].id.c_str();
g_fallbackDesc.desc.extra = booterDescription[index].extra.c_str();
g_fallbackDesc.gameID = booterDescription[i].gameID;
g_fallbackDesc.gameType = booterDescription[i].gameType;
g_fallbackDesc.features = booterDescription[i].features;
g_fallbackDesc.version = booterDescription[i].version;
return (const ADGameDescription *)&g_fallbackDesc;
}
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 {

View File

@ -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 }
};

View File

@ -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();