diff options
Diffstat (limited to 'engines/adl')
-rw-r--r-- | engines/adl/adl.cpp | 9 | ||||
-rw-r--r-- | engines/adl/adl.h | 3 | ||||
-rw-r--r-- | engines/adl/adl_v2.cpp | 28 | ||||
-rw-r--r-- | engines/adl/adl_v2.h | 5 | ||||
-rw-r--r-- | engines/adl/adl_v3.cpp | 217 | ||||
-rw-r--r-- | engines/adl/adl_v3.h | 29 | ||||
-rw-r--r-- | engines/adl/adl_v4.cpp | 271 | ||||
-rw-r--r-- | engines/adl/adl_v4.h | 73 | ||||
-rw-r--r-- | engines/adl/detection.cpp | 64 | ||||
-rw-r--r-- | engines/adl/detection.h | 1 | ||||
-rw-r--r-- | engines/adl/disk.cpp | 184 | ||||
-rw-r--r-- | engines/adl/disk.h | 52 | ||||
-rw-r--r-- | engines/adl/display.cpp | 27 | ||||
-rw-r--r-- | engines/adl/display.h | 1 | ||||
-rw-r--r-- | engines/adl/hires0.cpp | 168 | ||||
-rw-r--r-- | engines/adl/hires0.h | 59 | ||||
-rw-r--r-- | engines/adl/hires1.cpp | 1 | ||||
-rw-r--r-- | engines/adl/hires2.cpp | 10 | ||||
-rw-r--r-- | engines/adl/hires4.cpp | 50 | ||||
-rw-r--r-- | engines/adl/hires4.h | 45 | ||||
-rw-r--r-- | engines/adl/hires6.cpp | 8 | ||||
-rw-r--r-- | engines/adl/hires6.h | 8 | ||||
-rw-r--r-- | engines/adl/module.mk | 3 |
23 files changed, 912 insertions, 404 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp index b6af54962e..19595606e1 100644 --- a/engines/adl/adl.cpp +++ b/engines/adl/adl.cpp @@ -520,6 +520,8 @@ void AdlEngine::dropItem(byte noun) { } Common::Error AdlEngine::run() { + initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true); + _console = new Console(this); _speaker = new Speaker(); _display = new Display(); @@ -659,6 +661,11 @@ Common::Error AdlEngine::loadGameState(int slot) { _state.rooms[i].isFirstTime = inFile->readByte(); } + // NOTE: _state.curPicture is part of the save state in the original engine. We + // reconstruct it instead. This is believed to be safe for at least hires 0-2, but + // this may need to be re-evaluated for later games. + _state.curPicture = _state.rooms[_state.room].curPicture; + size = inFile->readUint32BE(); if (size != _state.items.size()) error("Item count mismatch (expected %i; found %i)", _state.items.size(), size); @@ -949,7 +956,7 @@ int AdlEngine::o1_isVarEQ(ScriptEnv &e) { int AdlEngine::o1_isCurPicEQ(ScriptEnv &e) { OP_DEBUG_1("\t&& GET_CURPIC() == %d", e.arg(1)); - if (getCurRoom().curPicture == e.arg(1)) + if (_state.curPicture == e.arg(1)) return 1; return -1; diff --git a/engines/adl/adl.h b/engines/adl/adl.h index c9d77fcc62..89cdabe384 100644 --- a/engines/adl/adl.h +++ b/engines/adl/adl.h @@ -165,11 +165,12 @@ struct State { Common::Array<byte> vars; byte room; + byte curPicture; uint16 moves; bool isDark; Time time; - State() : room(1), moves(1), isDark(false) { } + State() : room(1), curPicture(0), moves(1), isDark(false) { } }; typedef Common::List<Command> Commands; diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp index 4fdf796701..e18f3339f8 100644 --- a/engines/adl/adl_v2.cpp +++ b/engines/adl/adl_v2.cpp @@ -79,9 +79,9 @@ void AdlEngine_v2::setupOpcodeTables() { Opcode(o1_listInv); Opcode(o2_moveItem); Opcode(o1_setRoom); - Opcode(o1_setCurPic); + Opcode(o2_setCurPic); // 0x08 - Opcode(o1_setPic); + Opcode(o2_setPic); Opcode(o1_printMsg); Opcode(o1_setLight); Opcode(o1_setDark); @@ -250,6 +250,8 @@ void AdlEngine_v2::loadRoom(byte roomNr) { void AdlEngine_v2::showRoom() { bool redrawPic = false; + _state.curPicture = getCurRoom().curPicture; + if (_state.room != _roomOnScreen) { loadRoom(_state.room); clearScreen(); @@ -257,15 +259,15 @@ void AdlEngine_v2::showRoom() { if (!_state.isDark) redrawPic = true; } else { - if (getCurRoom().curPicture != _picOnScreen || _itemRemoved) + if (_state.curPicture != _picOnScreen || _itemRemoved) redrawPic = true; } if (redrawPic) { _roomOnScreen = _state.room; - _picOnScreen = getCurRoom().curPicture; + _picOnScreen = _state.curPicture; - drawPic(getCurRoom().curPicture); + drawPic(_state.curPicture); _itemRemoved = false; _itemsOnScreen = 0; @@ -336,7 +338,7 @@ void AdlEngine_v2::drawItems() { Common::Array<byte>::const_iterator pic; for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) { - if (*pic == getCurRoom().curPicture || *pic == IDI_ANY) { + if (*pic == _state.curPicture || *pic == IDI_ANY) { drawItem(*item, item->position); break; } @@ -425,6 +427,20 @@ int AdlEngine_v2::o2_moveItem(ScriptEnv &e) { return 2; } +int AdlEngine_v2::o2_setCurPic(ScriptEnv &e) { + OP_DEBUG_1("\tSET_CURPIC(%d)", e.arg(1)); + + getCurRoom().curPicture = _state.curPicture = e.arg(1); + return 1; +} + +int AdlEngine_v2::o2_setPic(ScriptEnv &e) { + OP_DEBUG_1("\tSET_PIC(%d)", e.arg(1)); + + getCurRoom().picture = getCurRoom().curPicture = _state.curPicture = e.arg(1); + return 1; +} + int AdlEngine_v2::o2_moveAllItems(ScriptEnv &e) { OP_DEBUG_2("\tMOVE_ALL_ITEMS(%s, %s)", itemRoomStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); diff --git a/engines/adl/adl_v2.h b/engines/adl/adl_v2.h index f18972b74b..327b36e913 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; } @@ -64,6 +61,8 @@ protected: int o2_isCarryingSomething(ScriptEnv &e); int o2_moveItem(ScriptEnv &e); + int o2_setCurPic(ScriptEnv &e); + int o2_setPic(ScriptEnv &e); int o2_moveAllItems(ScriptEnv &e); int o2_save(ScriptEnv &e); int o2_restore(ScriptEnv &e); diff --git a/engines/adl/adl_v3.cpp b/engines/adl/adl_v3.cpp index 623db661bc..6b93acde61 100644 --- a/engines/adl/adl_v3.cpp +++ b/engines/adl/adl_v3.cpp @@ -20,162 +20,26 @@ * */ -#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]; -} - -void AdlEngine_v3::applyDiskOffset(byte &track, byte §or) const { - sector += _diskOffsets[_curDisk].sector; - if (sector >= 16) { - sector -= 16; - ++track; - } - - 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"); - - if (track == 0 && sector == 0 && offset == 0 && size == 0) - return DataBlockPtr(); - - applyDiskOffset(track, sector); - - return _disk->getDataBlock(track, sector, offset, size); + return _itemDesc[item.description - 1]; } 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(o1_setCurPic); - // 0x08 - Opcode(o1_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 +47,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..759b17cc6f 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,12 @@ 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; - - 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..602ee25683 --- /dev/null +++ b/engines/adl/adl_v4.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/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; +} + +DataBlockPtr AdlEngine_v4::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"); + + if (track == 0 && sector == 0 && offset == 0 && size == 0) + return DataBlockPtr(); + + applyDiskOffset(track, sector); + + return _disk->getDataBlock(track, sector, offset, size); +} + +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/adl_v4.h b/engines/adl/adl_v4.h new file mode 100644 index 0000000000..dc9a27501e --- /dev/null +++ b/engines/adl/adl_v4.h @@ -0,0 +1,73 @@ +/* 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_ADL_V4_H +#define ADL_ADL_V4_H + +#include "adl/adl_v3.h" + +namespace Common { +class RandomSource; +} + +struct DiskOffset { + byte track; + byte sector; +}; + +namespace Adl { + +class AdlEngine_v4 : public AdlEngine_v3 { +public: + virtual ~AdlEngine_v4() { } + +protected: + AdlEngine_v4(OSystem *syst, const AdlGameDescription *gd); + + // 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; + + 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 + +#endif diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp index 12405e7c5e..be9165b127 100644 --- a/engines/adl/detection.cpp +++ b/engines/adl/detection.cpp @@ -32,13 +32,15 @@ namespace Adl { -#define GAMEOPTION_COLOR GUIO_GAMEOPTIONS1 -#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2 -#define GAMEOPTION_MONO GUIO_GAMEOPTIONS3 +// Mystery House was designed for monochrome display, so we default to +// monochrome mode there. All the other games default to color mode. +#define GAMEOPTION_COLOR_DEFAULT_OFF GUIO_GAMEOPTIONS1 +#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2 +#define GAMEOPTION_COLOR_DEFAULT_ON GUIO_GAMEOPTIONS3 static const ADExtraGuiOptionsMap optionsList[] = { { - GAMEOPTION_COLOR, + GAMEOPTION_COLOR_DEFAULT_OFF, { _s("Color mode"), _s("Use color graphics"), @@ -48,7 +50,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { }, { - GAMEOPTION_MONO, + GAMEOPTION_COLOR_DEFAULT_ON, { _s("Color mode"), _s("Use color graphics"), @@ -74,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 } }; @@ -89,9 +92,9 @@ static const AdlGameDescription gameDescriptions[] = { AD_LISTEND }, Common::EN_ANY, - Common::kPlatformApple2GS, // FIXME - ADGF_UNSTABLE, - GUIO2(GAMEOPTION_COLOR, GAMEOPTION_SCANLINES) + Common::kPlatformApple2, + ADGF_TESTING, + GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES) }, GAME_TYPE_HIRES1 }, @@ -103,9 +106,9 @@ static const AdlGameDescription gameDescriptions[] = { AD_LISTEND }, Common::EN_ANY, - Common::kPlatformApple2GS, // FIXME - ADGF_UNSTABLE, - GUIO2(GAMEOPTION_COLOR, GAMEOPTION_SCANLINES) + Common::kPlatformApple2, + ADGF_TESTING, + GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES) }, GAME_TYPE_HIRES1 }, @@ -117,9 +120,9 @@ static const AdlGameDescription gameDescriptions[] = { AD_LISTEND }, Common::EN_ANY, - Common::kPlatformApple2GS, // FIXME - ADGF_UNSTABLE, - GUIO2(GAMEOPTION_MONO, GAMEOPTION_SCANLINES) + Common::kPlatformApple2, + ADGF_TESTING, + GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES) }, GAME_TYPE_HIRES2 }, @@ -131,12 +134,27 @@ static const AdlGameDescription gameDescriptions[] = { AD_LISTEND }, Common::EN_ANY, - Common::kPlatformApple2GS, // FIXME - ADGF_UNSTABLE, - GUIO2(GAMEOPTION_MONO, GAMEOPTION_SCANLINES) + Common::kPlatformApple2, + ADGF_TESTING, + GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES) }, 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, @@ -148,9 +166,9 @@ static const AdlGameDescription gameDescriptions[] = { AD_LISTEND }, Common::EN_ANY, - Common::kPlatformApple2GS, // FIXME + Common::kPlatformApple2, ADGF_UNSTABLE, - GUIO2(GAMEOPTION_MONO, GAMEOPTION_SCANLINES) + GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES) }, GAME_TYPE_HIRES6 }, @@ -294,6 +312,8 @@ 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 { @@ -309,6 +329,12 @@ bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD case GAME_TYPE_HIRES2: *engine = HiRes2Engine_create(syst, adlGd); break; + 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 214f76aeae..49e01f9d0f 100644 --- a/engines/adl/disk.cpp +++ b/engines/adl/disk.cpp @@ -28,98 +28,63 @@ namespace Adl { -const DataBlockPtr DiskImage_DSK::getDataBlock(uint track, uint sector, uint offset, uint size) const { - return Common::SharedPtr<DiskImage::DataBlock>(new DiskImage::DataBlock(this, track, sector, offset, size)); -} - -Common::SeekableReadStream *DiskImage_DSK::createReadStream(uint track, uint sector, uint offset, uint size) const { - _f->seek((track * _sectorsPerTrack + sector) * _bytesPerSector + offset); - Common::SeekableReadStream *stream = _f->readStream(size * _bytesPerSector + _bytesPerSector - offset); - - if (_f->eos() || _f->err()) - error("Error reading disk image"); - - return stream; -} - -bool DiskImage_DSK::open(const Common::String &filename) { - assert(!_f->isOpen()); - - if (!_f->open(filename)) - return false; - - uint filesize = _f->size(); - switch (filesize) { - case 143360: - _tracks = 35; - _sectorsPerTrack = 16; - _bytesPerSector = 256; - break; - default: - warning("Unrecognized disk image '%s' of size %d bytes", filename.c_str(), filesize); - return false; +#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) { + Common::File *f = new Common::File; + + if (!f->open(filename)) { + delete f; + return nullptr; } - return true; -} - -const DataBlockPtr DiskImage_NIB::getDataBlock(uint track, uint sector, uint offset, uint size) const { - return Common::SharedPtr<DiskImage::DataBlock>(new DiskImage::DataBlock(this, track, sector, offset, size)); -} - -Common::SeekableReadStream *DiskImage_NIB::createReadStream(uint track, uint sector, uint offset, uint size) const { - _memStream->seek((track * _sectorsPerTrack + sector) * _bytesPerSector + offset); - Common::SeekableReadStream *stream = _memStream->readStream(size * _bytesPerSector + _bytesPerSector - offset); - - if (_memStream->eos() || _memStream->err()) - error("Error reading NIB image"); + if (f->size() != RAW_IMAGE_SIZE(16)) + error("Unrecognized DSK image '%s' of size %d bytes", filename.c_str(), f->size()); - return stream; + return f; } // 4-and-4 encoding (odd-even) -static uint8 read44(Common::SeekableReadStream *f) { +static uint8 read44(Common::SeekableReadStream &f) { // 1s in the other fields, so we can just AND - uint8 ret = f->readByte(); - return ((ret << 1) | 1) & f->readByte(); + uint8 ret = f.readByte(); + return ((ret << 1) | 1) & f.readByte(); } -bool DiskImage_NIB::open(const Common::String &filename) { - assert(!_f->isOpen()); +static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) { + Common::File f; - if (!_f->open(filename)) - return false; + if (!f.open(filename)) + return nullptr; - uint filesize = _f->size(); - switch (filesize) { - case 232960: - _tracks = 35; - _sectorsPerTrack = 16; // we always pad it out - _bytesPerSector = 256; - break; - default: - error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), filesize); - } + if (f.size() != NIB_IMAGE_SIZE) + error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), f.size()); // starting at 0xaa, 32 is invalid (see below) const byte c_5and3_lookup[] = { 32, 0, 32, 1, 2, 3, 32, 32, 32, 32, 32, 4, 5, 6, 32, 32, 7, 8, 32, 9, 10, 11, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 12, 13, 32, 32, 14, 15, 32, 16, 17, 18, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 20, 32, 21, 22, 23, 32, 32, 32, 32, 32, 24, 25, 26, 32, 32, 27, 28, 32, 29, 30, 31 }; // starting at 0x96, 64 is invalid (see below) const byte c_6and2_lookup[] = { 0, 1, 64, 64, 2, 3, 64, 4, 5, 6, 64, 64, 64, 64, 64, 64, 7, 8, 64, 64, 64, 9, 10, 11, 12, 13, 64, 64, 14, 15, 16, 17, 18, 19, 64, 20, 21, 22, 23, 24, 25, 26, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 27, 64, 28, 29, 30, 64, 64, 64, 31, 64, 64, 32, 33, 64, 34, 35, 36, 37, 38, 39, 40, 64, 64, 64, 64, 64, 41, 42, 43, 64, 44, 45, 46, 47, 48, 49, 50, 64, 64, 51, 52, 53, 54, 55, 56, 64, 57, 58, 59, 60, 61, 62, 63 }; - uint32 diskSize = _tracks * _sectorsPerTrack * _bytesPerSector; - byte *diskImage = (byte *)calloc(diskSize, 1); - _memStream = new Common::MemoryReadStream(diskImage, diskSize, DisposeAfterUse::YES); + // we always pad it out + const uint sectorsPerTrack = 16; + byte *diskImage = (byte *)calloc(RAW_IMAGE_SIZE(sectorsPerTrack), 1); bool sawAddress = false; uint8 volNo, track, sector; bool newStyle; - while (_f->pos() < _f->size()) { + while (f.pos() < f.size()) { // Read until we find two sync bytes. - if (_f->readByte() != 0xd5 || _f->readByte() != 0xaa) + if (f.readByte() != 0xd5 || f.readByte() != 0xaa) continue; - byte prologue = _f->readByte(); + byte prologue = f.readByte(); if (sawAddress && (prologue == 0xb5 || prologue == 0x96)) { warning("NIB: data for %02x/%02x/%02x missing", volNo, track, sector); @@ -140,21 +105,13 @@ bool DiskImage_NIB::open(const Common::String &filename) { } } - volNo = read44(_f); - track = read44(_f); - sector = read44(_f); - uint8 checksum = read44(_f); + volNo = read44(f); + track = read44(f); + sector = read44(f); + uint8 checksum = read44(f); if ((volNo ^ track ^ sector) != checksum) error("invalid NIB checksum"); - // FIXME: This is a hires0/hires2-specific hack. - if (volNo == 0xfe) { - if (track == 1) - track = 2; - else if (track == 2) - track = 1; - } - // Epilogue is de/aa plus a gap, but we don't care. continue; } @@ -163,17 +120,17 @@ bool DiskImage_NIB::open(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) * _bytesPerSector; + byte *output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; 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) * _bytesPerSector; + output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; // 6-and-2 uses 342 on-disk bytes byte inbuffer[342]; - _f->read(inbuffer, 342); + f.read(inbuffer, 342); byte oldVal = 0; for (uint n = 0; n < 342; ++n) { @@ -188,7 +145,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { inbuffer[n] = oldVal; } - byte checksum = _f->readByte(); + byte checksum = f.readByte(); if (checksum < 0x96 || oldVal != c_6and2_lookup[checksum - 0x96]) warning("NIB: checksum mismatch @ (%x, %x)", track, sector); @@ -208,7 +165,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { } else { // 5-and-3 uses 410 on-disk bytes, decoding to just over 256 bytes byte inbuffer[410]; - _f->read(inbuffer, 410); + f.read(inbuffer, 410); bool truncated = false; byte oldVal = 0; @@ -218,16 +175,16 @@ bool DiskImage_NIB::open(const Common::String &filename) { if (inbuffer[n] == 0xd5) { // Early end of block. truncated = true; - _f->seek(-(410 - n), SEEK_CUR); - warning("NIB: early end of block @ 0x%x (%x, %x)", _f->pos(), track, sector); + f.seek(-(410 - n), SEEK_CUR); + warning("NIB: early end of block @ 0x%x (%x, %x)", f.pos(), track, sector); break; } byte val = c_5and3_lookup[inbuffer[n] - 0xaa]; if (val == 0x20) { // Badly-encoded nibbles, stop trying to decode here. truncated = true; - warning("NIB: bad nibble %02x @ 0x%x (%x, %x)", inbuffer[n], _f->pos(), track, sector); - _f->seek(-(410 - n), SEEK_CUR); + warning("NIB: bad nibble %02x @ 0x%x (%x, %x)", inbuffer[n], f.pos(), track, sector); + f.seek(-(410 - n), SEEK_CUR); break; } // undo checksum @@ -235,7 +192,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { inbuffer[n] = oldVal; } if (!truncated) { - byte checksum = _f->readByte(); + byte checksum = f.readByte(); if (checksum < 0xaa || oldVal != c_5and3_lookup[checksum - 0xaa]) warning("NIB: checksum mismatch @ (%x, %x)", track, sector); } @@ -259,7 +216,52 @@ bool DiskImage_NIB::open(const Common::String &filename) { } } - return true; + return new Common::MemoryReadStream(diskImage, RAW_IMAGE_SIZE(sectorsPerTrack), 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")) + _stream = readImage_NIB(filename); + + return _stream != nullptr; +} + +const DataBlockPtr DiskImage::getDataBlock(uint track, uint sector, uint offset, uint size) const { + return DataBlockPtr(new DiskImage::DataBlock(this, track, sector, offset, size, _mode13)); +} + +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; + 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); + + 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); + + if (bytesToRead - dataOffset < bytesRemInTrack) + bytesRemInTrack = bytesToRead - dataOffset; + + if (_stream->read(data + dataOffset, bytesRemInTrack) < bytesRemInTrack) + error("Error reading disk image"); + + ++track; + + sector = 0; + offset = 0; + + dataOffset += bytesRemInTrack; + } + + return new Common::MemoryReadStream(data, bytesToRead, DisposeAfterUse::YES); } const DataBlockPtr Files_Plain::getDataBlock(const Common::String &filename, uint offset) const { @@ -449,7 +451,7 @@ Common::SeekableReadStream *Files_DOS33::createReadStream(const Common::String & } bool Files_DOS33::open(const Common::String &filename) { - _disk = new DiskImage_DSK(); + _disk = new DiskImage(); if (!_disk->open(filename)) return false; diff --git a/engines/adl/disk.h b/engines/adl/disk.h index 43b9e387ba..1041f0cebd 100644 --- a/engines/adl/disk.h +++ b/engines/adl/disk.h @@ -73,41 +73,41 @@ protected: class DiskImage { public: DiskImage() : - _tracks(0), - _sectorsPerTrack(0), - _bytesPerSector(0) { - _f = new Common::File(); - } + _stream(nullptr), + _mode13(false) { } - virtual ~DiskImage() { - delete _f; + ~DiskImage() { + delete _stream; } - virtual bool open(const Common::String &filename) = 0; - virtual const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const = 0; - virtual Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0) const = 0; + 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; } protected: class DataBlock : public Adl::DataBlock { public: - DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size) : + DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size, bool mode13) : _track(track), _sector(sector), _offset(offset), _size(size), + _mode13(mode13), _disk(disk) { } Common::SeekableReadStream *createReadStream() const { - return _disk->createReadStream(_track, _sector, _offset, _size); + return _disk->createReadStream(_track, _sector, _offset, _size, (_mode13 ? 13 : 16)); } private: uint _track, _sector, _offset, _size; + bool _mode13; const DiskImage *_disk; }; - Common::File *_f; - uint _tracks, _sectorsPerTrack, _bytesPerSector; + Common::SeekableReadStream *_stream; + bool _mode13; // Older 13-sector format }; // Data in plain files @@ -117,30 +117,6 @@ public: Common::SeekableReadStream *createReadStream(const Common::String &filename, uint offset = 0) const; }; -// .DSK disk image - 35 tracks, 16 sectors per track, 256 bytes per sector -class DiskImage_DSK : public DiskImage { -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) const; -}; - -// .NIB disk image -class DiskImage_NIB : public DiskImage { -public: - DiskImage_NIB() : _memStream(nullptr) { } - virtual ~DiskImage_NIB() { - delete _memStream; - } - - 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) const; - -private: - Common::SeekableReadStream *_memStream; -}; - // Data in files contained in Apple DOS 3.3 disk image class Files_DOS33 : public Files { public: diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp index b7f6eb9923..2cf50f72fc 100644 --- a/engines/adl/display.cpp +++ b/engines/adl/display.cpp @@ -55,6 +55,9 @@ static const byte colorPalette[COLOR_PALETTE_ENTRIES * 3] = { 0xf2, 0x5e, 0x00 }; +// Opacity of the optional scanlines (percentage) +#define SCANLINE_OPACITY 75 + // Corresponding color in second palette #define PAL2(X) ((X) | 0x04) @@ -109,8 +112,6 @@ Display::Display() : _cursorPos(0), _showCursor(false) { - initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true); - _monochrome = !ConfMan.getBool("color"); _scanlines = ConfMan.getBool("scanlines"); @@ -135,6 +136,8 @@ Display::Display() : _textBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8()); createFont(); + + _startMillis = g_system->getMillis(); } Display::~Display() { @@ -334,14 +337,16 @@ void Display::writeFrameBuffer(const Common::Point &p, byte color, byte mask) { } void Display::showScanlines(bool enable) { - byte pal[COLOR_PALETTE_ENTRIES * 3] = { }; + byte pal[COLOR_PALETTE_ENTRIES * 3]; - if (enable) - g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES); - else { - g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES); - g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES); + g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES); + + if (enable) { + for (uint i = 0; i < ARRAYSIZE(pal); ++i) + pal[i] = pal[i] * (100 - SCANLINE_OPACITY) / 100; } + + g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES); } static byte processColorBits(uint16 &bits, bool &odd, bool secondPal) { @@ -489,7 +494,11 @@ void Display::updateTextSurface() { r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2); if (!(c & 0x80)) { - if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1)) + // Blink text. We subtract _startMillis to make this compatible + // with the event recorder, which returns offsetted values on + // playback. + const uint32 millisPassed = g_system->getMillis() - _startMillis; + if (!(c & 0x40) || ((millisPassed / 270) & 1)) r.translate(0, 4 * 8 * 2); } diff --git a/engines/adl/display.h b/engines/adl/display.h index bc27b7cb6b..e761e63f2e 100644 --- a/engines/adl/display.h +++ b/engines/adl/display.h @@ -102,6 +102,7 @@ private: Graphics::Surface *_font; uint _cursorPos; bool _showCursor; + uint32 _startMillis; }; } // End of namespace Adl diff --git a/engines/adl/hires0.cpp b/engines/adl/hires0.cpp new file mode 100644 index 0000000000..a348779e89 --- /dev/null +++ b/engines/adl/hires0.cpp @@ -0,0 +1,168 @@ +/* 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 diff --git a/engines/adl/hires0.h b/engines/adl/hires0.h new file mode 100644 index 0000000000..a3d8845a4e --- /dev/null +++ b/engines/adl/hires0.h @@ -0,0 +1,59 @@ +/* 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 096d8ef496..26565c03c3 100644 --- a/engines/adl/hires1.cpp +++ b/engines/adl/hires1.cpp @@ -338,6 +338,7 @@ void HiRes1Engine::loadRoom(byte roomNr) { } void HiRes1Engine::showRoom() { + _state.curPicture = getCurRoom().curPicture; clearScreen(); loadRoom(_state.room); diff --git a/engines/adl/hires2.cpp b/engines/adl/hires2.cpp index d8e8a65e29..14db237d82 100644 --- a/engines/adl/hires2.cpp +++ b/engines/adl/hires2.cpp @@ -34,6 +34,10 @@ namespace Adl { 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); StreamPtr stream(_disk->createReadStream(0x00, 0xd, 0x17, 1)); _display->setMode(DISPLAY_MODE_TEXT); @@ -45,15 +49,19 @@ void HiRes2Engine::runIntro() const { _display->printString(str); delay(2000); + + _disk->setMode13(true); } void HiRes2Engine::init() { _graphics = new Graphics_v2(*_display); - _disk = new DiskImage_DSK(); + _disk = new DiskImage(); if (!_disk->open(IDS_HR2_DISK_IMAGE)) error("Failed to open disk image '" IDS_HR2_DISK_IMAGE "'"); + _disk->setMode13(true); + StreamPtr stream(_disk->createReadStream(0x1f, 0x2, 0x00, 4)); for (uint i = 0; i < IDI_HR2_NUM_MESSAGES; ++i) diff --git a/engines/adl/hires4.cpp b/engines/adl/hires4.cpp new file mode 100644 index 0000000000..22fd9c2f81 --- /dev/null +++ b/engines/adl/hires4.cpp @@ -0,0 +1,50 @@ +/* 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/hires4.h" +#include "adl/display.h" +#include "adl/graphics.h" +#include "adl/disk.h" + +namespace Adl { + +void HiRes4Engine::runIntro() const { +} + +void HiRes4Engine::init() { + _graphics = new Graphics_v2(*_display); +} + +void HiRes4Engine::initGameState() { +} + +Engine *HiRes4Engine_create(OSystem *syst, const AdlGameDescription *gd) { + return new HiRes4Engine(syst, gd); +} + +} // End of namespace Adl diff --git a/engines/adl/hires4.h b/engines/adl/hires4.h new file mode 100644 index 0000000000..f1c429ce38 --- /dev/null +++ b/engines/adl/hires4.h @@ -0,0 +1,45 @@ +/* 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_HIRES4_H +#define ADL_HIRES4_H + +#include "common/str.h" + +#include "adl/adl_v3.h" + +namespace Adl { + +class HiRes4Engine : public AdlEngine_v3 { +public: + HiRes4Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine_v3(syst, gd) { } + +private: + // AdlEngine + void runIntro() const; + void init(); + void initGameState(); +}; + +} // End of namespace Adl + +#endif diff --git a/engines/adl/hires6.cpp b/engines/adl/hires6.cpp index c42b4165a6..e9df7b513a 100644 --- a/engines/adl/hires6.cpp +++ b/engines/adl/hires6.cpp @@ -69,7 +69,7 @@ static Common::MemoryReadStream *loadSectors(DiskImage *disk, byte track, byte s } void HiRes6Engine::runIntro() const { - DiskImage_DSK *boot(new DiskImage_DSK()); + DiskImage *boot(new DiskImage()); if (!boot->open(disks[0])) error("Failed to open disk image '%s'", disks[0]); @@ -109,7 +109,7 @@ void HiRes6Engine::runIntro() const { } void HiRes6Engine::init() { - _boot = new DiskImage_DSK(); + _boot = new DiskImage(); _graphics = new Graphics_v2(*_display); if (!_boot->open(disks[0])) @@ -177,7 +177,7 @@ void HiRes6Engine::init() { void HiRes6Engine::loadDisk(byte disk) { delete _disk; - _disk = new DiskImage_NIB(); + _disk = new DiskImage(); if (!_disk->open(disks[disk])) error("Failed to open disk image '%s'", disks[disk]); @@ -317,6 +317,8 @@ void HiRes6Engine::initGameState() { } void HiRes6Engine::showRoom() { + _state.curPicture = getCurRoom().curPicture; + bool redrawPic = false; if (getVar(26) == 0xfe) diff --git a/engines/adl/hires6.h b/engines/adl/hires6.h index 4bd2bcc7cc..5ff039120b 100644 --- a/engines/adl/hires6.h +++ b/engines/adl/hires6.h @@ -25,7 +25,7 @@ #include "common/str.h" -#include "adl/adl_v3.h" +#include "adl/adl_v4.h" #include "adl/disk.h" namespace Common { @@ -56,10 +56,10 @@ struct DiskDataDesc { byte volume; }; -class HiRes6Engine : public AdlEngine_v3 { +class HiRes6Engine : public AdlEngine_v4 { public: HiRes6Engine(OSystem *syst, const AdlGameDescription *gd) : - AdlEngine_v3(syst, gd), + AdlEngine_v4(syst, gd), _boot(nullptr), _currVerb(0), _currNoun(0) { @@ -82,7 +82,7 @@ private: void loadDisk(byte disk); - DiskImage_DSK *_boot; + DiskImage *_boot; byte _currVerb, _currNoun; Common::Array<DiskDataDesc> _diskDataDesc; }; diff --git a/engines/adl/module.mk b/engines/adl/module.mk index 7ab37efc67..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 \ @@ -11,8 +12,10 @@ MODULE_OBJS := \ graphics.o \ graphics_v1.o \ graphics_v2.o \ + hires0.o \ hires1.o \ hires2.o \ + hires4.o \ hires6.o \ speaker.o |