diff options
Diffstat (limited to 'engines/adl')
-rw-r--r-- | engines/adl/adl.cpp | 22 | ||||
-rw-r--r-- | engines/adl/adl.h | 1 | ||||
-rw-r--r-- | engines/adl/adl_v2.cpp | 74 | ||||
-rw-r--r-- | engines/adl/adl_v2.h | 9 | ||||
-rw-r--r-- | engines/adl/adl_v3.cpp | 229 | ||||
-rw-r--r-- | engines/adl/adl_v3.h | 29 | ||||
-rw-r--r-- | engines/adl/adl_v4.cpp | 258 | ||||
-rw-r--r-- | engines/adl/adl_v4.h (renamed from engines/adl/hires2.h) | 63 | ||||
-rw-r--r-- | engines/adl/detection.cpp | 21 | ||||
-rw-r--r-- | engines/adl/detection.h | 1 | ||||
-rw-r--r-- | engines/adl/disk.cpp | 72 | ||||
-rw-r--r-- | engines/adl/disk.h | 20 | ||||
-rw-r--r-- | engines/adl/display.h | 2 | ||||
-rw-r--r-- | engines/adl/hires0.cpp | 311 | ||||
-rw-r--r-- | engines/adl/hires0.h | 59 | ||||
-rw-r--r-- | engines/adl/hires1.cpp | 103 | ||||
-rw-r--r-- | engines/adl/hires1.h | 134 | ||||
-rw-r--r-- | engines/adl/hires2.cpp | 98 | ||||
-rw-r--r-- | engines/adl/hires4.cpp | 271 | ||||
-rw-r--r-- | engines/adl/hires6.cpp | 119 | ||||
-rw-r--r-- | engines/adl/hires6.h | 92 | ||||
-rw-r--r-- | engines/adl/module.mk | 2 |
22 files changed, 1114 insertions, 876 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp index 19595606e1..9afb2c6700 100644 --- a/engines/adl/adl.cpp +++ b/engines/adl/adl.cpp @@ -402,6 +402,15 @@ byte AdlEngine::roomArg(byte room) const { return room; } +void AdlEngine::loadDroppedItemOffsets(Common::ReadStream &stream, byte count) { + for (uint i = 0; i < count; ++i) { + Common::Point p; + p.x = stream.readByte(); + p.y = stream.readByte(); + _itemOffsets.push_back(p); + } +} + void AdlEngine::clearScreen() const { _display->setMode(DISPLAY_MODE_MIXED); _display->clear(0x00); @@ -1263,16 +1272,17 @@ Common::String AdlEngine::toAscii(const Common::String &str) { } Common::String AdlEngine::itemStr(uint i) const { - byte desc = getItem(i).description; - byte noun = getItem(i).noun; + const Item &item(getItem(i)); + Common::String name = Common::String::format("%d", i); - if (noun > 0) { + if (item.noun > 0) { name += "/"; - name += _priNouns[noun - 1]; + name += _priNouns[item.noun - 1]; } - if (desc > 0) { + Common::String desc = getItemDescription(item); + if (!desc.empty()) { name += "/"; - name += toAscii(loadMessage(desc)); + name += toAscii(desc); } return name; } diff --git a/engines/adl/adl.h b/engines/adl/adl.h index 89cdabe384..971336ef50 100644 --- a/engines/adl/adl.h +++ b/engines/adl/adl.h @@ -247,6 +247,7 @@ protected: virtual void initState(); virtual byte roomArg(byte room) const; virtual void advanceClock() { } + void loadDroppedItemOffsets(Common::ReadStream &stream, byte count); // Opcodes int o1_isItemInRoom(ScriptEnv &e); diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp index e18f3339f8..979d794146 100644 --- a/engines/adl/adl_v2.cpp +++ b/engines/adl/adl_v2.cpp @@ -359,9 +359,83 @@ DataBlockPtr AdlEngine_v2::readDataBlockPtr(Common::ReadStream &f) const { if (track == 0 && sector == 0 && offset == 0 && size == 0) return DataBlockPtr(); + adjustDataBlockPtr(track, sector, offset, size); + return _disk->getDataBlock(track, sector, offset, size); } +void AdlEngine_v2::loadItems(Common::ReadStream &stream) { + byte id; + while ((id = stream.readByte()) != 0xff && !stream.eos() && !stream.err()) { + Item item = Item(); + item.id = id; + item.noun = stream.readByte(); + item.room = stream.readByte(); + item.picture = stream.readByte(); + item.isLineArt = stream.readByte(); // Disk number in later games + item.position.x = stream.readByte(); + item.position.y = stream.readByte(); + item.state = stream.readByte(); + item.description = stream.readByte(); + + stream.readByte(); // Struct size + + byte picListSize = stream.readByte(); + + // Flag to keep track of what has been drawn on the screen + stream.readByte(); + + for (uint i = 0; i < picListSize; ++i) + item.roomPictures.push_back(stream.readByte()); + + _state.items.push_back(item); + } + + if (stream.eos() || stream.err()) + error("Error loading items"); +} + +void AdlEngine_v2::loadRooms(Common::ReadStream &stream, byte count) { + for (uint i = 0; i < count; ++i) { + Room room; + + stream.readByte(); // number + for (uint j = 0; j < 6; ++j) + room.connections[j] = stream.readByte(); + room.data = readDataBlockPtr(stream); + room.picture = stream.readByte(); + room.curPicture = stream.readByte(); + room.isFirstTime = stream.readByte(); + + _state.rooms.push_back(room); + } + + if (stream.eos() || stream.err()) + error("Error loading rooms"); +} + +void AdlEngine_v2::loadMessages(Common::ReadStream &stream, byte count) { + for (uint i = 0; i < count; ++i) + _messages.push_back(readDataBlockPtr(stream)); +} + +void AdlEngine_v2::loadPictures(Common::ReadStream &stream) { + byte picNr; + while ((picNr = stream.readByte()) != 0xff) { + if (stream.eos() || stream.err()) + error("Error reading global pic list"); + + _pictures[picNr] = readDataBlockPtr(stream); + } +} + +void AdlEngine_v2::loadItemPictures(Common::ReadStream &stream, byte count) { + for (uint i = 0; i < count; ++i) { + stream.readByte(); // number + _itemPics.push_back(readDataBlockPtr(stream)); + } +} + int AdlEngine_v2::o2_isFirstTime(ScriptEnv &e) { OP_DEBUG_0("\t&& IS_FIRST_TIME()"); diff --git a/engines/adl/adl_v2.h b/engines/adl/adl_v2.h index 4a473e9b1f..8f36b5cdb8 100644 --- a/engines/adl/adl_v2.h +++ b/engines/adl/adl_v2.h @@ -25,9 +25,6 @@ #include "adl/adl.h" -// Note: this version of ADL redraws only when necessary, but -// this is not currently implemented. - namespace Common { class RandomSource; } @@ -55,6 +52,12 @@ protected: void takeItem(byte noun); virtual DataBlockPtr readDataBlockPtr(Common::ReadStream &f) const; + virtual void adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const { } + void loadItems(Common::ReadStream &stream); + void loadRooms(Common::ReadStream &stream, byte count); + void loadMessages(Common::ReadStream &stream, byte count); + void loadPictures(Common::ReadStream &stream); + void loadItemPictures(Common::ReadStream &stream, byte count); void checkTextOverflow(char c); diff --git a/engines/adl/adl_v3.cpp b/engines/adl/adl_v3.cpp index 005478c376..ba9e4a063e 100644 --- a/engines/adl/adl_v3.cpp +++ b/engines/adl/adl_v3.cpp @@ -20,162 +20,50 @@ * */ -#include "common/random.h" -#include "common/error.h" - #include "adl/adl_v3.h" -#include "adl/display.h" -#include "adl/graphics.h" namespace Adl { AdlEngine_v3::AdlEngine_v3(OSystem *syst, const AdlGameDescription *gd) : - AdlEngine_v2(syst, gd), - _curDisk(0) { -} - -Common::String AdlEngine_v3::loadMessage(uint idx) const { - Common::String str = AdlEngine_v2::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; + AdlEngine_v2(syst, gd) { } Common::String AdlEngine_v3::getItemDescription(const Item &item) const { - return _itemDesc[item.id - 1]; + return _itemDesc[item.description - 1]; } -void AdlEngine_v3::applyDiskOffset(byte &track, byte §or) const { - sector += _diskOffsets[_curDisk].sector; - if (sector >= 16) { - sector -= 16; - ++track; - } +void AdlEngine_v3::loadItemDescriptions(Common::SeekableReadStream &stream, byte count) { + int32 startPos = stream.pos(); + uint16 baseAddr = stream.readUint16LE(); - track += _diskOffsets[_curDisk].track; -} - -DataBlockPtr AdlEngine_v3::readDataBlockPtr(Common::ReadStream &f) const { - byte track = f.readByte(); - byte sector = f.readByte(); - byte offset = f.readByte(); - byte size = f.readByte(); - - if (f.eos() || f.err()) - error("Error reading DataBlockPtr"); + // This code assumes that the first pointer points to a string that + // directly follows the pointer table + assert(baseAddr != 0); + baseAddr -= count * 2; - if (track == 0 && sector == 0 && offset == 0 && size == 0) - return DataBlockPtr(); + for (uint i = 0; i < count; ++i) { + stream.seek(startPos + i * 2); + uint16 offset = stream.readUint16LE(); - applyDiskOffset(track, sector); + if (offset > 0) { + stream.seek(startPos + offset - baseAddr); + _itemDesc.push_back(readString(stream, 0xff)); + } else + _itemDesc.push_back(Common::String()); + } - return _disk->getDataBlock(track, sector, offset, size); + if (stream.eos() || stream.err()) + error("Error loading item descriptions"); } typedef Common::Functor1Mem<ScriptEnv &, int, AdlEngine_v3> OpcodeV3; -#define SetOpcodeTable(x) table = &x; -#define Opcode(x) table->push_back(new OpcodeV3(this, &AdlEngine_v3::x)) -#define OpcodeUnImpl() table->push_back(new OpcodeV3(this, 0)) void AdlEngine_v3::setupOpcodeTables() { - Common::Array<const Opcode *> *table = 0; - - SetOpcodeTable(_condOpcodes); - // 0x00 - OpcodeUnImpl(); - Opcode(o2_isFirstTime); - Opcode(o2_isRandomGT); - Opcode(o3_isItemInRoom); - // 0x04 - Opcode(o3_isNounNotInRoom); - Opcode(o1_isMovesGT); - Opcode(o1_isVarEQ); - Opcode(o2_isCarryingSomething); - // 0x08 - Opcode(o3_isVarGT); - Opcode(o1_isCurPicEQ); - Opcode(o3_skipOneCommand); - - SetOpcodeTable(_actOpcodes); - // 0x00 - OpcodeUnImpl(); - Opcode(o1_varAdd); - Opcode(o1_varSub); - Opcode(o1_varSet); - // 0x04 - Opcode(o1_listInv); - Opcode(o3_moveItem); - Opcode(o1_setRoom); - Opcode(o2_setCurPic); - // 0x08 - Opcode(o2_setPic); - Opcode(o1_printMsg); - Opcode(o3_dummy); - Opcode(o3_setTextMode); - // 0x0c - Opcode(o2_moveAllItems); - Opcode(o1_quit); - Opcode(o3_dummy); - Opcode(o2_save); - // 0x10 - Opcode(o2_restore); - Opcode(o1_restart); - Opcode(o3_setDisk); - Opcode(o3_dummy); - // 0x14 - Opcode(o1_resetPic); - Opcode(o1_goDirection<IDI_DIR_NORTH>); - Opcode(o1_goDirection<IDI_DIR_SOUTH>); - Opcode(o1_goDirection<IDI_DIR_EAST>); - // 0x18 - Opcode(o1_goDirection<IDI_DIR_WEST>); - Opcode(o1_goDirection<IDI_DIR_UP>); - Opcode(o1_goDirection<IDI_DIR_DOWN>); - Opcode(o1_takeItem); - // 0x1c - Opcode(o1_dropItem); - Opcode(o1_setRoomPic); - Opcode(o3_sound); - OpcodeUnImpl(); - // 0x20 - Opcode(o2_initDisk); -} - -int AdlEngine_v3::o3_isVarGT(ScriptEnv &e) { - OP_DEBUG_2("\t&& VARS[%d] > %d", e.arg(1), e.arg(2)); - - if (getVar(e.arg(1)) > e.arg(2)) - return 2; - - return -1; -} - -int AdlEngine_v3::o3_skipOneCommand(ScriptEnv &e) { - OP_DEBUG_0("\t&& SKIP_ONE_COMMAND()"); - - _skipOneCommand = true; - setVar(2, 0); - - return -1; -} - -// FIXME: Rename "isLineArt" and look at code duplication -int AdlEngine_v3::o3_isItemInRoom(ScriptEnv &e) { - OP_DEBUG_2("\t&& GET_ITEM_ROOM(%s) == %s", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); - - const Item &item = getItem(e.arg(1)); - - if (e.arg(2) != IDI_ANY && item.isLineArt != _curDisk) - return -1; - - if (item.room == roomArg(e.arg(2))) - return 2; - - return -1; + AdlEngine_v2::setupOpcodeTables(); + delete _condOpcodes[0x04]; + _condOpcodes[0x04] = new OpcodeV3(this, &AdlEngine_v3::o3_isNounNotInRoom); + delete _actOpcodes[0x04]; + _actOpcodes[0x04] = new OpcodeV3(this, &AdlEngine_v3::o3_listInv); } int AdlEngine_v3::o3_isNounNotInRoom(ScriptEnv &e) { @@ -183,75 +71,28 @@ int AdlEngine_v3::o3_isNounNotInRoom(ScriptEnv &e) { Common::List<Item>::const_iterator item; - setVar(24, 0); + bool isAnItem = false; - for (item = _state.items.begin(); item != _state.items.end(); ++item) + for (item = _state.items.begin(); item != _state.items.end(); ++item) { if (item->noun == e.getNoun()) { - setVar(24, 1); + isAnItem = true; if (item->room == roomArg(e.arg(1))) return -1; } - - return 1; -} - -int AdlEngine_v3::o3_moveItem(ScriptEnv &e) { - OP_DEBUG_2("\tSET_ITEM_ROOM(%s, %s)", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); - - byte room = roomArg(e.arg(2)); - - Item &item = getItem(e.arg(1)); - - if (item.room == _roomOnScreen) - _picOnScreen = 0; - - // Set items that move from inventory to a room to state "dropped" - if (item.room == IDI_ANY && room != IDI_VOID_ROOM) - item.state = IDI_ITEM_DROPPED; - - item.room = room; - item.isLineArt = _curDisk; - return 2; -} - -int AdlEngine_v3::o3_dummy(ScriptEnv &e) { - OP_DEBUG_0("\tDUMMY()"); - - return 0; -} - -int AdlEngine_v3::o3_setTextMode(ScriptEnv &e) { - OP_DEBUG_1("\tSET_TEXT_MODE(%d)", e.arg(1)); - - // TODO - // 1: 4-line mode - // 2: 24-line mode - - switch (e.arg(1)) { - case 3: - // We re-use the restarting flag here, to simulate a long jump - _isRestarting = true; - return -1; } - return 1; + return (isAnItem ? 1 : -1); } -int AdlEngine_v3::o3_setDisk(ScriptEnv &e) { - OP_DEBUG_2("\tSET_DISK(%d, %d)", e.arg(1), e.arg(2)); - - // TODO - // Arg 1: disk - // Arg 2: room - - return 2; -} +int AdlEngine_v3::o3_listInv(ScriptEnv &e) { + OP_DEBUG_0("\tLIST_INVENTORY()"); -int AdlEngine_v3::o3_sound(ScriptEnv &e) { - OP_DEBUG_0("\tSOUND()"); + Common::List<Item>::const_iterator item; - // TODO + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->room == IDI_ANY) + printString(_itemDesc[item->description - 1]); return 0; } diff --git a/engines/adl/adl_v3.h b/engines/adl/adl_v3.h index 61dd5852e7..b0d40f3993 100644 --- a/engines/adl/adl_v3.h +++ b/engines/adl/adl_v3.h @@ -25,18 +25,6 @@ #include "adl/adl_v2.h" -// Note: this version of ADL redraws only when necessary, but -// this is not currently implemented. - -namespace Common { -class RandomSource; -} - -struct DiskOffset { - byte track; - byte sector; -}; - namespace Adl { class AdlEngine_v3 : public AdlEngine_v2 { @@ -48,27 +36,14 @@ protected: // AdlEngine virtual void setupOpcodeTables(); - virtual Common::String loadMessage(uint idx) const; Common::String getItemDescription(const Item &item) const; - // AdlEngine_v2 - virtual DataBlockPtr readDataBlockPtr(Common::ReadStream &f) const; - - void applyDiskOffset(byte &track, byte §or) const; + void loadItemDescriptions(Common::SeekableReadStream &stream, byte count); - int o3_isVarGT(ScriptEnv &e); - int o3_isItemInRoom(ScriptEnv &e); int o3_isNounNotInRoom(ScriptEnv &e); - int o3_skipOneCommand(ScriptEnv &e); - int o3_moveItem(ScriptEnv &e); - int o3_dummy(ScriptEnv &e); - int o3_setTextMode(ScriptEnv &e); - int o3_setDisk(ScriptEnv &e); - int o3_sound(ScriptEnv &e); + int o3_listInv(ScriptEnv &e); Common::Array<Common::String> _itemDesc; - byte _curDisk; - Common::Array<DiskOffset> _diskOffsets; }; } // End of namespace Adl diff --git a/engines/adl/adl_v4.cpp b/engines/adl/adl_v4.cpp new file mode 100644 index 0000000000..ed20c82513 --- /dev/null +++ b/engines/adl/adl_v4.cpp @@ -0,0 +1,258 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#include "common/random.h" +#include "common/error.h" + +#include "adl/adl_v4.h" +#include "adl/display.h" +#include "adl/graphics.h" + +namespace Adl { + +AdlEngine_v4::AdlEngine_v4(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine_v3(syst, gd), + _curDisk(0) { +} + +Common::String AdlEngine_v4::loadMessage(uint idx) const { + Common::String str = AdlEngine_v2::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]; +} + +void AdlEngine_v4::applyDiskOffset(byte &track, byte §or) const { + sector += _diskOffsets[_curDisk].sector; + if (sector >= 16) { + sector -= 16; + ++track; + } + + track += _diskOffsets[_curDisk].track; +} + +void AdlEngine_v4::adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const { + applyDiskOffset(track, sector); +} + +typedef Common::Functor1Mem<ScriptEnv &, int, AdlEngine_v4> OpcodeV4; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV4(this, &AdlEngine_v4::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV4(this, 0)) + +void AdlEngine_v4::setupOpcodeTables() { + Common::Array<const Opcode *> *table = 0; + + SetOpcodeTable(_condOpcodes); + // 0x00 + OpcodeUnImpl(); + Opcode(o2_isFirstTime); + Opcode(o2_isRandomGT); + Opcode(o4_isItemInRoom); + // 0x04 + Opcode(o4_isNounNotInRoom); + Opcode(o1_isMovesGT); + Opcode(o1_isVarEQ); + Opcode(o2_isCarryingSomething); + // 0x08 + Opcode(o4_isVarGT); + Opcode(o1_isCurPicEQ); + Opcode(o4_skipOneCommand); + + SetOpcodeTable(_actOpcodes); + // 0x00 + OpcodeUnImpl(); + Opcode(o1_varAdd); + Opcode(o1_varSub); + Opcode(o1_varSet); + // 0x04 + Opcode(o4_listInv); + Opcode(o4_moveItem); + Opcode(o1_setRoom); + Opcode(o2_setCurPic); + // 0x08 + Opcode(o2_setPic); + Opcode(o1_printMsg); + Opcode(o4_dummy); + Opcode(o4_setTextMode); + // 0x0c + Opcode(o2_moveAllItems); + Opcode(o1_quit); + Opcode(o4_dummy); + Opcode(o2_save); + // 0x10 + Opcode(o2_restore); + Opcode(o1_restart); + Opcode(o4_setDisk); + Opcode(o4_dummy); + // 0x14 + Opcode(o1_resetPic); + Opcode(o1_goDirection<IDI_DIR_NORTH>); + Opcode(o1_goDirection<IDI_DIR_SOUTH>); + Opcode(o1_goDirection<IDI_DIR_EAST>); + // 0x18 + Opcode(o1_goDirection<IDI_DIR_WEST>); + Opcode(o1_goDirection<IDI_DIR_UP>); + Opcode(o1_goDirection<IDI_DIR_DOWN>); + Opcode(o1_takeItem); + // 0x1c + Opcode(o1_dropItem); + Opcode(o1_setRoomPic); + Opcode(o4_sound); + OpcodeUnImpl(); + // 0x20 + Opcode(o2_initDisk); +} + +int AdlEngine_v4::o4_isVarGT(ScriptEnv &e) { + OP_DEBUG_2("\t&& VARS[%d] > %d", e.arg(1), e.arg(2)); + + if (getVar(e.arg(1)) > e.arg(2)) + return 2; + + return -1; +} + +int AdlEngine_v4::o4_skipOneCommand(ScriptEnv &e) { + OP_DEBUG_0("\t&& SKIP_ONE_COMMAND()"); + + _skipOneCommand = true; + setVar(2, 0); + + return -1; +} + +// FIXME: Rename "isLineArt" and look at code duplication +int AdlEngine_v4::o4_isItemInRoom(ScriptEnv &e) { + OP_DEBUG_2("\t&& GET_ITEM_ROOM(%s) == %s", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); + + const Item &item = getItem(e.arg(1)); + + if (e.arg(2) != IDI_ANY && item.isLineArt != _curDisk) + return -1; + + if (item.room == roomArg(e.arg(2))) + return 2; + + return -1; +} + +int AdlEngine_v4::o4_isNounNotInRoom(ScriptEnv &e) { + OP_DEBUG_1("\t&& NO_SUCH_ITEMS_IN_ROOM(%s)", itemRoomStr(e.arg(1)).c_str()); + + Common::List<Item>::const_iterator item; + + setVar(24, 0); + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->noun == e.getNoun()) { + setVar(24, 1); + + if (item->room == roomArg(e.arg(1))) + return -1; + } + + return 1; +} + +int AdlEngine_v4::o4_listInv(ScriptEnv &e) { + OP_DEBUG_0("\tLIST_INVENTORY()"); + + Common::List<Item>::const_iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->room == IDI_ANY) + printString(_itemDesc[item->id - 1]); + + return 0; +} + +int AdlEngine_v4::o4_moveItem(ScriptEnv &e) { + OP_DEBUG_2("\tSET_ITEM_ROOM(%s, %s)", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); + + byte room = roomArg(e.arg(2)); + + Item &item = getItem(e.arg(1)); + + if (item.room == _roomOnScreen) + _picOnScreen = 0; + + // Set items that move from inventory to a room to state "dropped" + if (item.room == IDI_ANY && room != IDI_VOID_ROOM) + item.state = IDI_ITEM_DROPPED; + + item.room = room; + item.isLineArt = _curDisk; + return 2; +} + +int AdlEngine_v4::o4_dummy(ScriptEnv &e) { + OP_DEBUG_0("\tDUMMY()"); + + return 0; +} + +int AdlEngine_v4::o4_setTextMode(ScriptEnv &e) { + OP_DEBUG_1("\tSET_TEXT_MODE(%d)", e.arg(1)); + + // TODO + // 1: 4-line mode + // 2: 24-line mode + + switch (e.arg(1)) { + case 3: + // We re-use the restarting flag here, to simulate a long jump + _isRestarting = true; + return -1; + } + + return 1; +} + +int AdlEngine_v4::o4_setDisk(ScriptEnv &e) { + OP_DEBUG_2("\tSET_DISK(%d, %d)", e.arg(1), e.arg(2)); + + // TODO + // Arg 1: disk + // Arg 2: room + + return 2; +} + +int AdlEngine_v4::o4_sound(ScriptEnv &e) { + OP_DEBUG_0("\tSOUND()"); + + // TODO + + return 0; +} + +} // End of namespace Adl diff --git a/engines/adl/hires2.h b/engines/adl/adl_v4.h index 50016725d6..79aa824d92 100644 --- a/engines/adl/hires2.h +++ b/engines/adl/adl_v4.h @@ -20,45 +20,52 @@ * */ -#ifndef ADL_HIRES2_H -#define ADL_HIRES2_H +#ifndef ADL_ADL_V4_H +#define ADL_ADL_V4_H -#include "common/str.h" - -#include "adl/adl_v2.h" -#include "adl/disk.h" +#include "adl/adl_v3.h" namespace Common { -class ReadStream; -struct Point; +class RandomSource; } +struct DiskOffset { + byte track; + byte sector; +}; + namespace Adl { -#define IDS_HR2_DISK_IMAGE "WIZARD.DSK" +class AdlEngine_v4 : public AdlEngine_v3 { +public: + virtual ~AdlEngine_v4() { } -#define IDI_HR2_NUM_ROOMS 135 -#define IDI_HR2_NUM_MESSAGES 255 -#define IDI_HR2_NUM_VARS 40 -#define IDI_HR2_NUM_ITEM_PICS 38 -#define IDI_HR2_NUM_ITEM_OFFSETS 16 +protected: + AdlEngine_v4(OSystem *syst, const AdlGameDescription *gd); -// Messages used outside of scripts -#define IDI_HR2_MSG_CANT_GO_THERE 123 -#define IDI_HR2_MSG_DONT_UNDERSTAND 19 -#define IDI_HR2_MSG_ITEM_DOESNT_MOVE 242 -#define IDI_HR2_MSG_ITEM_NOT_HERE 4 -#define IDI_HR2_MSG_THANKS_FOR_PLAYING 239 + // AdlEngine + virtual void setupOpcodeTables(); + virtual Common::String loadMessage(uint idx) const; + Common::String getItemDescription(const Item &item) const; -class HiRes2Engine : public AdlEngine_v2 { -public: - HiRes2Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine_v2(syst, gd) { } + // AdlEngine_v2 + virtual void adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const; -private: - // AdlEngine - void runIntro() const; - void init(); - void initGameState(); + void applyDiskOffset(byte &track, byte §or) const; + + int o4_isVarGT(ScriptEnv &e); + int o4_isItemInRoom(ScriptEnv &e); + int o4_isNounNotInRoom(ScriptEnv &e); + int o4_skipOneCommand(ScriptEnv &e); + int o4_listInv(ScriptEnv &e); + int o4_moveItem(ScriptEnv &e); + int o4_dummy(ScriptEnv &e); + int o4_setTextMode(ScriptEnv &e); + int o4_setDisk(ScriptEnv &e); + int o4_sound(ScriptEnv &e); + + byte _curDisk; + Common::Array<DiskOffset> _diskOffsets; }; } // End of namespace Adl diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp index 200e0b592a..10812d79ea 100644 --- a/engines/adl/detection.cpp +++ b/engines/adl/detection.cpp @@ -76,6 +76,7 @@ static const PlainGameDescriptor adlGames[] = { { "hires0", "Hi-Res Adventure #0: Mission Asteroid" }, { "hires1", "Hi-Res Adventure #1: Mystery House" }, { "hires2", "Hi-Res Adventure #2: Wizard and the Princess" }, + { "hires4", "Hi-Res Adventure #4: Ulysses and the Golden Fleece" }, { "hires6", "Hi-Res Adventure #6: The Dark Crystal" }, { 0, 0 } }; @@ -139,6 +140,21 @@ static const AdlGameDescription gameDescriptions[] = { }, GAME_TYPE_HIRES0 }, + { // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Atari 8-bit - Re-release + { + "hires4", 0, + { + { "ULYS1A.XFD", 0, "26365d2b06509fd21e7a7919e33f7199", 92160 }, + // FIXME: Add sides 1B and 2C + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformAtariST, // FIXME + ADGF_UNSTABLE, + GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES) + }, + GAME_TYPE_HIRES4 + }, { // Hi-Res Adventure #6: The Dark Crystal - Apple II - Roberta Williams Anthology { "hires6", 0, @@ -189,6 +205,7 @@ bool AdlMetaEngine::hasFeature(MetaEngineFeature f) const { case kSavesSupportThumbnail: case kSavesSupportCreationDate: case kSavesSupportPlayTime: + case kSimpleSavesNames: return true; default: return false; @@ -297,6 +314,7 @@ void AdlMetaEngine::removeSaveState(const char *target, int slot) const { Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd); Engine *HiRes2Engine_create(OSystem *syst, const AdlGameDescription *gd); Engine *HiRes0Engine_create(OSystem *syst, const AdlGameDescription *gd); +Engine *HiRes4Engine_create(OSystem *syst, const AdlGameDescription *gd); Engine *HiRes6Engine_create(OSystem *syst, const AdlGameDescription *gd); bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const { @@ -315,6 +333,9 @@ bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD case GAME_TYPE_HIRES0: *engine = HiRes0Engine_create(syst, adlGd); break; + case GAME_TYPE_HIRES4: + *engine = HiRes4Engine_create(syst, adlGd); + break; case GAME_TYPE_HIRES6: *engine = HiRes6Engine_create(syst, adlGd); break; diff --git a/engines/adl/detection.h b/engines/adl/detection.h index 533466c094..b4dc3c430f 100644 --- a/engines/adl/detection.h +++ b/engines/adl/detection.h @@ -35,6 +35,7 @@ enum GameType { GAME_TYPE_HIRES0, GAME_TYPE_HIRES1, GAME_TYPE_HIRES2, + GAME_TYPE_HIRES4, GAME_TYPE_HIRES6 }; diff --git a/engines/adl/disk.cpp b/engines/adl/disk.cpp index 49e01f9d0f..d429556670 100644 --- a/engines/adl/disk.cpp +++ b/engines/adl/disk.cpp @@ -28,15 +28,7 @@ namespace Adl { -#define TRACKS 35 -// The Apple II uses either 13- or 16-sector disks. We currently pad out -// 13-sector disks, so we set SECTORS_PER_TRACK to 16 here. -#define SECTORS_PER_TRACK 16 -#define BYTES_PER_SECTOR 256 -#define RAW_IMAGE_SIZE(S) (TRACKS * (S) * BYTES_PER_SECTOR) -#define NIB_IMAGE_SIZE (RAW_IMAGE_SIZE(13) * 2) - -static Common::SeekableReadStream *readImage_DSK(const Common::String &filename) { +static Common::SeekableReadStream *readImage(const Common::String &filename) { Common::File *f = new Common::File; if (!f->open(filename)) { @@ -44,9 +36,6 @@ static Common::SeekableReadStream *readImage_DSK(const Common::String &filename) return nullptr; } - if (f->size() != RAW_IMAGE_SIZE(16)) - error("Unrecognized DSK image '%s' of size %d bytes", filename.c_str(), f->size()); - return f; } @@ -63,7 +52,7 @@ static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) if (!f.open(filename)) return nullptr; - if (f.size() != NIB_IMAGE_SIZE) + if (f.size() != 232960) error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), f.size()); // starting at 0xaa, 32 is invalid (see below) @@ -73,7 +62,9 @@ static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) // we always pad it out const uint sectorsPerTrack = 16; - byte *diskImage = (byte *)calloc(RAW_IMAGE_SIZE(sectorsPerTrack), 1); + const uint bytesPerSector = 256; + const uint imageSize = 35 * sectorsPerTrack * bytesPerSector; + byte *const diskImage = (byte *)calloc(imageSize, 1); bool sawAddress = false; uint8 volNo, track, sector; @@ -120,13 +111,13 @@ static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) // We should always find the data field after an address field. // TODO: we ignore volNo? - byte *output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; + byte *output = diskImage + (track * sectorsPerTrack + sector) * bytesPerSector; if (newStyle) { // We hardcode the DOS 3.3 mapping here. TODO: Do we also need raw/prodos? int raw2dos[16] = { 0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15 }; sector = raw2dos[sector]; - output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; + output = diskImage + (track * sectorsPerTrack + sector) * bytesPerSector; // 6-and-2 uses 342 on-disk bytes byte inbuffer[342]; @@ -216,42 +207,65 @@ static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) } } - return new Common::MemoryReadStream(diskImage, RAW_IMAGE_SIZE(sectorsPerTrack), DisposeAfterUse::YES); + return new Common::MemoryReadStream(diskImage, imageSize, DisposeAfterUse::YES); } bool DiskImage::open(const Common::String &filename) { Common::String lcName(filename); lcName.toLowercase(); - if (lcName.hasSuffix(".dsk")) - _stream = readImage_DSK(filename); - else if (lcName.hasSuffix(".nib")) + if (lcName.hasSuffix(".dsk")) { + _stream = readImage(filename); + _tracks = 35; + _sectorsPerTrack = 16; + _bytesPerSector = 256; + } else if (lcName.hasSuffix(".nib")) { _stream = readImage_NIB(filename); + _tracks = 35; + _sectorsPerTrack = 16; + _bytesPerSector = 256; + } else if (lcName.hasSuffix(".xfd")) { + _stream = readImage(filename); + _tracks = 40; + _sectorsPerTrack = 18; + _bytesPerSector = 128; + } + + int expectedSize = _tracks * _sectorsPerTrack * _bytesPerSector; + + if (!_stream) + return false; + + if (_stream->size() != expectedSize) + error("Unrecognized disk image '%s' of size %d bytes (expected %d bytes)", filename.c_str(), _stream->size(), expectedSize); - return _stream != nullptr; + return true; } const DataBlockPtr DiskImage::getDataBlock(uint track, uint sector, uint offset, uint size) const { - return DataBlockPtr(new DiskImage::DataBlock(this, track, sector, offset, size, _mode13)); + return DataBlockPtr(new DiskImage::DataBlock(this, track, sector, offset, size, _sectorLimit)); } -Common::SeekableReadStream *DiskImage::createReadStream(uint track, uint sector, uint offset, uint size, uint sectorsPerTrackToRead) const { - const uint bytesToRead = size * BYTES_PER_SECTOR + BYTES_PER_SECTOR - offset; +Common::SeekableReadStream *DiskImage::createReadStream(uint track, uint sector, uint offset, uint size, uint sectorLimit) const { + const uint bytesToRead = size * _bytesPerSector + _bytesPerSector - offset; byte *const data = (byte *)malloc(bytesToRead); uint dataOffset = 0; - if (sector > sectorsPerTrackToRead - 1) - error("Sector %i is out of bounds for %i-sector reading", sector, sectorsPerTrackToRead); + if (sectorLimit == 0) + sectorLimit = _sectorsPerTrack; + + if (sector >= sectorLimit) + error("Sector %i is out of bounds for %i-sector reading", sector, sectorLimit); while (dataOffset < bytesToRead) { - uint bytesRemInTrack = (sectorsPerTrackToRead - 1 - sector) * BYTES_PER_SECTOR + BYTES_PER_SECTOR - offset; - _stream->seek((track * SECTORS_PER_TRACK + sector) * BYTES_PER_SECTOR + offset); + uint bytesRemInTrack = (sectorLimit - 1 - sector) * _bytesPerSector + _bytesPerSector - offset; + _stream->seek((track * _sectorsPerTrack + sector) * _bytesPerSector + offset); if (bytesToRead - dataOffset < bytesRemInTrack) bytesRemInTrack = bytesToRead - dataOffset; if (_stream->read(data + dataOffset, bytesRemInTrack) < bytesRemInTrack) - error("Error reading disk image"); + error("Error reading disk image at track %d; sector %d", track, sector); ++track; diff --git a/engines/adl/disk.h b/engines/adl/disk.h index 1041f0cebd..653d76ff10 100644 --- a/engines/adl/disk.h +++ b/engines/adl/disk.h @@ -74,7 +74,10 @@ class DiskImage { public: DiskImage() : _stream(nullptr), - _mode13(false) { } + _tracks(0), + _sectorsPerTrack(0), + _bytesPerSector(0), + _sectorLimit(0) { } ~DiskImage() { delete _stream; @@ -82,32 +85,33 @@ public: bool open(const Common::String &filename); const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const; - Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0, uint sectorsPerTrackToRead = 16) const; - void setMode13(bool enable) { _mode13 = enable; } + Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0, uint sectorsUsed = 0) const; + void setSectorLimit(uint sectorLimit) { _sectorLimit = sectorLimit; } // Maximum number of sectors to read per track before stepping protected: class DataBlock : public Adl::DataBlock { public: - DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size, bool mode13) : + DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size, uint sectorLimit) : _track(track), _sector(sector), _offset(offset), _size(size), - _mode13(mode13), + _sectorLimit(sectorLimit), _disk(disk) { } Common::SeekableReadStream *createReadStream() const { - return _disk->createReadStream(_track, _sector, _offset, _size, (_mode13 ? 13 : 16)); + return _disk->createReadStream(_track, _sector, _offset, _size, _sectorLimit); } private: uint _track, _sector, _offset, _size; - bool _mode13; + uint _sectorLimit; const DiskImage *_disk; }; Common::SeekableReadStream *_stream; - bool _mode13; // Older 13-sector format + uint _tracks, _sectorsPerTrack, _bytesPerSector; + uint _sectorLimit; }; // Data in plain files diff --git a/engines/adl/display.h b/engines/adl/display.h index e761e63f2e..311a05893c 100644 --- a/engines/adl/display.h +++ b/engines/adl/display.h @@ -23,7 +23,7 @@ #ifndef ADL_DISPLAY_H #define ADL_DISPLAY_H -#include <common/types.h> +#include "common/types.h" namespace Common { class ReadStream; diff --git a/engines/adl/hires0.cpp b/engines/adl/hires0.cpp index 316f77a9b6..9a0af05d20 100644 --- a/engines/adl/hires0.cpp +++ b/engines/adl/hires0.cpp @@ -1,168 +1,143 @@ -/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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.
- *
- */
-
-#include "common/textconsole.h"
-
-#include "adl/hires0.h"
-#include "adl/graphics.h"
-#include "adl/disk.h"
-
-namespace Adl {
-
-void HiRes0Engine::init() {
- _graphics = new Graphics_v2(*_display);
-
- _disk = new DiskImage();
- if (!_disk->open(IDS_HR0_DISK_IMAGE))
- error("Failed to open disk image '" IDS_HR0_DISK_IMAGE "'");
-
- _disk->setMode13(true);
-
- StreamPtr stream(_disk->createReadStream(0x1f, 0x2, 0x00, 2));
-
- // TODO: all these strings/offsets/etc are the same as hires2
-
- for (uint i = 0; i < IDI_HR0_NUM_MESSAGES; ++i)
- _messages.push_back(readDataBlockPtr(*stream));
-
- // Read parser messages
- stream.reset(_disk->createReadStream(0x1a, 0x1));
- _strings.verbError = readStringAt(*stream, 0x4f);
- _strings.nounError = readStringAt(*stream, 0x8e);
- _strings.enterCommand = readStringAt(*stream, 0xbc);
-
- // Read time string
- stream.reset(_disk->createReadStream(0x19, 0x7, 0xd7));
- _strings_v2.time = readString(*stream, 0xff);
-
- // Read line feeds
- stream.reset(_disk->createReadStream(0x19, 0xb, 0xf8, 1));
- _strings.lineFeeds = readString(*stream);
-
- // Read opcode strings
- stream.reset(_disk->createReadStream(0x1a, 0x6, 0x00, 2));
- _strings_v2.saveInsert = readStringAt(*stream, 0x5f);
- _strings_v2.saveReplace = readStringAt(*stream, 0xe5);
- _strings_v2.restoreInsert = readStringAt(*stream, 0x132);
- _strings_v2.restoreReplace = readStringAt(*stream, 0x1c2);
- _strings.playAgain = readStringAt(*stream, 0x225);
- _strings.pressReturn = readStringAt(*stream, 0x25f);
-
- _messageIds.cantGoThere = IDI_HR0_MSG_CANT_GO_THERE;
- _messageIds.dontUnderstand = IDI_HR0_MSG_DONT_UNDERSTAND;
- _messageIds.itemDoesntMove = IDI_HR0_MSG_ITEM_DOESNT_MOVE;
- _messageIds.itemNotHere = IDI_HR0_MSG_ITEM_NOT_HERE;
- _messageIds.thanksForPlaying = IDI_HR0_MSG_THANKS_FOR_PLAYING;
-
- // Load global picture data
- stream.reset(_disk->createReadStream(0x19, 0xa, 0x80, 0));
- byte picNr;
- while ((picNr = stream->readByte()) != 0xff) {
- if (stream->eos() || stream->err())
- error("Error reading global pic list");
-
- _pictures[picNr] = readDataBlockPtr(*stream);
- }
-
- // Load item picture data
- stream.reset(_disk->createReadStream(0x1e, 0x9, 0x05));
- for (uint i = 0; i < IDI_HR0_NUM_ITEM_PICS; ++i) {
- stream->readByte(); // number
- _itemPics.push_back(readDataBlockPtr(*stream));
- }
-
- // Load commands from executable
- stream.reset(_disk->createReadStream(0x1d, 0x7, 0x00, 2));
- readCommands(*stream, _roomCommands);
-
- stream.reset(_disk->createReadStream(0x1f, 0x7, 0x00, 3));
- readCommands(*stream, _globalCommands);
-
- // Load dropped item offsets
- stream.reset(_disk->createReadStream(0x1b, 0x4, 0x15));
- for (uint i = 0; i < IDI_HR0_NUM_ITEM_OFFSETS; ++i) {
- Common::Point p;
- p.x = stream->readByte();
- p.y = stream->readByte();
- _itemOffsets.push_back(p);
- }
-
- // Load verbs
- stream.reset(_disk->createReadStream(0x19, 0x0, 0x00, 3));
- loadWords(*stream, _verbs, _priVerbs);
-
- // Load nouns
- stream.reset(_disk->createReadStream(0x22, 0x2, 0x00, 2));
- loadWords(*stream, _nouns, _priNouns);
-}
-
-void HiRes0Engine::initGameState() {
- _state.vars.resize(IDI_HR0_NUM_VARS);
-
- StreamPtr stream(_disk->createReadStream(0x21, 0x5, 0x0e, 2));
-
- for (uint i = 0; i < IDI_HR0_NUM_ROOMS; ++i) {
- Room room;
- stream->readByte(); // number
- for (uint j = 0; j < 6; ++j)
- room.connections[j] = stream->readByte();
- room.data = readDataBlockPtr(*stream);
- room.picture = stream->readByte();
- room.curPicture = stream->readByte();
- room.isFirstTime = stream->readByte();
- _state.rooms.push_back(room);
- }
-
- stream.reset(_disk->createReadStream(0x21, 0x0));
-
- byte id;
- while ((id = stream->readByte()) != 0xff) {
- Item item = Item();
- item.id = id;
- item.noun = stream->readByte();
- item.room = stream->readByte();
- item.picture = stream->readByte();
- item.isLineArt = stream->readByte();
- item.position.x = stream->readByte();
- item.position.y = stream->readByte();
- item.state = stream->readByte();
- item.description = stream->readByte();
-
- stream->readByte(); // Struct size
-
- byte picListSize = stream->readByte();
-
- // Flag to keep track of what has been drawn on the screen
- stream->readByte();
-
- for (uint i = 0; i < picListSize; ++i)
- item.roomPictures.push_back(stream->readByte());
-
- _state.items.push_back(item);
- }
-}
-
-Engine *HiRes0Engine_create(OSystem *syst, const AdlGameDescription *gd) {
- return new HiRes0Engine(syst, gd);
-}
-
-} // End of namespace Adl
+/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#include "common/textconsole.h" + +#include "adl/adl_v2.h" +#include "adl/graphics.h" +#include "adl/disk.h" + +namespace Adl { + +#define IDS_HR0_DISK_IMAGE "MISSION.NIB" + +#define IDI_HR0_NUM_ROOMS 43 +#define IDI_HR0_NUM_MESSAGES 142 +#define IDI_HR0_NUM_VARS 40 +#define IDI_HR0_NUM_ITEM_PICS 2 +#define IDI_HR0_NUM_ITEM_OFFSETS 16 + +// Messages used outside of scripts +#define IDI_HR0_MSG_CANT_GO_THERE 110 +#define IDI_HR0_MSG_DONT_UNDERSTAND 112 +#define IDI_HR0_MSG_ITEM_DOESNT_MOVE 114 +#define IDI_HR0_MSG_ITEM_NOT_HERE 115 +#define IDI_HR0_MSG_THANKS_FOR_PLAYING 113 + +class HiRes0Engine : public AdlEngine_v2 { +public: + HiRes0Engine(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine_v2(syst, gd) { } + ~HiRes0Engine() { } + +private: + // AdlEngine + void init(); + void initGameState(); +}; + +void HiRes0Engine::init() { + _graphics = new Graphics_v2(*_display); + + _disk = new DiskImage(); + if (!_disk->open(IDS_HR0_DISK_IMAGE)) + error("Failed to open disk image '" IDS_HR0_DISK_IMAGE "'"); + + _disk->setSectorLimit(13); + + // TODO: all these strings/offsets/etc are the same as hires2 + + StreamPtr stream(_disk->createReadStream(0x1f, 0x2, 0x00, 2)); + loadMessages(*stream, IDI_HR0_NUM_MESSAGES); + + // Read parser messages + stream.reset(_disk->createReadStream(0x1a, 0x1)); + _strings.verbError = readStringAt(*stream, 0x4f); + _strings.nounError = readStringAt(*stream, 0x8e); + _strings.enterCommand = readStringAt(*stream, 0xbc); + + // Read time string + stream.reset(_disk->createReadStream(0x19, 0x7, 0xd7)); + _strings_v2.time = readString(*stream, 0xff); + + // Read line feeds + stream.reset(_disk->createReadStream(0x19, 0xb, 0xf8, 1)); + _strings.lineFeeds = readString(*stream); + + // Read opcode strings + stream.reset(_disk->createReadStream(0x1a, 0x6, 0x00, 2)); + _strings_v2.saveInsert = readStringAt(*stream, 0x5f); + _strings_v2.saveReplace = readStringAt(*stream, 0xe5); + _strings_v2.restoreInsert = readStringAt(*stream, 0x132); + _strings_v2.restoreReplace = readStringAt(*stream, 0x1c2); + _strings.playAgain = readStringAt(*stream, 0x225); + _strings.pressReturn = readStringAt(*stream, 0x25f); + + _messageIds.cantGoThere = IDI_HR0_MSG_CANT_GO_THERE; + _messageIds.dontUnderstand = IDI_HR0_MSG_DONT_UNDERSTAND; + _messageIds.itemDoesntMove = IDI_HR0_MSG_ITEM_DOESNT_MOVE; + _messageIds.itemNotHere = IDI_HR0_MSG_ITEM_NOT_HERE; + _messageIds.thanksForPlaying = IDI_HR0_MSG_THANKS_FOR_PLAYING; + + // Load global picture data + stream.reset(_disk->createReadStream(0x19, 0xa, 0x80, 0)); + loadPictures(*stream); + + // Load item picture data + stream.reset(_disk->createReadStream(0x1e, 0x9, 0x05)); + loadItemPictures(*stream, IDI_HR0_NUM_ITEM_PICS); + + // Load commands from executable + stream.reset(_disk->createReadStream(0x1d, 0x7, 0x00, 2)); + readCommands(*stream, _roomCommands); + + stream.reset(_disk->createReadStream(0x1f, 0x7, 0x00, 3)); + readCommands(*stream, _globalCommands); + + // Load dropped item offsets + stream.reset(_disk->createReadStream(0x1b, 0x4, 0x15)); + loadDroppedItemOffsets(*stream, IDI_HR0_NUM_ITEM_OFFSETS); + + // Load verbs + stream.reset(_disk->createReadStream(0x19, 0x0, 0x00, 3)); + loadWords(*stream, _verbs, _priVerbs); + + // Load nouns + stream.reset(_disk->createReadStream(0x22, 0x2, 0x00, 2)); + loadWords(*stream, _nouns, _priNouns); +} + +void HiRes0Engine::initGameState() { + _state.vars.resize(IDI_HR0_NUM_VARS); + + StreamPtr stream(_disk->createReadStream(0x21, 0x5, 0x0e, 2)); + loadRooms(*stream, IDI_HR0_NUM_ROOMS); + + stream.reset(_disk->createReadStream(0x21, 0x0)); + loadItems(*stream); +} + +Engine *HiRes0Engine_create(OSystem *syst, const AdlGameDescription *gd) { + return new HiRes0Engine(syst, gd); +} + +} // End of namespace Adl diff --git a/engines/adl/hires0.h b/engines/adl/hires0.h deleted file mode 100644 index 624f248ea8..0000000000 --- a/engines/adl/hires0.h +++ /dev/null @@ -1,59 +0,0 @@ -/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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.
- *
- */
-
-#ifndef ADL_HIRES0_H
-#define ADL_HIRES0_H
-
-#include "adl/adl_v2.h"
-
-namespace Adl {
-
-#define IDS_HR0_DISK_IMAGE "MISSION.NIB"
-
-#define IDI_HR0_NUM_ROOMS 43
-#define IDI_HR0_NUM_MESSAGES 142
-#define IDI_HR0_NUM_VARS 40
-#define IDI_HR0_NUM_ITEM_PICS 2
-#define IDI_HR0_NUM_ITEM_OFFSETS 16
-
-// Messages used outside of scripts
-#define IDI_HR0_MSG_CANT_GO_THERE 110
-#define IDI_HR0_MSG_DONT_UNDERSTAND 112
-#define IDI_HR0_MSG_ITEM_DOESNT_MOVE 114
-#define IDI_HR0_MSG_ITEM_NOT_HERE 115
-#define IDI_HR0_MSG_THANKS_FOR_PLAYING 113
-
-class HiRes0Engine : public AdlEngine_v2 {
-public:
- HiRes0Engine(OSystem *syst, const AdlGameDescription *gd) :
- AdlEngine_v2(syst, gd) { }
- ~HiRes0Engine() { }
-
-private:
- // AdlEngine
- void init();
- void initGameState();
-};
-
-} // End of namespace Adl
-
-#endif
diff --git a/engines/adl/hires1.cpp b/engines/adl/hires1.cpp index 26565c03c3..217a9013ba 100644 --- a/engines/adl/hires1.cpp +++ b/engines/adl/hires1.cpp @@ -27,11 +27,105 @@ #include "common/stream.h" #include "common/ptr.h" -#include "adl/hires1.h" +#include "adl/adl.h" +#include "adl/graphics.h" #include "adl/display.h" namespace Adl { +#define IDS_HR1_EXE_0 "AUTO LOAD OBJ" +#define IDS_HR1_EXE_1 "ADVENTURE" +#define IDS_HR1_LOADER "MYSTERY.HELLO" +#define IDS_HR1_MESSAGES "MESSAGES" + +#define IDI_HR1_NUM_ROOMS 41 +#define IDI_HR1_NUM_PICS 97 +#define IDI_HR1_NUM_VARS 20 +#define IDI_HR1_NUM_ITEM_OFFSETS 21 +#define IDI_HR1_NUM_MESSAGES 168 + +// Messages used outside of scripts +#define IDI_HR1_MSG_CANT_GO_THERE 137 +#define IDI_HR1_MSG_DONT_UNDERSTAND 37 +#define IDI_HR1_MSG_ITEM_DOESNT_MOVE 151 +#define IDI_HR1_MSG_ITEM_NOT_HERE 152 +#define IDI_HR1_MSG_THANKS_FOR_PLAYING 140 +#define IDI_HR1_MSG_DONT_HAVE_IT 127 +#define IDI_HR1_MSG_GETTING_DARK 7 + +#define IDI_HR1_OFS_STR_ENTER_COMMAND 0x5bbc +#define IDI_HR1_OFS_STR_VERB_ERROR 0x5b4f +#define IDI_HR1_OFS_STR_NOUN_ERROR 0x5b8e +#define IDI_HR1_OFS_STR_PLAY_AGAIN 0x5f1e +#define IDI_HR1_OFS_STR_CANT_GO_THERE 0x6c0a +#define IDI_HR1_OFS_STR_DONT_HAVE_IT 0x6c31 +#define IDI_HR1_OFS_STR_DONT_UNDERSTAND 0x6c51 +#define IDI_HR1_OFS_STR_GETTING_DARK 0x6c7c +#define IDI_HR1_OFS_STR_PRESS_RETURN 0x5f68 +#define IDI_HR1_OFS_STR_LINE_FEEDS 0x59d4 + +#define IDI_HR1_OFS_PD_TEXT_0 0x005d +#define IDI_HR1_OFS_PD_TEXT_1 0x012b +#define IDI_HR1_OFS_PD_TEXT_2 0x016d +#define IDI_HR1_OFS_PD_TEXT_3 0x0259 + +#define IDI_HR1_OFS_INTRO_TEXT 0x0066 +#define IDI_HR1_OFS_GAME_OR_HELP 0x000f + +#define IDI_HR1_OFS_LOGO_0 0x1003 +#define IDI_HR1_OFS_LOGO_1 0x1800 + +#define IDI_HR1_OFS_ITEMS 0x0100 +#define IDI_HR1_OFS_ROOMS 0x050a +#define IDI_HR1_OFS_PICS 0x4b03 +#define IDI_HR1_OFS_CMDS_0 0x3c00 +#define IDI_HR1_OFS_CMDS_1 0x3d00 +#define IDI_HR1_OFS_MSGS 0x4d00 + +#define IDI_HR1_OFS_ITEM_OFFSETS 0x68ff +#define IDI_HR1_OFS_CORNERS 0x4f00 + +#define IDI_HR1_OFS_VERBS 0x3800 +#define IDI_HR1_OFS_NOUNS 0x0f00 + +class HiRes1Engine : public AdlEngine { +public: + HiRes1Engine(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine(syst, gd), + _files(nullptr), + _messageDelay(true) { } + ~HiRes1Engine() { delete _files; } + +private: + // AdlEngine + void runIntro() const; + void init(); + void initGameState(); + void restartGame(); + void printString(const Common::String &str); + Common::String loadMessage(uint idx) const; + void printMessage(uint idx); + void drawItems(); + void drawItem(Item &item, const Common::Point &pos); + void loadRoom(byte roomNr); + void showRoom(); + + void wordWrap(Common::String &str) const; + + Files *_files; + Common::File _exe; + Common::Array<DataBlockPtr> _corners; + Common::Array<byte> _roomDesc; + bool _messageDelay; + + struct { + Common::String cantGoThere; + Common::String dontHaveIt; + Common::String dontUnderstand; + Common::String gettingDark; + } _gameStrings; +}; + void HiRes1Engine::runIntro() const { StreamPtr stream(_files->createReadStream(IDS_HR1_EXE_0)); @@ -183,12 +277,7 @@ void HiRes1Engine::init() { // Load dropped item offsets stream->seek(IDI_HR1_OFS_ITEM_OFFSETS); - for (uint i = 0; i < IDI_HR1_NUM_ITEM_OFFSETS; ++i) { - Common::Point p; - p.x = stream->readByte(); - p.y = stream->readByte(); - _itemOffsets.push_back(p); - } + loadDroppedItemOffsets(*stream, IDI_HR1_NUM_ITEM_OFFSETS); // Load right-angle line art stream->seek(IDI_HR1_OFS_CORNERS); diff --git a/engines/adl/hires1.h b/engines/adl/hires1.h deleted file mode 100644 index c060bc892e..0000000000 --- a/engines/adl/hires1.h +++ /dev/null @@ -1,134 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * 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. - * - */ - -#ifndef ADL_HIRES1_H -#define ADL_HIRES1_H - -#include "common/str.h" - -#include "adl/adl.h" -#include "adl/graphics.h" -#include "adl/disk.h" - -namespace Common { -class ReadStream; -struct Point; -} - -namespace Adl { - -#define IDS_HR1_EXE_0 "AUTO LOAD OBJ" -#define IDS_HR1_EXE_1 "ADVENTURE" -#define IDS_HR1_LOADER "MYSTERY.HELLO" -#define IDS_HR1_MESSAGES "MESSAGES" - -#define IDI_HR1_NUM_ROOMS 41 -#define IDI_HR1_NUM_PICS 97 -#define IDI_HR1_NUM_VARS 20 -#define IDI_HR1_NUM_ITEM_OFFSETS 21 -#define IDI_HR1_NUM_MESSAGES 168 - -// Messages used outside of scripts -#define IDI_HR1_MSG_CANT_GO_THERE 137 -#define IDI_HR1_MSG_DONT_UNDERSTAND 37 -#define IDI_HR1_MSG_ITEM_DOESNT_MOVE 151 -#define IDI_HR1_MSG_ITEM_NOT_HERE 152 -#define IDI_HR1_MSG_THANKS_FOR_PLAYING 140 -#define IDI_HR1_MSG_DONT_HAVE_IT 127 -#define IDI_HR1_MSG_GETTING_DARK 7 - -#define IDI_HR1_OFS_STR_ENTER_COMMAND 0x5bbc -#define IDI_HR1_OFS_STR_VERB_ERROR 0x5b4f -#define IDI_HR1_OFS_STR_NOUN_ERROR 0x5b8e -#define IDI_HR1_OFS_STR_PLAY_AGAIN 0x5f1e -#define IDI_HR1_OFS_STR_CANT_GO_THERE 0x6c0a -#define IDI_HR1_OFS_STR_DONT_HAVE_IT 0x6c31 -#define IDI_HR1_OFS_STR_DONT_UNDERSTAND 0x6c51 -#define IDI_HR1_OFS_STR_GETTING_DARK 0x6c7c -#define IDI_HR1_OFS_STR_PRESS_RETURN 0x5f68 -#define IDI_HR1_OFS_STR_LINE_FEEDS 0x59d4 - -#define IDI_HR1_OFS_PD_TEXT_0 0x005d -#define IDI_HR1_OFS_PD_TEXT_1 0x012b -#define IDI_HR1_OFS_PD_TEXT_2 0x016d -#define IDI_HR1_OFS_PD_TEXT_3 0x0259 - -#define IDI_HR1_OFS_INTRO_TEXT 0x0066 -#define IDI_HR1_OFS_GAME_OR_HELP 0x000f - -#define IDI_HR1_OFS_LOGO_0 0x1003 -#define IDI_HR1_OFS_LOGO_1 0x1800 - -#define IDI_HR1_OFS_ITEMS 0x0100 -#define IDI_HR1_OFS_ROOMS 0x050a -#define IDI_HR1_OFS_PICS 0x4b03 -#define IDI_HR1_OFS_CMDS_0 0x3c00 -#define IDI_HR1_OFS_CMDS_1 0x3d00 -#define IDI_HR1_OFS_MSGS 0x4d00 - -#define IDI_HR1_OFS_ITEM_OFFSETS 0x68ff -#define IDI_HR1_OFS_CORNERS 0x4f00 - -#define IDI_HR1_OFS_VERBS 0x3800 -#define IDI_HR1_OFS_NOUNS 0x0f00 - -class HiRes1Engine : public AdlEngine { -public: - HiRes1Engine(OSystem *syst, const AdlGameDescription *gd) : - AdlEngine(syst, gd), - _files(nullptr), - _messageDelay(true) { } - ~HiRes1Engine() { delete _files; } - -private: - // AdlEngine - void runIntro() const; - void init(); - void initGameState(); - void restartGame(); - void printString(const Common::String &str); - Common::String loadMessage(uint idx) const; - void printMessage(uint idx); - void drawItems(); - void drawItem(Item &item, const Common::Point &pos); - void loadRoom(byte roomNr); - void showRoom(); - - void wordWrap(Common::String &str) const; - - Files *_files; - Common::File _exe; - Common::Array<DataBlockPtr> _corners; - Common::Array<byte> _roomDesc; - bool _messageDelay; - - struct { - Common::String cantGoThere; - Common::String dontHaveIt; - Common::String dontUnderstand; - Common::String gettingDark; - } _gameStrings; -}; - -} // End of namespace Adl - -#endif diff --git a/engines/adl/hires2.cpp b/engines/adl/hires2.cpp index 14db237d82..199f457b4f 100644 --- a/engines/adl/hires2.cpp +++ b/engines/adl/hires2.cpp @@ -26,18 +26,44 @@ #include "common/file.h" #include "common/stream.h" -#include "adl/hires2.h" +#include "adl/adl_v2.h" #include "adl/display.h" #include "adl/graphics.h" #include "adl/disk.h" namespace Adl { +#define IDS_HR2_DISK_IMAGE "WIZARD.DSK" + +#define IDI_HR2_NUM_ROOMS 135 +#define IDI_HR2_NUM_MESSAGES 255 +#define IDI_HR2_NUM_VARS 40 +#define IDI_HR2_NUM_ITEM_PICS 38 +#define IDI_HR2_NUM_ITEM_OFFSETS 16 + +// Messages used outside of scripts +#define IDI_HR2_MSG_CANT_GO_THERE 123 +#define IDI_HR2_MSG_DONT_UNDERSTAND 19 +#define IDI_HR2_MSG_ITEM_DOESNT_MOVE 242 +#define IDI_HR2_MSG_ITEM_NOT_HERE 4 +#define IDI_HR2_MSG_THANKS_FOR_PLAYING 239 + +class HiRes2Engine : public AdlEngine_v2 { +public: + HiRes2Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine_v2(syst, gd) { } + +private: + // AdlEngine + void runIntro() const; + void init(); + void initGameState(); +}; + void HiRes2Engine::runIntro() const { // 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. - _disk->setMode13(false); + _disk->setSectorLimit(0); StreamPtr stream(_disk->createReadStream(0x00, 0xd, 0x17, 1)); _display->setMode(DISPLAY_MODE_TEXT); @@ -50,7 +76,7 @@ void HiRes2Engine::runIntro() const { _display->printString(str); delay(2000); - _disk->setMode13(true); + _disk->setSectorLimit(13); } void HiRes2Engine::init() { @@ -60,12 +86,10 @@ void HiRes2Engine::init() { if (!_disk->open(IDS_HR2_DISK_IMAGE)) error("Failed to open disk image '" IDS_HR2_DISK_IMAGE "'"); - _disk->setMode13(true); + _disk->setSectorLimit(13); StreamPtr stream(_disk->createReadStream(0x1f, 0x2, 0x00, 4)); - - for (uint i = 0; i < IDI_HR2_NUM_MESSAGES; ++i) - _messages.push_back(readDataBlockPtr(*stream)); + loadMessages(*stream, IDI_HR2_NUM_MESSAGES); // Read parser messages stream.reset(_disk->createReadStream(0x1a, 0x1)); @@ -98,20 +122,11 @@ void HiRes2Engine::init() { // Load global picture data stream.reset(_disk->createReadStream(0x19, 0xa, 0x80, 0)); - byte picNr; - while ((picNr = stream->readByte()) != 0xff) { - if (stream->eos() || stream->err()) - error("Error reading global pic list"); - - _pictures[picNr] = readDataBlockPtr(*stream); - } + loadPictures(*stream); // Load item picture data stream.reset(_disk->createReadStream(0x1e, 0x9, 0x05)); - for (uint i = 0; i < IDI_HR2_NUM_ITEM_PICS; ++i) { - stream->readByte(); // number - _itemPics.push_back(readDataBlockPtr(*stream)); - } + loadItemPictures(*stream, IDI_HR2_NUM_ITEM_PICS); // Load commands from executable stream.reset(_disk->createReadStream(0x1d, 0x7, 0x00, 4)); @@ -122,12 +137,7 @@ void HiRes2Engine::init() { // Load dropped item offsets stream.reset(_disk->createReadStream(0x1b, 0x4, 0x15)); - for (uint i = 0; i < IDI_HR2_NUM_ITEM_OFFSETS; ++i) { - Common::Point p; - p.x = stream->readByte(); - p.y = stream->readByte(); - _itemOffsets.push_back(p); - } + loadDroppedItemOffsets(*stream, IDI_HR2_NUM_ITEM_OFFSETS); // Load verbs stream.reset(_disk->createReadStream(0x19, 0x0, 0x00, 3)); @@ -142,46 +152,10 @@ void HiRes2Engine::initGameState() { _state.vars.resize(IDI_HR2_NUM_VARS); StreamPtr stream(_disk->createReadStream(0x21, 0x5, 0x0e, 7)); - - for (uint i = 0; i < IDI_HR2_NUM_ROOMS; ++i) { - Room room; - stream->readByte(); // number - for (uint j = 0; j < 6; ++j) - room.connections[j] = stream->readByte(); - room.data = readDataBlockPtr(*stream); - room.picture = stream->readByte(); - room.curPicture = stream->readByte(); - room.isFirstTime = stream->readByte(); - _state.rooms.push_back(room); - } + loadRooms(*stream, IDI_HR2_NUM_ROOMS); stream.reset(_disk->createReadStream(0x21, 0x0, 0x00, 2)); - - byte id; - while ((id = stream->readByte()) != 0xff) { - Item item = Item(); - item.id = id; - item.noun = stream->readByte(); - item.room = stream->readByte(); - item.picture = stream->readByte(); - item.isLineArt = stream->readByte(); // Is this still used in this way? - item.position.x = stream->readByte(); - item.position.y = stream->readByte(); - item.state = stream->readByte(); - item.description = stream->readByte(); - - stream->readByte(); // Struct size - - byte picListSize = stream->readByte(); - - // Flag to keep track of what has been drawn on the screen - stream->readByte(); - - for (uint i = 0; i < picListSize; ++i) - item.roomPictures.push_back(stream->readByte()); - - _state.items.push_back(item); - } + loadItems(*stream); } Engine *HiRes2Engine_create(OSystem *syst, const AdlGameDescription *gd) { diff --git a/engines/adl/hires4.cpp b/engines/adl/hires4.cpp new file mode 100644 index 0000000000..ddfc868e9a --- /dev/null +++ b/engines/adl/hires4.cpp @@ -0,0 +1,271 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +#include "common/system.h" +#include "common/debug.h" +#include "common/error.h" +#include "common/file.h" +#include "common/stream.h" + +#include "adl/adl_v3.h" +#include "adl/detection.h" +#include "adl/display.h" +#include "adl/graphics.h" +#include "adl/disk.h" + +namespace Adl { + +#define IDI_HR4_NUM_ROOMS 164 +#define IDI_HR4_NUM_MESSAGES 255 +#define IDI_HR4_NUM_VARS 40 +#define IDI_HR4_NUM_ITEM_DESCS 44 +#define IDI_HR4_NUM_ITEM_PICS 41 +#define IDI_HR4_NUM_ITEM_OFFSETS 40 + +// Messages used outside of scripts +#define IDI_HR4_MSG_CANT_GO_THERE 110 +#define IDI_HR4_MSG_DONT_UNDERSTAND 112 +#define IDI_HR4_MSG_ITEM_DOESNT_MOVE 114 +#define IDI_HR4_MSG_ITEM_NOT_HERE 115 +#define IDI_HR4_MSG_THANKS_FOR_PLAYING 113 + +class HiRes4Engine_Atari : public AdlEngine_v3 { +public: + HiRes4Engine_Atari(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine_v3(syst, gd), + _boot(nullptr), + _curDisk(0) { } + ~HiRes4Engine_Atari(); + +private: + // AdlEngine + void init(); + void initGameState(); + void loadRoom(byte roomNr); + Common::String formatVerbError(const Common::String &verb) const; + Common::String formatNounError(const Common::String &verb, const Common::String &noun) const; + + // AdlEngine_v2 + void adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const; + + Common::SeekableReadStream *createReadStream(DiskImage *disk, byte track, byte sector, byte offset = 0, byte size = 0) const; + void loadCommonData(); + void insertDisk(byte diskNr); + void rebindDisk(); + + DiskImage *_boot; + byte _curDisk; +}; + +static const char *const atariDisks[] = { "ULYS1A.XFD", "ULYS1B.XFD", "ULYS2C.XFD" }; + +HiRes4Engine_Atari::~HiRes4Engine_Atari() { + delete _boot; +} + +void HiRes4Engine_Atari::init() { + _graphics = new Graphics_v2(*_display); + + _boot = new DiskImage(); + if (!_boot->open(atariDisks[0])) + error("Failed to open disk image '%s'", atariDisks[0]); + + insertDisk(1); + loadCommonData(); + + StreamPtr stream(createReadStream(_boot, 0x06, 0x2)); + _strings.verbError = readStringAt(*stream, 0x4f); + _strings.nounError = readStringAt(*stream, 0x83); + _strings.enterCommand = readStringAt(*stream, 0xa6); + + stream.reset(createReadStream(_boot, 0x05, 0xb, 0xd7)); + _strings_v2.time = readString(*stream, 0xff); + + stream.reset(createReadStream(_boot, 0x06, 0x7, 0x00, 2)); + _strings_v2.saveInsert = readStringAt(*stream, 0x62); + _strings_v2.saveReplace = readStringAt(*stream, 0xdd); + _strings_v2.restoreInsert = readStringAt(*stream, 0x12a); + _strings_v2.restoreReplace = readStringAt(*stream, 0x1b8); + _strings.playAgain = readStringAt(*stream, 0x21b); + // TODO: restart sequence has "insert side a/b" strings + + _messageIds.cantGoThere = IDI_HR4_MSG_CANT_GO_THERE; + _messageIds.dontUnderstand = IDI_HR4_MSG_DONT_UNDERSTAND; + _messageIds.itemDoesntMove = IDI_HR4_MSG_ITEM_DOESNT_MOVE; + _messageIds.itemNotHere = IDI_HR4_MSG_ITEM_NOT_HERE; + _messageIds.thanksForPlaying = IDI_HR4_MSG_THANKS_FOR_PLAYING; + + stream.reset(createReadStream(_boot, 0x06, 0xd, 0x12, 2)); + loadItemDescriptions(*stream, IDI_HR4_NUM_ITEM_DESCS); + + stream.reset(createReadStream(_boot, 0x07, 0x1, 0xf4)); + loadDroppedItemOffsets(*stream, IDI_HR4_NUM_ITEM_OFFSETS); + + stream.reset(createReadStream(_boot, 0x08, 0xe, 0xa5, 5)); + readCommands(*stream, _roomCommands); + + stream.reset(createReadStream(_boot, 0x0a, 0x9, 0x00, 3)); + readCommands(*stream, _globalCommands); + + stream.reset(createReadStream(_boot, 0x05, 0x4, 0x00, 3)); + loadWords(*stream, _verbs, _priVerbs); + + stream.reset(createReadStream(_boot, 0x03, 0xb, 0x00, 6)); + loadWords(*stream, _nouns, _priNouns); +} + +void HiRes4Engine_Atari::loadRoom(byte roomNr) { + if (roomNr >= 59 && roomNr < 113) { + if (_curDisk != 2) { + insertDisk(2); + rebindDisk(); + } + } else if (_curDisk != 1) { + insertDisk(1); + rebindDisk(); + } + + if (roomNr == 121) { + // Room 121 is not present in the Atari version. This causes + // problems when we're dumping scripts with the debugger, so + // we intercept this room load here. + // FIXME: Find out if the Apple II version does have this room + // FIXME: Implement more generic handling of invalid rooms? + debug("Warning: attempt to load non-existent room 121"); + _roomData.description.clear(); + _roomData.pictures.clear(); + _roomData.commands.clear(); + return; + } + + AdlEngine_v3::loadRoom(roomNr); +} + +Common::String HiRes4Engine_Atari::formatVerbError(const Common::String &verb) const { + Common::String err = _strings.verbError; + for (uint i = 0; i < verb.size(); ++i) + err.setChar(verb[i], i + 8); + return err; +} + +Common::String HiRes4Engine_Atari::formatNounError(const Common::String &verb, const Common::String &noun) const { + Common::String err = _strings.nounError; + for (uint i = 0; i < verb.size(); ++i) + err.setChar(verb[i], i + 8); + for (uint i = 0; i < noun.size(); ++i) + err.setChar(noun[i], i + 19); + return err; +} + +void HiRes4Engine_Atari::insertDisk(byte diskNr) { + if (_curDisk == diskNr) + return; + + _curDisk = diskNr; + + delete _disk; + _disk = new DiskImage(); + if (!_disk->open(atariDisks[diskNr])) + error("Failed to open disk image '%s'", atariDisks[diskNr]); +} + +void HiRes4Engine_Atari::rebindDisk() { + // As room.data is bound to the DiskImage, we need to rebind them here + // We cannot simply reload the rooms as that would reset their state + + // FIXME: Remove DataBlockPtr-DiskImage coupling? + + StreamPtr stream(createReadStream(_boot, 0x03, 0x1, 0x0e, 9)); + for (uint i = 0; i < IDI_HR4_NUM_ROOMS; ++i) { + stream->skip(7); + _state.rooms[i].data = readDataBlockPtr(*stream); + stream->skip(3); + } + + // Rebind data that is on both side B and C + loadCommonData(); +} + +void HiRes4Engine_Atari::loadCommonData() { + _messages.clear(); + StreamPtr stream(createReadStream(_boot, 0x0a, 0x4, 0x00, 3)); + loadMessages(*stream, IDI_HR4_NUM_MESSAGES); + + _pictures.clear(); + stream.reset(createReadStream(_boot, 0x05, 0xe, 0x80)); + loadPictures(*stream); + + _itemPics.clear(); + stream.reset(createReadStream(_boot, 0x09, 0xe, 0x05)); + loadItemPictures(*stream, IDI_HR4_NUM_ITEM_PICS); +} + +void HiRes4Engine_Atari::initGameState() { + _state.vars.resize(IDI_HR4_NUM_VARS); + + StreamPtr stream(createReadStream(_boot, 0x03, 0x1, 0x0e, 9)); + loadRooms(*stream, IDI_HR4_NUM_ROOMS); + + stream.reset(createReadStream(_boot, 0x02, 0xc, 0x00, 12)); + loadItems(*stream); + + // FIXME + _display->moveCursorTo(Common::Point(0, 23)); +} + +Common::SeekableReadStream *HiRes4Engine_Atari::createReadStream(DiskImage *disk, byte track, byte sector, byte offset, byte size) const { + adjustDataBlockPtr(track, sector, offset, size); + return disk->createReadStream(track, sector, offset, size); +} + +void HiRes4Engine_Atari::adjustDataBlockPtr(byte &track, byte §or, byte &offset, byte &size) const { + // Convert the Apple II disk offsets in the game, to Atari disk offsets + uint sectorIndex = (track * 16 + sector + 1) << 1; + + // Atari uses 128 bytes per sector vs. 256 on the Apple II + // Note that size indicates *additional* sectors to read after reading one sector + size *= 2; + + if (offset >= 128) { + // Offset in the second half of an Apple II sector, skip one sector and adjust offset + ++sectorIndex; + offset -= 128; + } else { + // Offset in the first half of an Apple II sector, we need to read one additional sector + ++size; + } + + // Compute track/sector for Atari's 18 sectors per track (sectorIndex is 1-based) + track = (sectorIndex - 1) / 18; + sector = (sectorIndex - 1) % 18; +} + +Engine *HiRes4Engine_create(OSystem *syst, const AdlGameDescription *gd) { + switch (gd->desc.platform) { + case Common::kPlatformAtariST: + return new HiRes4Engine_Atari(syst, gd); + default: + error("Unsupported platform"); + } +} + +} // End of namespace Adl diff --git a/engines/adl/hires6.cpp b/engines/adl/hires6.cpp index e9df7b513a..a1fea05f19 100644 --- a/engines/adl/hires6.cpp +++ b/engines/adl/hires6.cpp @@ -27,13 +27,65 @@ #include "common/stream.h" #include "common/memstream.h" -#include "adl/hires6.h" +#include "adl/adl_v4.h" #include "adl/display.h" #include "adl/graphics.h" #include "adl/disk.h" namespace Adl { +#define IDI_HR6_NUM_ROOMS 35 +#define IDI_HR6_NUM_MESSAGES 256 +#define IDI_HR6_NUM_VARS 40 +#define IDI_HR6_NUM_ITEM_DESCS 15 +#define IDI_HR6_NUM_ITEM_PICS 15 +#define IDI_HR6_NUM_ITEM_OFFSETS 16 + +// Messages used outside of scripts +#define IDI_HR6_MSG_CANT_GO_THERE 249 +#define IDI_HR6_MSG_DONT_UNDERSTAND 247 +#define IDI_HR6_MSG_ITEM_DOESNT_MOVE 253 +#define IDI_HR6_MSG_ITEM_NOT_HERE 254 +#define IDI_HR6_MSG_THANKS_FOR_PLAYING 252 + +struct DiskDataDesc { + byte track; + byte sector; + byte offset; + byte volume; +}; + +class HiRes6Engine : public AdlEngine_v4 { +public: + HiRes6Engine(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine_v4(syst, gd), + _boot(nullptr), + _currVerb(0), + _currNoun(0) { + } + + ~HiRes6Engine() { delete _boot; } + +private: + // AdlEngine + void runIntro() const; + void init(); + void initGameState(); + void printRoomDescription(); + void showRoom(); + Common::String formatVerbError(const Common::String &verb) const; + Common::String formatNounError(const Common::String &verb, const Common::String &noun) const; + + // AdlEngine_v2 + void printString(const Common::String &str); + + void loadDisk(byte disk); + + DiskImage *_boot; + byte _currVerb, _currNoun; + Common::Array<DiskDataDesc> _diskDataDesc; +}; + static const char *disks[] = { "DARK1A.DSK", "DARK1B.NIB", "DARK2A.NIB", "DARK2B.NIB" }; #define SECTORS_PER_TRACK 16 @@ -141,18 +193,12 @@ void HiRes6Engine::init() { // Item descriptions stream.reset(loadSectors(_boot, 0x6, 0xb, 2)); - stream->seek(0x34); - for (uint i = 0; i < IDI_HR6_NUM_ITEM_DESCS; ++i) - _itemDesc.push_back(readString(*stream, 0xff)); + stream->seek(0x16); + loadItemDescriptions(*stream, IDI_HR6_NUM_ITEM_DESCS); // Load dropped item offsets stream.reset(_boot->createReadStream(0x8, 0x9, 0x16)); - for (uint i = 0; i < IDI_HR6_NUM_ITEM_OFFSETS; ++i) { - Common::Point p; - p.x = stream->readByte(); - p.y = stream->readByte(); - _itemOffsets.push_back(p); - } + loadDroppedItemOffsets(*stream, IDI_HR6_NUM_ITEM_OFFSETS); // Location of game data for each disc stream.reset(_boot->createReadStream(0x5, 0xa, 0x03)); @@ -187,10 +233,7 @@ void HiRes6Engine::loadDisk(byte disk) { // Load item picture data (indexed on boot disk) StreamPtr stream(_boot->createReadStream(0xb, 0xd, 0x08)); _itemPics.clear(); - for (uint i = 0; i < IDI_HR6_NUM_ITEM_PICS; ++i) { - stream->readByte(); - _itemPics.push_back(readDataBlockPtr(*stream)); - } + loadItemPictures(*stream, IDI_HR6_NUM_ITEM_PICS); _curDisk = disk; @@ -214,19 +257,13 @@ void HiRes6Engine::loadDisk(byte disk) { // Messages _messages.clear(); uint count = size / 4; - for (uint i = 0; i < count; ++i) - _messages.push_back(readDataBlockPtr(*stream)); + loadMessages(*stream, count); break; } case 0x4a80: { // Global pics _pictures.clear(); - byte picNr; - while ((picNr = stream->readByte()) != 0xff) { - if (stream->eos() || stream->err()) - error("Error reading global pic list"); - _pictures[picNr] = readDataBlockPtr(*stream); - } + loadPictures(*stream); break; } case 0x4000: @@ -243,17 +280,7 @@ void HiRes6Engine::loadDisk(byte disk) { stream->skip(14); // Skip invalid room 0 _state.rooms.clear(); - for (uint i = 0; i < count; ++i) { - Room room; - stream->readByte(); // number - for (uint j = 0; j < 6; ++j) - room.connections[j] = stream->readByte(); - room.data = readDataBlockPtr(*stream); - room.picture = stream->readByte(); - room.curPicture = stream->readByte(); - room.isFirstTime = stream->readByte(); - _state.rooms.push_back(room); - } + loadRooms(*stream, count); break; } case 0x7b00: @@ -287,31 +314,7 @@ void HiRes6Engine::initGameState() { StreamPtr stream(_boot->createReadStream(0x3, 0xe, 0x03)); - byte id; - while ((id = stream->readByte()) != 0xff) { - Item item = Item(); - item.id = id; - item.noun = stream->readByte(); - item.room = stream->readByte(); - item.picture = stream->readByte(); - item.isLineArt = stream->readByte(); // Now seems to be disk number - item.position.x = stream->readByte(); - item.position.y = stream->readByte(); - item.state = stream->readByte(); - item.description = stream->readByte(); - - stream->readByte(); // Struct size - - byte picListSize = stream->readByte(); - - // Flag to keep track of what has been drawn on the screen - stream->readByte(); - - for (uint i = 0; i < picListSize; ++i) - item.roomPictures.push_back(stream->readByte()); - - _state.items.push_back(item); - } + loadItems(*stream); _currVerb = _currNoun = 0; } diff --git a/engines/adl/hires6.h b/engines/adl/hires6.h deleted file mode 100644 index 0f604d848c..0000000000 --- a/engines/adl/hires6.h +++ /dev/null @@ -1,92 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * 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. - * - */ - -#ifndef ADL_HIRES6_H -#define ADL_HIRES6_H - -#include "common/str.h" - -#include "adl/adl_v3.h" -#include "adl/disk.h" - -namespace Common { -class ReadStream; -struct Point; -} - -namespace Adl { - -#define IDI_HR6_NUM_ROOMS 35 -#define IDI_HR6_NUM_MESSAGES 256 -#define IDI_HR6_NUM_VARS 40 -#define IDI_HR6_NUM_ITEM_DESCS 15 -#define IDI_HR6_NUM_ITEM_PICS 15 -#define IDI_HR6_NUM_ITEM_OFFSETS 16 - -// Messages used outside of scripts -#define IDI_HR6_MSG_CANT_GO_THERE 249 -#define IDI_HR6_MSG_DONT_UNDERSTAND 247 -#define IDI_HR6_MSG_ITEM_DOESNT_MOVE 253 -#define IDI_HR6_MSG_ITEM_NOT_HERE 254 -#define IDI_HR6_MSG_THANKS_FOR_PLAYING 252 - -struct DiskDataDesc { - byte track; - byte sector; - byte offset; - byte volume; -}; - -class HiRes6Engine : public AdlEngine_v3 { -public: - HiRes6Engine(OSystem *syst, const AdlGameDescription *gd) : - AdlEngine_v3(syst, gd), - _boot(nullptr), - _currVerb(0), - _currNoun(0) { - } - - ~HiRes6Engine() { delete _boot; } - -private: - // AdlEngine - void runIntro() const; - void init(); - void initGameState(); - void printRoomDescription(); - void showRoom(); - Common::String formatVerbError(const Common::String &verb) const; - Common::String formatNounError(const Common::String &verb, const Common::String &noun) const; - - // AdlEngine_v2 - void printString(const Common::String &str); - - void loadDisk(byte disk); - - DiskImage *_boot; - byte _currVerb, _currNoun; - Common::Array<DiskDataDesc> _diskDataDesc; -}; - -} // End of namespace Adl - -#endif diff --git a/engines/adl/module.mk b/engines/adl/module.mk index d17c8569b3..d1de2a6c02 100644 --- a/engines/adl/module.mk +++ b/engines/adl/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS := \ adl.o \ adl_v2.o \ adl_v3.o \ + adl_v4.o \ console.o \ detection.o \ disk.o \ @@ -14,6 +15,7 @@ MODULE_OBJS := \ hires0.o \ hires1.o \ hires2.o \ + hires4.o \ hires6.o \ speaker.o |