From 04353038ae51599a430da76cdeecc6c78ab4d137 Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Tue, 14 Jun 2011 15:03:27 +0300 Subject: AGI: Add loader and detection for Black Cauldron booter --- engines/agi/agi.h | 8 ++- engines/agi/detection.cpp | 1 + engines/agi/detection_tables.h | 2 + engines/agi/loader_v1.cpp | 121 ++++++++++++++++++++++++++++++++--------- 4 files changed, 104 insertions(+), 28 deletions(-) diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 55c1ab32e8..2ba617f0ad 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -131,8 +131,9 @@ enum AgiGameID { enum AgiGameType { GType_PreAGI = 0, - GType_V2 = 1, - GType_V3 = 2 + GType_V1 = 1, + GType_V2 = 2, + GType_V3 = 3 }; // @@ -678,7 +679,8 @@ private: Common::String _filenameDisk0; Common::String _filenameDisk1; - int loadDir(AgiDir *agid, int offset, int num); + int loadDir_DDP(AgiDir *agid, int offset, int max); + int loadDir_BC(AgiDir *agid, int offset, int max); uint8 *loadVolRes(AgiDir *agid); public: diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index c6c9740f8c..ad3760c7fe 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -199,6 +199,7 @@ bool AgiMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD case Agi::GType_PreAGI: *engine = new Agi::PreAgiEngine(syst, gd); break; + case Agi::GType_V1: case Agi::GType_V2: case Agi::GType_V3: *engine = new Agi::AgiEngine(syst, gd); diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index e59765f6b9..be9bc1e185 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -865,9 +865,11 @@ static AGIGameDescription g_fallbackDesc = { /** * Descriptor table for booter games */ +#define BOOTER_V1(id, extra, md50, md51, gid) { md50, md51, id, extra, gid, GType_V1, ADGF_NO_FLAGS, 0x1120 }, #define BOOTER_V2(id, extra, md50, md51, gid) { md50, md51, id, extra, gid, GType_V2, ADGF_NO_FLAGS, 0x2001 }, static AGIBooterDescription booterDescription[] = { + BOOTER_V1("bc", "booter", "98a51d3a372baa9df288b6c0f0232567", "5568f7a52e787305656246f95e2aa375", GID_BC) 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 6a7ab6c61f..ab350cd920 100644 --- a/engines/agi/loader_v1.cpp +++ b/engines/agi/loader_v1.cpp @@ -23,41 +23,56 @@ #include "agi/agi.h" #include "common/md5.h" +#define IMAGE_SIZE 368640 // = 40 * 2 * 9 * 512 = tracks * sides * sectors * sector size #define SECTOR_OFFSET(s) ((s) * 512) -#define BASE_SECTOR 0x1C2 -#define LOGDIR_SEC SECTOR_OFFSET(171) + 5 -#define LOGDIR_NUM 43 +#define DDP_BASE_SECTOR 0x1C2 -#define PICDIR_SEC SECTOR_OFFSET(180) + 5 -#define PICDIR_NUM 30 +#define DDP_LOGDIR_SEC SECTOR_OFFSET(171) + 5 +#define DDP_LOGDIR_MAX 43 -#define VIEWDIR_SEC SECTOR_OFFSET(189) + 5 -#define VIEWDIR_NUM 171 +#define DDP_PICDIR_SEC SECTOR_OFFSET(180) + 5 +#define DDP_PICDIR_MAX 30 -#define SNDDIR_SEC SECTOR_OFFSET(198) + 5 -#define SNDDIR_NUM 64 +#define DDP_VIEWDIR_SEC SECTOR_OFFSET(189) + 5 +#define DDP_VIEWDIR_MAX 171 + +#define DDP_SNDDIR_SEC SECTOR_OFFSET(198) + 5 +#define DDP_SNDDIR_MAX 64 + + +#define BC_LOGDIR_SEC SECTOR_OFFSET(90) + 5 +#define BC_LOGDIR_MAX 118 + +#define BC_VIEWDIR_SEC SECTOR_OFFSET(96) + 5 +#define BC_VIEWDIR_MAX 180 + +#define BC_PICDIR_SEC SECTOR_OFFSET(93) + 8 +#define BC_PICDIR_MAX 117 + +#define BC_SNDDIR_SEC SECTOR_OFFSET(99) + 5 +#define BC_SNDDIR_MAX 29 namespace Agi { AgiLoader_v1::AgiLoader_v1(AgiEngine *vm) { _vm = vm; - +} + +int AgiLoader_v1::detectGame() { // 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()); } -int AgiLoader_v1::loadDir(AgiDir *agid, int offset, int num) { +int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { Common::File fp; if (!fp.open(_filenameDisk0)) @@ -70,7 +85,7 @@ int AgiLoader_v1::loadDir(AgiDir *agid, int offset, int num) { } fp.seek(offset, SEEK_SET); - for (int i = 0; i < num; i++) { + for (int i = 0; i <= max; i++) { int b0 = fp.readByte(); int b1 = fp.readByte(); int b2 = fp.readByte(); @@ -79,7 +94,7 @@ int AgiLoader_v1::loadDir(AgiDir *agid, int offset, int num) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; } else { - int sec = (BASE_SECTOR + (((b0 & 0xF) << 8) | b1)) >> 1; + int sec = (DDP_BASE_SECTOR + (((b0 & 0xF) << 8) | b1)) >> 1; int off = ((b1 & 0x1) << 8) | b2; agid[i].volume = 0; agid[i].offset = SECTOR_OFFSET(sec) + off; @@ -91,16 +106,65 @@ int AgiLoader_v1::loadDir(AgiDir *agid, int offset, int num) { return errOK; } +int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) { + Common::File fp; + + if (!fp.open(_filenameDisk0)) + return errBadFileOpen; + + // Cleanup + for (int i = 0; i < MAX_DIRS; i++) { + agid[i].volume = 0xFF; + agid[i].offset = _EMPTY; + } + + fp.seek(offset, SEEK_SET); + for (int i = 0; i <= max; i++) { + int b0 = fp.readByte(); + int b1 = fp.readByte(); + int b2 = fp.readByte(); + + if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) { + agid[i].volume = 0xFF; + agid[i].offset = _EMPTY; + } else { + int sec = (b0 & 0x3F) * 18 + ((b1 >> 1) & 0x1) * 9 + ((b1 >> 2) & 0x1F) - 1; + int off = ((b1 & 0x1) << 8) | b2; + int vol = (b0 & 0xC0) >> 6; + agid[i].volume = 0; + agid[i].offset = (vol == 2) * IMAGE_SIZE + SECTOR_OFFSET(sec) + off; + } + } + + fp.close(); + + return errOK; +} + int AgiLoader_v1::init() { int ec = errOK; - ec = loadDir(_vm->_game.dirLogic, LOGDIR_SEC, LOGDIR_NUM); - if (ec == errOK) - ec = loadDir(_vm->_game.dirPic, PICDIR_SEC, PICDIR_NUM); - if (ec == errOK) - ec = loadDir(_vm->_game.dirView, VIEWDIR_SEC, VIEWDIR_NUM); - if (ec == errOK) - ec = loadDir(_vm->_game.dirSound, SNDDIR_SEC, SNDDIR_NUM); + switch (_vm->getGameID()) { + case GID_DDP: + ec = loadDir_DDP(_vm->_game.dirLogic, DDP_LOGDIR_SEC, DDP_LOGDIR_MAX); + if (ec == errOK) + ec = loadDir_DDP(_vm->_game.dirPic, DDP_PICDIR_SEC, DDP_PICDIR_MAX); + if (ec == errOK) + ec = loadDir_DDP(_vm->_game.dirView, DDP_VIEWDIR_SEC, DDP_VIEWDIR_MAX); + if (ec == errOK) + ec = loadDir_DDP(_vm->_game.dirSound, DDP_SNDDIR_SEC, DDP_SNDDIR_MAX); + break; + + case GID_BC: + ec = loadDir_BC(_vm->_game.dirLogic, BC_LOGDIR_SEC, BC_LOGDIR_MAX); + if (ec == errOK) + ec = loadDir_BC(_vm->_game.dirPic, BC_PICDIR_SEC, BC_PICDIR_MAX); + if (ec == errOK) + ec = loadDir_BC(_vm->_game.dirView, BC_VIEWDIR_SEC, BC_VIEWDIR_MAX); + if (ec == errOK) + ec = loadDir_BC(_vm->_game.dirSound, BC_SNDDIR_SEC, BC_SNDDIR_MAX); + break; + } return ec; } @@ -113,12 +177,19 @@ int AgiLoader_v1::deinit() { uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { uint8 *data = NULL; Common::File fp; + int offset = agid->offset; - if (agid->offset == _EMPTY) + if (offset == _EMPTY) return NULL; - fp.open(_filenameDisk0); - fp.seek(agid->offset, SEEK_SET); + if (offset > IMAGE_SIZE) { + fp.open(_filenameDisk1); + offset -= IMAGE_SIZE; + } else { + fp.open(_filenameDisk0); + } + + fp.seek(offset, SEEK_SET); int signature = fp.readUint16BE(); if (signature != 0x1234) { -- cgit v1.2.3