From 6b132706d309f0b62bd2af36ad8b956a5db652f7 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Sun, 11 Dec 2016 13:35:15 +0100 Subject: ADL: Load hires5 data --- engines/adl/adl.h | 19 ++++- engines/adl/adl_v4.cpp | 199 +++++++++++++++++++++++++++++++++++++++++++++- engines/adl/adl_v4.h | 37 ++++++++- engines/adl/detection.cpp | 24 +++--- engines/adl/hires1.cpp | 4 +- engines/adl/hires2.cpp | 4 +- engines/adl/hires5.cpp | 90 ++++++++++++++++++++- engines/adl/hires6.cpp | 4 +- 8 files changed, 357 insertions(+), 24 deletions(-) diff --git a/engines/adl/adl.h b/engines/adl/adl.h index fc696f074f..06be0e072d 100644 --- a/engines/adl/adl.h +++ b/engines/adl/adl.h @@ -159,18 +159,30 @@ struct Time { Time() : hours(12), minutes(0) { } }; +struct RoomState { + byte picture; + byte isFirstTime; +}; + +struct Region { + Common::Array vars; + Common::Array rooms; +}; + struct State { + Common::Array regions; Common::Array rooms; Common::List items; Common::Array vars; + byte region; byte room; byte curPicture; uint16 moves; bool isDark; Time time; - State() : room(1), curPicture(0), moves(1), isDark(false) { } + State() : region(0), room(1), curPicture(0), moves(1), isDark(false) { } }; typedef Common::List Commands; @@ -362,8 +374,10 @@ protected: bool _isRestarting, _isRestoring, _isQuitting; bool _skipOneCommand; + const AdlGameDescription *_gameDescription; + private: - virtual void runIntro() const { } + virtual void runIntro() { } virtual void init() = 0; virtual void initGameState() = 0; virtual void drawItems() = 0; @@ -385,7 +399,6 @@ private: Console *_console; GUI::Debugger *getDebugger() { return _console; } - const AdlGameDescription *_gameDescription; byte _saveVerb, _saveNoun, _restoreVerb, _restoreNoun; bool _canSaveNow, _canRestoreNow; }; diff --git a/engines/adl/adl_v4.cpp b/engines/adl/adl_v4.cpp index 5fc15169c5..6a74881db5 100644 --- a/engines/adl/adl_v4.cpp +++ b/engines/adl/adl_v4.cpp @@ -21,11 +21,208 @@ */ #include "adl/adl_v4.h" +#include "adl/detection.h" namespace Adl { AdlEngine_v4::AdlEngine_v4(OSystem *syst, const AdlGameDescription *gd) : - AdlEngine_v3(syst, gd) { + AdlEngine_v3(syst, gd), + _currentVolume(0), + _itemPicIndex(nullptr) { + +} + +AdlEngine_v4::~AdlEngine_v4() { + delete _itemPicIndex; +} + +Common::String AdlEngine_v4::loadMessage(uint idx) const { + Common::String str = AdlEngine_v3::loadMessage(idx); + + for (uint i = 0; i < str.size(); ++i) { + const char *xorStr = "AVISDURGAN"; + str.setChar(str[i] ^ xorStr[i % strlen(xorStr)], i); + } + + return str; +} + +Common::String AdlEngine_v4::getItemDescription(const Item &item) const { + return _itemDesc[item.id - 1]; +} + +DiskImage *AdlEngine_v4::loadDisk(byte volume) const { + const ADGameFileDescription *ag; + + for (ag = _gameDescription->desc.filesDescriptions; ag->fileName; ag++) { + if (ag->fileType == volume) { + DiskImage *disk = new DiskImage(); + if (!disk->open(ag->fileName)) + error("Failed to open %s", ag->fileName); + return disk; + } + } + + error("Disk volume %d not found", volume); +} + +void AdlEngine_v4::insertDisk(byte volume) { + delete _disk; + _disk = loadDisk(volume); + _currentVolume = volume; +} + +void AdlEngine_v4::loadRegionLocations(Common::ReadStream &stream, uint regions) { + for (uint r = 0; r < regions; ++r) { + RegionLocation loc; + loc.track = stream.readByte(); + loc.sector = stream.readByte(); + + if (stream.eos() || stream.err()) + error("Failed to read region locations"); + + _regionLocations.push_back(loc); + } +} + +void AdlEngine_v4::loadRegionInitDataOffsets(Common::ReadStream &stream, uint regions) { + for (uint r = 0; r < regions; ++r) { + RegionInitDataOffset init; + init.track = stream.readByte(); + init.sector = stream.readByte(); + init.offset = stream.readByte(); + init.volume = stream.readByte(); + + if (stream.eos() || stream.err()) + error("Failed to read region init data offsets"); + + _regionInitDataOffsets.push_back(init); + } +} + +void AdlEngine_v4::initRegions(const byte *roomsPerRegion, uint regions) { + _state.regions.resize(regions); + + for (uint r = 0; r < regions; ++r) { + Region ®n = _state.regions[r]; + // Each region has 24 variables + regn.vars.resize(24); + + regn.rooms.resize(roomsPerRegion[r]); + for (uint rm = 0; rm < roomsPerRegion[r]; ++rm) { + // TODO: hires6 uses 0xff and has slightly different + // code working on these values + regn.rooms[rm].picture = 1; + regn.rooms[rm].isFirstTime = 1; + } + } +} + +void AdlEngine_v4::fixupDiskOffset(byte &track, byte §or) const { + if (_state.region == 0) + return; + + sector += _regionLocations[_state.region - 1].sector; + if (sector >= 16) { + sector -= 16; + ++track; + } + + track += _regionLocations[_state.region - 1].track; +} + +void AdlEngine_v4::adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const { + fixupDiskOffset(track, sector); +} + +void AdlEngine_v4::loadRegion(byte region) { + if (_currentVolume != _regionInitDataOffsets[region - 1].volume) { + insertDisk(_regionInitDataOffsets[region - 1].volume); + + // FIXME: This shouldn't be needed, but currently is, due to + // implementation choices made earlier on for DataBlockPtr and DiskImage. + _state.region = 0; // To avoid region offset being applied + _itemPics.clear(); + loadItemPictures(*_itemPicIndex, _itemPicIndex->size() / 5); + } + + _state.region = region; + + byte track = _regionInitDataOffsets[region - 1].track; + byte sector = _regionInitDataOffsets[region - 1].sector; + uint offset = _regionInitDataOffsets[region - 1].offset; + + fixupDiskOffset(track, sector); + + for (uint block = 0; block < 7; ++block) { + StreamPtr stream(_disk->createReadStream(track, sector, offset, 1)); + + uint16 addr = stream->readUint16LE(); + uint16 size = stream->readUint16LE(); + + stream.reset(_disk->createReadStream(track, sector, offset, size / 256 + 1)); + stream->skip(4); + + switch (addr) { + case 0x9000: { + // Messages + _messages.clear(); + uint count = size / 4; + loadMessages(*stream, count); + break; + } + case 0x4a80: { + // Global pics + _pictures.clear(); + loadPictures(*stream); + break; + } + case 0x4000: + // Verbs + loadWords(*stream, _verbs, _priVerbs); + break; + case 0x1800: + // Nouns + loadWords(*stream, _nouns, _priNouns); + break; + case 0x0e00: { + // Rooms + uint count = size / 14 - 1; + stream->skip(14); // Skip invalid room 0 + + _state.rooms.clear(); + loadRooms(*stream, count); + break; + } + case 0x7b00: + // Global commands + readCommands(*stream, _globalCommands); + break; + case 0x9500: + // Room commands + readCommands(*stream, _roomCommands); + break; + default: + error("Unknown data block found (addr %04x; size %04x)", addr, size); + } + + offset += 4 + size; + while (offset >= 256) { + offset -= 256; + ++sector; + if (sector >= 16) { + sector = 0; + ++track; + } + } + } +} + +void AdlEngine_v4::loadItemPicIndex(Common::ReadStream &stream, uint items) { + _itemPicIndex = stream.readStream(items * 5); + + if (stream.eos() || stream.err()) + error("Error reading item index"); } } // End of namespace Adl diff --git a/engines/adl/adl_v4.h b/engines/adl/adl_v4.h index b3465cb10a..2a5ceb422d 100644 --- a/engines/adl/adl_v4.h +++ b/engines/adl/adl_v4.h @@ -27,12 +27,47 @@ namespace Adl { +// Base track/sector for a region +struct RegionLocation { + byte track; + byte sector; +}; + +// Location of the 7 initial data blocks, relative to RegionLocation +struct RegionInitDataOffset { + byte track; + byte sector; + byte offset; + byte volume; +}; + class AdlEngine_v4 : public AdlEngine_v3 { public: - virtual ~AdlEngine_v4() { } + virtual ~AdlEngine_v4(); protected: AdlEngine_v4(OSystem *syst, const AdlGameDescription *gd); + + // AdlEngine + virtual Common::String loadMessage(uint idx) const; + virtual Common::String getItemDescription(const Item &item) const; + + // AdlEngine_v2 + virtual void adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const; + + DiskImage *loadDisk(byte volume) const; + void insertDisk(byte volume); + void loadRegionLocations(Common::ReadStream &stream, uint regions); + void loadRegionInitDataOffsets(Common::ReadStream &stream, uint regions); + void initRegions(const byte *roomsPerRegion, uint regions); + void fixupDiskOffset(byte &track, byte §or) const; + void loadRegion(byte region); + void loadItemPicIndex(Common::ReadStream &stream, uint items); + + byte _currentVolume; + Common::Array _regionLocations; + Common::Array _regionInitDataOffsets; + Common::SeekableReadStream *_itemPicIndex; }; } // End of namespace Adl diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp index 6c1085a127..cba66faca9 100644 --- a/engines/adl/detection.cpp +++ b/engines/adl/detection.cpp @@ -162,18 +162,18 @@ static const AdlGameDescription gameDescriptions[] = { { "hires5", 0, { - { "TZONE1A.NIB", 0, "475dedb7396fdcea81c1a2a4046caebe", 232960 }, - { "TZONE1B.NIB", 0, "f8aaea094ebbe41cf4354d9fe2c30d9a", 232960 }, - { "TZONE2C.NIB", 0, "b351a367dc48e776bf08e42a3f50ae74", 232960 }, - { "TZONE2D.NIB", 0, "9583b287a5c95960f5335878102bb8b1", 232960 }, - { "TZONE3E.NIB", 0, "502e42a0cb69ffe4a48cd51c1ff210cf", 232960 }, - { "TZONE3F.NIB", 0, "3d6e0aae15f590b72b6759535b6b7d3c", 232960 }, - { "TZONE4G.NIB", 0, "ede4113a9c9e17745faf71d099808a18", 232960 }, - { "TZONE4H.NIB", 0, "f95dae4aae1155a27f7120230464d4e1", 232960 }, - { "TZONE5I.NIB", 0, "92b3b376877f81a7b7ae426bf1e65456", 232960 }, - { "TZONE5J.NIB", 0, "c9ef796fa596548dbf8f085901f0bac3", 232960 }, - { "TZONE6K.NIB", 0, "2e5323be637002efce1d4c813ae20a3f", 232960 }, - { "TZONE6L.NIB", 0, "7c9268f0ea2d02120c77a46337b3d975", 232960 }, + { "TZONE1A.NIB", 2, "475dedb7396fdcea81c1a2a4046caebe", 232960 }, + { "TZONE1B.NIB", 3, "f8aaea094ebbe41cf4354d9fe2c30d9a", 232960 }, + { "TZONE2C.NIB", 4, "b351a367dc48e776bf08e42a3f50ae74", 232960 }, + { "TZONE2D.NIB", 5, "9583b287a5c95960f5335878102bb8b1", 232960 }, + { "TZONE3E.NIB", 6, "502e42a0cb69ffe4a48cd51c1ff210cf", 232960 }, + { "TZONE3F.NIB", 7, "3d6e0aae15f590b72b6759535b6b7d3c", 232960 }, + { "TZONE4G.NIB", 8, "ede4113a9c9e17745faf71d099808a18", 232960 }, + { "TZONE4H.NIB", 9, "f95dae4aae1155a27f7120230464d4e1", 232960 }, + { "TZONE5I.NIB", 10, "92b3b376877f81a7b7ae426bf1e65456", 232960 }, + { "TZONE5J.NIB", 11, "c9ef796fa596548dbf8f085901f0bac3", 232960 }, + { "TZONE6K.NIB", 12, "2e5323be637002efce1d4c813ae20a3f", 232960 }, + { "TZONE6L.NIB", 13, "7c9268f0ea2d02120c77a46337b3d975", 232960 }, AD_LISTEND }, Common::EN_ANY, diff --git a/engines/adl/hires1.cpp b/engines/adl/hires1.cpp index 217a9013ba..8bd49c75b4 100644 --- a/engines/adl/hires1.cpp +++ b/engines/adl/hires1.cpp @@ -98,7 +98,7 @@ public: private: // AdlEngine - void runIntro() const; + void runIntro(); void init(); void initGameState(); void restartGame(); @@ -126,7 +126,7 @@ private: } _gameStrings; }; -void HiRes1Engine::runIntro() const { +void HiRes1Engine::runIntro() { StreamPtr stream(_files->createReadStream(IDS_HR1_EXE_0)); stream->seek(IDI_HR1_OFS_LOGO_0); diff --git a/engines/adl/hires2.cpp b/engines/adl/hires2.cpp index 199f457b4f..9562095ec9 100644 --- a/engines/adl/hires2.cpp +++ b/engines/adl/hires2.cpp @@ -54,12 +54,12 @@ public: private: // AdlEngine - void runIntro() const; + void runIntro(); void init(); void initGameState(); }; -void HiRes2Engine::runIntro() const { +void HiRes2Engine::runIntro() { // This only works for the 16-sector re-release. The original // release is not supported at this time, because we don't have // access to it. diff --git a/engines/adl/hires5.cpp b/engines/adl/hires5.cpp index 4f1d5a28b8..4d52d2822c 100644 --- a/engines/adl/hires5.cpp +++ b/engines/adl/hires5.cpp @@ -36,19 +36,107 @@ namespace Adl { class HiRes5Engine : public AdlEngine_v4 { public: - HiRes5Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine_v4(syst, gd) { } + HiRes5Engine(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine_v4(syst, gd) { } private: // AdlEngine + void runIntro(); void init(); void initGameState(); + + static const uint kRegions = 41; + static const uint kItems = 69; }; +void HiRes5Engine::runIntro() { + insertDisk(2); + + StreamPtr stream(_disk->createReadStream(0x10, 0x0, 0x00, 31)); + + _display->setMode(DISPLAY_MODE_HIRES); + _display->loadFrameBuffer(*stream); + _display->updateHiResScreen(); + + inputKey(); + + _display->home(); + _display->setMode(DISPLAY_MODE_TEXT); + + stream.reset(_disk->createReadStream(0x03, 0xc, 0x34, 1)); + Common::String menu(readString(*stream)); + + while (!g_engine->shouldQuit()) { + _display->home(); + _display->printString(menu); + + Common::String cmd(inputString()); + + // We ignore the backup and format menu options + if (!cmd.empty() && cmd[0] == APPLECHAR('1')) + break; + }; +} + void HiRes5Engine::init() { _graphics = new Graphics_v2(*_display); + + insertDisk(2); + + StreamPtr stream(_disk->createReadStream(0x5, 0x0, 0x02)); + loadRegionLocations(*stream, kRegions); + + stream.reset(_disk->createReadStream(0xd, 0x2, 0x04)); + loadRegionInitDataOffsets(*stream, kRegions); + + stream.reset(_disk->createReadStream(0x7, 0xe)); + _strings.verbError = readStringAt(*stream, 0x4f); + _strings.nounError = readStringAt(*stream, 0x8e); + _strings.enterCommand = readStringAt(*stream, 0xbc); + + stream.reset(_disk->createReadStream(0x7, 0xc)); + _strings.lineFeeds = readString(*stream); + + // TODO: opcode strings + + _messageIds.cantGoThere = 110; + _messageIds.dontUnderstand = 112; + _messageIds.itemDoesntMove = 114; + _messageIds.itemNotHere = 115; + _messageIds.thanksForPlaying = 113; + + stream.reset(_disk->createReadStream(0xe, 0x1, 0x13, 4)); + loadItemDescriptions(*stream, kItems); + + stream.reset(_disk->createReadStream(0xb, 0xa, 0x05, 1)); + loadItemPicIndex(*stream, kItems); + + if (stream->eos() || stream->err()) + error("Error reading item index"); } void HiRes5Engine::initGameState() { + _state.vars.resize(40); + + insertDisk(2); + + StreamPtr stream(_disk->createReadStream(0x5, 0x1, 0x00, 3)); + loadItems(*stream); + + // A combined total of 1213 rooms + static const byte rooms[kRegions] = { + 6, 16, 24, 57, 40, 30, 76, 40, + 54, 38, 44, 21, 26, 42, 49, 32, + 59, 69, 1, 1, 1, 1, 1, 18, + 25, 13, 28, 28, 11, 23, 9, 31, + 6, 29, 29, 34, 9, 10, 95, 86, + 1 + }; + + initRegions(rooms, kRegions); + + loadRegion(1); + _state.room = 5; } Engine *HiRes5Engine_create(OSystem *syst, const AdlGameDescription *gd) { diff --git a/engines/adl/hires6.cpp b/engines/adl/hires6.cpp index 2cf37d4c37..c07493f5bd 100644 --- a/engines/adl/hires6.cpp +++ b/engines/adl/hires6.cpp @@ -68,7 +68,7 @@ public: private: // AdlEngine - void runIntro() const; + void runIntro(); void init(); void initGameState(); void printRoomDescription(); @@ -120,7 +120,7 @@ static Common::MemoryReadStream *loadSectors(DiskImage *disk, byte track, byte s return new Common::MemoryReadStream(buf, bufSize, DisposeAfterUse::YES); } -void HiRes6Engine::runIntro() const { +void HiRes6Engine::runIntro() { DiskImage *boot(new DiskImage()); if (!boot->open(disks[0])) -- cgit v1.2.3