aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/adl/adl.cpp155
-rw-r--r--engines/adl/adl.h98
-rw-r--r--engines/adl/adl_v1.cpp690
-rw-r--r--engines/adl/adl_v1.h133
-rw-r--r--engines/adl/configure.engine3
-rw-r--r--engines/adl/detection.cpp108
-rw-r--r--engines/adl/display.cpp611
-rw-r--r--engines/adl/display.h103
-rw-r--r--engines/adl/module.mk19
-rw-r--r--engines/adl/parser.cpp172
-rw-r--r--engines/adl/parser.h66
11 files changed, 2158 insertions, 0 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp
new file mode 100644
index 0000000000..be3d8d7151
--- /dev/null
+++ b/engines/adl/adl.cpp
@@ -0,0 +1,155 @@
+/* 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/scummsys.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/fs.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/stream.h"
+#include "graphics/palette.h"
+
+#include "engines/util.h"
+
+#include "adl/adl.h"
+#include "adl/display.h"
+#include "adl/parser.h"
+
+namespace Adl {
+
+Common::String asciiToApple(Common::String str) {
+ Common::String ret(str);
+ Common::String::iterator it;
+
+ for (it = ret.begin(); it != ret.end(); ++it)
+ *it = *it | 0x80;
+
+ return ret;
+}
+
+Common::String appleToAscii(Common::String str) {
+ Common::String ret(str);
+ Common::String::iterator it;
+
+ for (it = ret.begin(); it != ret.end(); ++it)
+ *it = *it & 0x7f;
+
+ return ret;
+}
+
+AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
+ Engine(syst),
+ _gameDescription(gd),
+ _console(nullptr),
+ _display(nullptr) {
+ // Put your engine in a sane state, but do nothing big yet;
+ // in particular, do not load data from files; rather, if you
+ // need to do such things, do them from run().
+
+ // Do not initialize graphics here
+ // Do not initialize audio devices here
+
+ // However this is the place to specify all default directories
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "sound");
+
+ // Don't forget to register your random source
+ _rnd = new Common::RandomSource("adl");
+
+ debug("AdlEngine::AdlEngine");
+}
+
+AdlEngine::~AdlEngine() {
+ debug("AdlEngine::~AdlEngine");
+
+ delete _rnd;
+ delete _console;
+ delete _display;
+
+ DebugMan.clearAllDebugChannels();
+}
+
+Common::Error AdlEngine::run() {
+ initGraphics(560, 384, true);
+
+ byte palette[6 * 3] = {
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0xc7, 0x34, 0xff,
+ 0x38, 0xcb, 0x00,
+ 0x00, 0x00, 0xff, // FIXME
+ 0xff, 0xa5, 0x00 // FIXME
+ };
+
+ g_system->getPaletteManager()->setPalette(palette, 0, 6);
+
+ _console = new Console(this);
+ _display = new Display();
+ _parser = new Parser(*this, *_display);
+
+ runGame();
+
+ return Common::kNoError;
+}
+
+Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) {
+ Common::String str;
+
+ while (1) {
+ byte b = stream.readByte();
+
+ if (stream.eos() || stream.err() || b == until)
+ break;
+
+ str += b;
+ };
+
+ return str;
+}
+
+void AdlEngine::printStrings(Common::SeekableReadStream &stream, int count) {
+ while (1) {
+ Common::String str = readString(stream);
+ _display->printString(str);
+
+ if (--count == 0)
+ break;
+
+ stream.seek(3, SEEK_CUR);
+ };
+}
+
+AdlEngine *AdlEngine::create(GameType type, OSystem *syst, const AdlGameDescription *gd) {
+ switch(type) {
+ case kGameTypeAdl1:
+ return AdlEngine_v1__create(syst, gd);
+ default:
+ error("Unknown GameType");
+ }
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/adl.h b/engines/adl/adl.h
new file mode 100644
index 0000000000..d5a518faa7
--- /dev/null
+++ b/engines/adl/adl.h
@@ -0,0 +1,98 @@
+/* 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_H
+#define ADL_ADL_H
+
+#include "common/random.h"
+#include "engines/engine.h"
+#include "gui/debugger.h"
+
+namespace Common {
+class ReadStream;
+class SeekableReadStream;
+}
+
+namespace Adl {
+
+class Display;
+class Parser;
+class Console;
+struct AdlGameDescription;
+
+enum GameType {
+ kGameTypeNone = 0,
+ kGameTypeAdl1
+};
+
+Common::String asciiToApple(Common::String str);
+Common::String appleToAscii(Common::String str);
+
+enum {
+ STR_COMMON_ENTERCMD,
+ STR_COMMON_VERBERR,
+ STR_COMMON_NOUNERR,
+ STR_CUSTOM_START
+};
+
+#define A2CHAR(C) ((C) | 0x80)
+
+class AdlEngine : public Engine {
+public:
+ AdlEngine(OSystem *syst, const AdlGameDescription *gd);
+ virtual ~AdlEngine();
+
+ const AdlGameDescription *_gameDescription;
+ uint32 getFeatures() const;
+ const char *getGameId() const;
+
+ static AdlEngine *create(GameType type, OSystem *syst, const AdlGameDescription *gd);
+
+ Common::Error run();
+ virtual Common::String getExeString(uint id) = 0;
+
+protected:
+ virtual void runGame() = 0;
+ Common::String readString(Common::ReadStream &stream, byte until = 0);
+ void printStrings(Common::SeekableReadStream &stream, int count = 1);
+ Display *_display;
+ Parser *_parser;
+
+private:
+ Console *_console;
+
+ // We need random numbers
+ Common::RandomSource *_rnd;
+};
+
+// Example console class
+class Console : public GUI::Debugger {
+public:
+ Console(AdlEngine *vm) {}
+ virtual ~Console(void) {}
+};
+
+AdlEngine *AdlEngine_v1__create(OSystem *syst, const AdlGameDescription *gd);
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/adl_v1.cpp b/engines/adl/adl_v1.cpp
new file mode 100644
index 0000000000..61671e5d7f
--- /dev/null
+++ b/engines/adl/adl_v1.cpp
@@ -0,0 +1,690 @@
+/* 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/scummsys.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/fs.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/stream.h"
+#include "graphics/palette.h"
+
+#include "engines/util.h"
+
+#include "adl/adl_v1.h"
+#include "adl/display.h"
+#include "adl/parser.h"
+
+namespace Adl {
+
+static uint exeStrings[STR_MH_TOTAL] = {
+ 23484, 23375, 23438, 27658, 0x6c31, 27729, 27772, 0x5f1e
+};
+
+AdlEngine_v1::AdlEngine_v1(OSystem *syst, const AdlGameDescription *gd) :
+ AdlEngine(syst, gd),
+ _state(kIntro),
+ _room(1),
+ _steps(1),
+ _isDark(false) {
+ _variables.resize(20);
+}
+
+void AdlEngine_v1::runIntro() {
+ Common::File file;
+
+ if (!file.open("AUTO LOAD OBJ"))
+ error("Failed to open file");
+
+ file.seek(0x1003);
+ _display->setMode(Display::kModeHires);
+ _display->loadFrameBuffer(file);
+ _display->decodeFrameBuffer();
+ _display->delay(4000);
+
+ if (shouldQuit())
+ return;
+
+ _display->setMode(Display::kModeText);
+
+ Common::File basic;
+ if (!basic.open("MYSTERY.HELLO"))
+ error("Failed to open file");
+
+ Common::String str;
+
+ basic.seek(93);
+ str = readString(basic, '"');
+ _display->printASCIIString(str + '\r');
+
+ basic.seek(299);
+ str = readString(basic, '"');
+ _display->printASCIIString(str + "\r\r");
+
+ basic.seek(365);
+ str = readString(basic, '"');
+ _display->printASCIIString(str + "\r\r");
+
+ basic.seek(601);
+ str = readString(basic, '"');
+ _display->printASCIIString(str + '\r');
+
+ _display->inputKey();
+ if (g_engine->shouldQuit())
+ return;
+
+ _display->setMode(Display::kModeMixed);
+
+ file.seek(15);
+ str = readString(file);
+
+ while (1) {
+ _display->printString(str);
+ Common::String s = _display->inputString();
+
+ if (g_engine->shouldQuit())
+ break;
+
+ if (s.empty())
+ continue;
+
+ if ((byte)s[0] == ('I' | 0x80))
+ break;
+ else if ((byte)s[0] == ('G' | 0x80))
+ return;
+ };
+
+ _display->setMode(Display::kModeText);
+ file.seek(102);
+
+ const int pages[] = { 6, 6, 4, 5, 8, 7, 0 };
+
+ int page = 0;
+ while (pages[page] != 0) {
+ _display->home();
+ printStrings(file, pages[page++]);
+ _display->inputString();
+
+ if (g_engine->shouldQuit())
+ return;
+
+ file.seek(9, SEEK_CUR);
+ }
+}
+
+void AdlEngine_v1::drawPic(Common::ReadStream &stream, byte xOffset, byte yOffset) {
+ byte x, y;
+ bool bNewLine = false;
+ byte oldX = 0, oldY = 0;
+ while (1) {
+ x = stream.readByte();
+ y = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Failed to read picture");
+
+ if (x == 0xff && y == 0xff)
+ return;
+
+ if (x == 0 && y == 0) {
+ bNewLine = true;
+ continue;
+ }
+
+ x += xOffset;
+ y += yOffset;
+
+ if (y > 160)
+ y = 160;
+
+ if (bNewLine) {
+ _display->drawPixel(x, y, 0x7f);
+ bNewLine = false;
+ } else {
+ _display->drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+}
+
+void AdlEngine_v1::drawPic(byte pic, byte xOffset, byte yOffset) {
+ Common::File f;
+ Common::String name = Common::String::format("BLOCK%i", _pictures[pic].block);
+
+ if (!f.open(name))
+ error("Failed to open file");
+
+ f.seek(_pictures[pic].offset);
+ drawPic(f, xOffset, yOffset);
+}
+
+void AdlEngine_v1::drawItems() {
+ Common::Array<Item>::const_iterator it;
+
+ uint dropped = 0;
+
+ for (it = _inventory.begin(); it != _inventory.end(); ++it) {
+ if (it->field2 != _room)
+ continue;
+
+ if (it->field7 == 1) {
+ if (_rooms[_room].field8 == _rooms[_room].picture) {
+ const Common::Point &p = _itemOffsets[dropped];
+ if (it->field4)
+ _display->drawRightAngles(_drawings[it->field3 - 1], Common::Point(p.x, p.y), 0, 1, 0x7f);
+ else
+ drawPic(it->field3, p.x, p.y);
+ ++dropped;
+ }
+ continue;
+ }
+
+ Common::Array<byte>::const_iterator it2;
+
+ for (it2 = it->field10.begin(); it2 != it->field10.end(); ++it2) {
+ if (*it2 == _rooms[_room].picture) {
+ if (it->field4)
+ _display->drawRightAngles(_drawings[it->field3 - 1], Common::Point(it->field5, it->field6), 0, 1, 0x7f);
+ else
+ drawPic(it->field3, it->field5, it->field6);
+ continue;
+ }
+ }
+ }
+}
+
+void AdlEngine_v1::showRoom() {
+ if (!_isDark) {
+ drawPic(_rooms[_room].picture, 0, 0);
+ drawItems();
+ }
+
+ _display->decodeFrameBuffer();
+ printMessage(_rooms[_room].description, false);
+}
+
+Common::String AdlEngine_v1::getExeString(uint idx) {
+ return _exeStrings[idx];
+}
+
+void AdlEngine_v1::wordWrap(Common::String &str) {
+ uint end = 39;
+
+ while (1) {
+ if (str.size() <= end)
+ return;
+
+ while (str[end] != (char)A2CHAR(' '))
+ --end;
+
+ str.setChar((char)A2CHAR('\r'), end);
+ end += 40;
+ }
+}
+
+void AdlEngine_v1::printMessage(uint idx, bool wait) {
+ // Hardcoded overrides that don't wait after printing
+ // Note: strings may differ slightly from the ones in MESSAGES
+ switch (idx) {
+ case 137:
+ _display->printString(_exeStrings[STR_MH_DIRERR]);
+ return;
+ case 127:
+ _display->printString(_exeStrings[STR_MH_DONTHAVEIT]);
+ return;
+ case 37:
+ _display->printString(_exeStrings[STR_MH_DONTUNDERSTAND]);
+ return;
+ case 7:
+ _display->printString(_exeStrings[STR_MH_GETTINGDARK]);
+ return;
+ }
+
+ Common::String msg = _msgStrings[idx - 1];
+ wordWrap(msg);
+ _display->printString(msg);
+
+ if (wait)
+ _display->delay(14 * 166018 / 1000);
+}
+
+void AdlEngine_v1::readCommands(Common::ReadStream &stream, Commands &commands) {
+ while (1) {
+ Command command;
+ command.room = stream.readByte();
+
+ if (command.room == 0xff)
+ return;
+
+ command.verb = stream.readByte();
+ command.noun = stream.readByte();
+
+ byte scriptSize = stream.readByte() - 6;
+
+ command.numCond = stream.readByte();
+ command.numAct = stream.readByte();
+
+ for (uint i = 0; i < scriptSize; ++i)
+ command.script.push_back(stream.readByte());
+
+ if (stream.eos() || stream.err())
+ error("Failed to read commands");
+
+ commands.push_back(command);
+ }
+}
+
+void AdlEngine_v1::takeItem(byte noun) {
+ Common::Array<Item>::iterator it;
+
+ for (it = _inventory.begin(); it != _inventory.end(); ++it) {
+ if (it->field1 != noun || it->field2 != _room)
+ continue;
+
+ if (it->field7 == 2) {
+ // It doesn't move
+ printMessage(151);
+ return;
+ }
+
+ if (it->field7 == 1) {
+ it->field2 = 0xfe;
+ it->field7 = 1;
+ return;
+ }
+
+ Common::Array<byte>::const_iterator it2;
+ for (it2 = it->field10.begin(); it->field10.end(); ++it2) {
+ if (*it2 == _rooms[_room].picture) {
+ it->field2 = 0xfe;
+ it->field7 = 1;
+ return;
+ }
+ }
+ }
+
+ // Item not here
+ printMessage(152);
+}
+
+void AdlEngine_v1::dropItem(byte noun) {
+ Common::Array<Item>::iterator it;
+
+ for (it = _inventory.begin(); it != _inventory.end(); ++it) {
+ if (it->field1 != noun || it->field2 != 0xfe)
+ continue;
+
+ it->field2 = _room;
+ it->field7 = 1;
+ return;
+ }
+
+ // Don't understand
+ printMessage(37);
+}
+
+void AdlEngine_v1::doActions(const Command &command, byte noun, byte offset) {
+ for (uint i = 0; i < command.numAct; ++i) {
+ switch (command.script[offset]) {
+ case 1:
+ _variables[command.script[offset + 2]] += command.script[offset + 1];
+ offset += 3;
+ break;
+ case 2:
+ _variables[command.script[offset + 2]] -= command.script[offset + 1];
+ offset += 3;
+ break;
+ case 3:
+ _variables[command.script[offset + 1]] = command.script[offset + 2];
+ offset += 3;
+ break;
+ case 4: {
+ Common::Array<Item>::const_iterator it;
+
+ for (it = _inventory.begin(); it != _inventory.end(); ++it)
+ if (it->field2 == 0xfe)
+ printMessage(it->field8);
+
+ ++offset;
+ break;
+ }
+ case 5:
+ _inventory[command.script[offset + 1] - 1].field2 = command.script[offset + 2];
+ offset += 3;
+ break;
+ case 6:
+ _rooms[_room].picture = _rooms[_room].field8;
+ _room = command.script[offset + 1];
+ offset += 2;
+ break;
+ case 7:
+ _rooms[_room].picture = command.script[offset + 1];
+ offset += 2;
+ break;
+ case 8:
+ _rooms[_room].field8 = _rooms[_room].picture = command.script[offset + 1];
+ offset += 2;
+ break;
+ case 9:
+ printMessage(command.script[offset + 1]);
+ offset += 2;
+ break;
+ case 0xa:
+ _isDark = false;
+ ++offset;
+ break;
+ case 0xb:
+ _isDark = true;
+ ++offset;
+ break;
+ case 0xf:
+ warning("Save game not implemented");
+ ++offset;
+ break;
+ case 0x10:
+ warning("Load game not implemented");
+ ++offset;
+ break;
+ case 0x11: {
+ _display->printString(_exeStrings[STR_MH_PLAYAGAIN]);
+ Common::String input = _display->inputString();
+ if (input.size() == 0 || input[0] != (char)A2CHAR('N')) {
+ warning("Restart game not implemented");
+ return;
+ }
+ // Fall-through
+ }
+ case 0xd:
+ printMessage(140);
+ quitGame();
+ return;
+ case 0x12: {
+ byte item = command.script[offset + 1] - 1;
+ _inventory[item].field2 = command.script[offset + 2];
+ _inventory[item].field5 = command.script[offset + 3];
+ _inventory[item].field6 = command.script[offset + 4];
+ offset += 5;
+ break;
+ }
+ case 0x13: {
+ byte item = command.script[offset + 2] - 1;
+ _inventory[item].field3 = command.script[offset + 1];
+ offset += 3;
+ break;
+ }
+ case 0x14:
+ _rooms[_room].picture = _rooms[_room].field8;
+ ++offset;
+ break;
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1a: {
+ byte room = _rooms[_room].connections[command.script[offset] - 0x15];
+
+ if (room == 0) {
+ printMessage(137);
+ return;
+ }
+
+ _rooms[_room].picture = _rooms[_room].field8;
+ _room = room;
+ return;
+ }
+ case 0x1b:
+ takeItem(noun);
+ ++offset;
+ break;
+ case 0x1c:
+ dropItem(noun);
+ ++offset;
+ break;
+ case 0x1d:
+ _rooms[command.script[offset + 1]].field8 = _rooms[command.script[offset + 1]].picture = command.script[offset + 2];
+ offset += 3;
+ break;
+ default:
+ error("Invalid action opcode %02x", command.script[offset]);
+ }
+ }
+}
+
+bool AdlEngine_v1::checkCommand(const Command &command, byte verb, byte noun) {
+ if (command.room != 0xfe && command.room != _room)
+ return false;
+
+ if (command.verb != 0xfe && command.verb != verb)
+ return false;
+
+ if (command.noun != 0xfe && command.noun != noun)
+ return false;
+
+ uint offset = 0;
+ for (uint i = 0; i < command.numCond; ++i) {
+ switch (command.script[offset]) {
+ case 3:
+ if (_inventory[command.script[offset + 1] - 1].field2 != command.script[offset + 2])
+ return false;
+ offset += 3;
+ break;
+ case 5:
+ if (command.script[offset + 1] > _steps)
+ return false;
+ offset += 2;
+ break;
+ case 6:
+ if (_variables[command.script[offset + 1]] != command.script[offset + 2])
+ return false;
+ offset += 3;
+ break;
+ case 9:
+ if (_rooms[_room].picture != command.script[offset + 1])
+ return false;
+ offset += 2;
+ break;
+ case 10:
+ if (_inventory[command.script[offset + 1] - 1].field3 != command.script[offset + 2])
+ return false;
+ offset += 3;
+ break;
+ default:
+ error("Invalid condition opcode %02x", command.script[offset]);
+ }
+ }
+
+ doActions(command, noun, offset);
+
+ return true;
+}
+
+bool AdlEngine_v1::doOneCommand(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator it;
+
+ for (it = commands.begin(); it != commands.end(); ++it) {
+ if (checkCommand(*it, verb, noun)) {
+ debug("Found match: %i %i %i", it->room, it->verb, it->noun);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AdlEngine_v1::doAllCommands(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator it;
+
+ for (it = commands.begin(); it != commands.end(); ++it) {
+ if (checkCommand(*it, verb, noun)) {
+ debug("Found match: %i %i %i", it->room, it->verb, it->noun);
+ }
+ }
+}
+
+void AdlEngine_v1::clearScreen() {
+ _display->setMode(Display::kModeMixed);
+ _display->clear(0x00);
+}
+
+void AdlEngine_v1::runGame() {
+ runIntro();
+ _display->printASCIIString("\r");
+
+ Common::File f;
+
+ if (!f.open("MESSAGES"))
+ error("Failed to open file");
+
+ while (!f.eos() && !f.err())
+ _msgStrings.push_back(readString(f, A2CHAR('\r')) + (char)A2CHAR('\r'));
+
+ f.close();
+
+ if (!f.open("ADVENTURE"))
+ error("Failed to open file");
+
+ // Load strings from executable
+ for (uint idx = 0; idx < STR_MH_TOTAL; ++idx) {
+ f.seek(exeStrings[idx]);
+ _exeStrings.push_back(readString(f));
+ }
+
+ // Load room data from executable
+ f.seek(1280);
+ for (uint i = 0; i < MH_ROOMS; ++i) {
+ struct Room room;
+ f.readByte();
+ room.description = f.readByte();
+ for (uint j = 0; j < 6; ++j)
+ room.connections[j] = f.readByte();
+ room.field8 = f.readByte();
+ room.picture = f.readByte();
+ _rooms.push_back(room);
+ }
+
+ // Load inventory data from executable
+ f.seek(0x100);
+ while (f.readByte() != 0xff) {
+ struct Item item;
+ item.field1 = f.readByte();
+ item.field2 = f.readByte();
+ item.field3 = f.readByte();
+ item.field4 = f.readByte();
+ item.field5 = f.readByte();
+ item.field6 = f.readByte();
+ item.field7 = f.readByte();
+ item.field8 = f.readByte();
+
+ f.readByte();
+
+ byte size = f.readByte();
+
+ for (uint i = 0; i < size; ++i)
+ item.field10.push_back(f.readByte());
+
+ _inventory.push_back(item);
+ }
+
+ // Load picture data from executable
+ f.seek(0x4b00);
+ for (uint i = 0; i < MH_PICS; ++i) {
+ struct Picture pic;
+ pic.block = f.readByte();
+ pic.offset = f.readUint16LE();
+ _pictures.push_back(pic);
+ }
+
+ // Load commands from executable
+ f.seek(0x3D00);
+ readCommands(f, _roomCommands);
+
+ f.seek(0x3C00);
+ readCommands(f, _globalCommands);
+
+ // Load dropped item offsets
+ f.seek(0x68ff);
+ for (uint i = 0; i < MH_ITEM_OFFSETS; ++i) {
+ Common::Point p;
+ p.x = f.readByte();
+ p.y = f.readByte();
+ _itemOffsets.push_back(p);
+ }
+
+ // Load right-angle drawings
+ f.seek(0x4f00);
+ uint16 drawingsTotal = f.readUint16LE();
+ for (uint i = 0; i < drawingsTotal; ++i) {
+ f.seek(0x4f00 + 2 + i * 2);
+ uint16 offset = f.readUint16LE();
+ f.seek(0x4f00 + offset);
+
+ Common::Array<byte> drawing;
+ byte b = f.readByte();
+ while (b != 0) {
+ drawing.push_back(b);
+ b = f.readByte();
+ }
+ _drawings.push_back(drawing);
+ }
+
+ // Title screen shown during loading
+ f.seek(0x1800);
+ _display->loadFrameBuffer(f);
+ _display->decodeFrameBuffer();
+ _display->delay(2000);
+
+ f.seek(0x3800);
+ _parser->loadVerbs(f);
+
+ f.seek(0xf00);
+ _parser->loadNouns(f);
+
+ while (1) {
+ uint verb = 0, noun = 0;
+ clearScreen();
+ showRoom();
+ _parser->getInput(verb, noun);
+
+ if (!doOneCommand(_roomCommands, verb, noun))
+ printMessage(37);
+ doAllCommands(_globalCommands, verb, noun);
+
+ _steps++;
+
+ if (shouldQuit())
+ return;
+ }
+}
+
+AdlEngine *AdlEngine_v1__create(OSystem *syst, const AdlGameDescription *gd) {
+ return new AdlEngine_v1(syst, gd);
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/adl_v1.h b/engines/adl/adl_v1.h
new file mode 100644
index 0000000000..b8f4c536a5
--- /dev/null
+++ b/engines/adl/adl_v1.h
@@ -0,0 +1,133 @@
+/* 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_V1_H
+#define ADL_ADL_V1_H
+
+#include "adl/adl.h"
+
+namespace Common {
+class ReadStream;
+}
+
+namespace Adl {
+
+enum {
+ // Some of these are probably common
+ STR_MH_DIRERR = STR_CUSTOM_START,
+ STR_MH_DONTHAVEIT,
+ STR_MH_DONTUNDERSTAND,
+ STR_MH_GETTINGDARK,
+ STR_MH_PLAYAGAIN,
+
+ STR_MH_TOTAL
+};
+
+class AdlEngine_v1 : public AdlEngine {
+public:
+ AdlEngine_v1(OSystem *syst, const AdlGameDescription *gd);
+ Common::String getExeString(uint idx);
+
+protected:
+ void runGame();
+
+private:
+ enum {
+ MH_ROOMS = 42,
+ MH_PICS = 98,
+ MH_ITEM_OFFSETS = 21
+ };
+
+ enum State {
+ kIntro,
+ kIdle
+ };
+
+ struct Room {
+ byte description;
+ byte connections[6];
+ byte field8;
+ byte picture;
+ };
+
+ struct Picture {
+ byte block;
+ uint16 offset;
+ };
+
+ struct Command {
+ byte room;
+ byte verb, noun;
+ byte numCond, numAct;
+ Common::Array<byte> script;
+ };
+
+ struct Item {
+ byte field1;
+ byte field2;
+ byte field3;
+ byte field4;
+ byte field5;
+ byte field6;
+ byte field7;
+ byte field8;
+ Common::Array<byte> field10;
+ };
+
+ typedef Common::List<Command> Commands;
+
+ int _state;
+
+ void runIntro();
+ void drawPic(Common::ReadStream &stream, byte xOffset, byte yOffset);
+ void showRoom();
+ void printMessage(uint idx, bool wait = true);
+ void wordWrap(Common::String &str);
+ void readCommands(Common::ReadStream &stream, Commands &commands);
+ bool checkCommand(const Command &command, byte verb, byte noun);
+ bool doOneCommand(const Commands &commands, byte verb, byte noun);
+ void doAllCommands(const Commands &commands, byte verb, byte noun);
+ void doActions(const Command &command, byte noun, byte offset);
+ void clearScreen();
+ void takeItem(byte noun);
+ void dropItem(byte noun);
+ void drawItems();
+ void drawPic(byte pic, byte xOffset, byte yOffset);
+
+ Common::Array<Common::String> _exeStrings;
+ Common::Array<Common::String> _msgStrings;
+ Common::Array<Room> _rooms;
+ Common::Array<Picture> _pictures;
+ Common::Array<Item> _inventory;
+ Common::Array<Common::Point> _itemOffsets;
+ Common::Array<Common::Array<byte> > _drawings;
+ Commands _roomCommands;
+ Commands _globalCommands;
+ byte _room;
+ uint16 _steps;
+ Common::Array<byte> _variables;
+ bool _isDark;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/configure.engine b/engines/adl/configure.engine
new file mode 100644
index 0000000000..844e2b8e6a
--- /dev/null
+++ b/engines/adl/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine adl "ADL" no
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
new file mode 100644
index 0000000000..ba4e0104b9
--- /dev/null
+++ b/engines/adl/detection.cpp
@@ -0,0 +1,108 @@
+/* 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 "adl/adl.h"
+
+#include "common/config-manager.h"
+#include "common/error.h"
+#include "common/fs.h"
+
+#include "engines/advancedDetector.h"
+#include "engines/metaengine.h"
+
+namespace Adl {
+
+struct AdlGameDescription {
+ ADGameDescription desc;
+ GameType gameType;
+};
+
+uint32 AdlEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+const char *AdlEngine::getGameId() const {
+ return _gameDescription->desc.gameid;
+}
+
+const char *const directoryGlobs[] = {
+ "game",
+ "datafiles",
+ 0
+};
+
+static const PlainGameDescriptor adlGames[] = {
+ // Games
+ {"hires1", "Hi-Res Adventure #1: Mystery House"},
+ {0, 0}
+};
+
+static const AdlGameDescription gameDescriptions[] = {
+
+ { // MD5 by waltervn
+ {
+ "hires1", 0,
+ {
+ {"ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952},
+ {"AUTO LOAD OBJ", 0, "23bfccfe9fcff9b22cf6c41bde9078ac", 12291},
+ {"MYSTERY.HELLO", 0, "2289b7fea300b506e902a4c597968369", 836},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformApple2GS, // FIXME
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+ kGameTypeAdl1
+ },
+ {AD_TABLE_END_MARKER, kGameTypeNone}
+};
+
+class AdlMetaEngine : public AdvancedMetaEngine {
+public:
+ AdlMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(AdlGameDescription), adlGames) { }
+
+ const char *getName() const {
+ return "Hi-Res Adventure";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "Copyright (C) Sierra On-Line";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (gd) {
+ *engine = AdlEngine::create(((const AdlGameDescription *)gd)->gameType, syst, (const AdlGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+} // End of namespace Adl
+
+#if PLUGIN_ENABLED_DYNAMIC(ADL)
+ REGISTER_PLUGIN_DYNAMIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#endif
diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp
new file mode 100644
index 0000000000..02b8d51b7f
--- /dev/null
+++ b/engines/adl/display.cpp
@@ -0,0 +1,611 @@
+/* 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 "adl/display.h"
+#include "common/stream.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "common/system.h"
+#include "common/str.h"
+#include "common/events.h"
+#include "common/rect.h"
+#include "common/array.h"
+#include "engines/engine.h"
+
+namespace Adl {
+
+static byte font[64][5] = {
+ { 0x7c, 0x82, 0xba, 0xb2, 0x9c }, { 0xf8, 0x24, 0x22, 0x24, 0xf8 }, // @A
+ { 0xfe, 0x92, 0x92, 0x92, 0x6c }, { 0x7c, 0x82, 0x82, 0x82, 0x44 }, // BC
+ { 0xfe, 0x82, 0x82, 0x82, 0x7c }, { 0xfe, 0x92, 0x92, 0x92, 0x82 }, // DE
+ { 0xfe, 0x12, 0x12, 0x12, 0x02 }, { 0x7c, 0x82, 0x82, 0xa2, 0xe2 }, // FG
+ { 0xfe, 0x10, 0x10, 0x10, 0xfe }, { 0x00, 0x82, 0xfe, 0x82, 0x00 }, // HI
+ { 0x40, 0x80, 0x80, 0x80, 0x7e }, { 0xfe, 0x10, 0x28, 0x44, 0x82 }, // JK
+ { 0xfe, 0x80, 0x80, 0x80, 0x80 }, { 0xfe, 0x04, 0x18, 0x04, 0xfe }, // LM
+ { 0xfe, 0x08, 0x10, 0x20, 0xfe }, { 0x7c, 0x82, 0x82, 0x82, 0x7c }, // NO
+ { 0xfe, 0x12, 0x12, 0x12, 0x0c }, { 0x7c, 0x82, 0xa2, 0x42, 0xbc }, // PQ
+ { 0xfe, 0x12, 0x32, 0x52, 0x8c }, { 0x4c, 0x92, 0x92, 0x92, 0x64 }, // RS
+ { 0x02, 0x02, 0xfe, 0x02, 0x02 }, { 0x7e, 0x80, 0x80, 0x80, 0x7e }, // TU
+ { 0x3e, 0x40, 0x80, 0x40, 0x3e }, { 0xfe, 0x40, 0x30, 0x40, 0xfe }, // VW
+ { 0xc6, 0x28, 0x10, 0x28, 0xc6 }, { 0x06, 0x08, 0xf0, 0x08, 0x06 }, // XY
+ { 0xc2, 0xa2, 0x92, 0x8a, 0x86 }, { 0xfe, 0xfe, 0x82, 0x82, 0x82 }, // Z[
+ { 0x04, 0x08, 0x10, 0x20, 0x40 }, { 0x82, 0x82, 0x82, 0xfe, 0xfe }, // \]
+ { 0x20, 0x10, 0x08, 0x10, 0x20 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, // ^_
+ { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xbe, 0x00, 0x00 }, // !
+ { 0x00, 0x0e, 0x00, 0x0e, 0x00 }, { 0x28, 0xfe, 0x28, 0xfe, 0x28 }, // "#
+ { 0x48, 0x54, 0xfe, 0x54, 0x24 }, { 0x46, 0x26, 0x10, 0xc8, 0xc4 }, // $%
+ { 0x6c, 0x92, 0xac, 0x40, 0xa0 }, { 0x00, 0x00, 0x0e, 0x00, 0x00 }, // &'
+ { 0x38, 0x44, 0x82, 0x00, 0x00 }, { 0x00, 0x00, 0x82, 0x44, 0x38 }, // ()
+ { 0x44, 0x28, 0xfe, 0x28, 0x44 }, { 0x10, 0x10, 0x7c, 0x10, 0x10 }, // *+
+ { 0x00, 0x80, 0x60, 0x00, 0x00 }, { 0x10, 0x10, 0x10, 0x10, 0x10 }, // ,-
+ { 0x00, 0x00, 0x80, 0x00, 0x00 }, { 0x40, 0x20, 0x10, 0x08, 0x04 }, // ./
+ { 0x7c, 0xa2, 0x92, 0x8a, 0x7c }, { 0x00, 0x84, 0xfe, 0x80, 0x00 }, // 01
+ { 0xc4, 0xa2, 0x92, 0x92, 0x8c }, { 0x42, 0x82, 0x92, 0x9a, 0x66 }, // 23
+ { 0x30, 0x28, 0x24, 0xfe, 0x20 }, { 0x4e, 0x8a, 0x8a, 0x8a, 0x72 }, // 45
+ { 0x78, 0x94, 0x92, 0x92, 0x62 }, { 0x02, 0xe2, 0x12, 0x0a, 0x06 }, // 67
+ { 0x6c, 0x92, 0x92, 0x92, 0x6c }, { 0x8c, 0x92, 0x92, 0x52, 0x3c }, // 89
+ { 0x00, 0x00, 0x28, 0x00, 0x00 }, { 0x00, 0x80, 0x68, 0x00, 0x00 }, // :;
+ { 0x10, 0x28, 0x44, 0x82, 0x00 }, { 0x28, 0x28, 0x28, 0x28, 0x28 }, // <=
+ { 0x00, 0x82, 0x44, 0x28, 0x10 }, { 0x04, 0x02, 0xb2, 0x0a, 0x04 } // >?
+};
+
+Display::Display() :
+ _scanlines(false),
+ _cursorPos(0),
+ _mode(kModeText) {
+ _frameBuf = new byte[kFrameBufSize];
+ _frameBufSurface = new Graphics::Surface;
+ _frameBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ _textBuf = new byte[kTextBufSize];
+ memset(_textBuf, ' ' | 0x80, kTextBufSize);
+ _textBufSurface = new Graphics::Surface;
+ _textBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ createFont();
+
+ struct PixelPos rel = getPixelPos(0, 191);
+ struct PixelPos absy;
+ for (int i = 191; i >= 0; --i) {
+ absy = getPixelPos(0, i);
+ if (absy.rowAddr != rel.rowAddr)
+ debug("%i: %04x %04x", i, absy.rowAddr, rel.rowAddr);
+ moveY(rel, false);
+ }
+ absy = getPixelPos(0, 191);
+ if (absy.rowAddr != rel.rowAddr)
+ debug("%i: %04x %04x", 191, absy.rowAddr, rel.rowAddr);
+
+ rel = getPixelPos(0, 0);
+ for (int i = 0; i < 192; ++i) {
+ absy = getPixelPos(0, i);
+ if (absy.rowAddr != rel.rowAddr)
+ debug("%i: %04x %04x", i, absy.rowAddr, rel.rowAddr);
+ moveY(rel, true);
+ }
+ absy = getPixelPos(0, 0);
+ if (absy.rowAddr != rel.rowAddr)
+ debug("%i: %04x %04x", 191, absy.rowAddr, rel.rowAddr);
+}
+
+Display::~Display() {
+ delete[] _frameBuf;
+ _frameBufSurface->free();
+ delete _frameBufSurface;
+
+ delete[] _textBuf;
+ _textBufSurface->free();
+ delete _textBufSurface;
+
+ _font->free();
+ delete _font;
+}
+
+void Display::loadFrameBuffer(Common::ReadStream &stream) {
+ stream.read(_frameBuf, kFrameBufSize);
+}
+
+void Display::decodeScanline(byte *dst, int pitch, byte *src) {
+ // TODO: shift secondPal by half a pixel
+
+ bool prevOn = false;
+
+ for (uint j = 0; j < 39; ++j) {
+ bool secondPal = src[j] & 0x80;
+ byte cur = src[j];
+ byte next = 0;
+ if (j != 39)
+ next = src[j + 1];
+
+ for (uint k = 0; k < 7; ++k) {
+ bool curOn = cur & (1 << k);
+ bool nextOn;
+
+ if (k != 6)
+ nextOn = cur & (1 << (k + 1));
+ else
+ nextOn = next & 1;
+
+ byte color;
+ if (curOn == prevOn || curOn == nextOn)
+ color = curOn ? 1 : 0;
+ else {
+ if (secondPal)
+ color = (curOn == ((j + k) % 2) ? 5 : 4);
+ else
+ color = (curOn == ((j + k) % 2) ? 3 : 2);
+ }
+
+ dst[0] = color;
+ dst[1] = color;
+
+ if (!_scanlines) {
+ dst[pitch] = color;
+ dst[pitch + 1] = color;
+ }
+
+ dst += 2;
+ prevOn = curOn;
+ }
+ }
+}
+
+Display::PixelPos Display::getPixelPos(byte x, byte y) {
+ PixelPos pixelPos;
+
+ // FIXME: check X, Y range
+
+ byte offsetL = y & 0xc0;
+ offsetL |= offsetL >> 2;
+ byte offsetH = y;
+ y <<= 2;
+ offsetH <<= 1;
+ offsetH |= y >> 7;
+ y <<= 1;
+ offsetH <<= 1;
+ offsetH |= y >> 7;
+ y <<= 1;
+ offsetL >>= 1;
+ offsetL |= y & 0x80;
+ y <<= 1;
+ offsetH = offsetH & 0x1f;
+ pixelPos.rowAddr = (offsetH << 8) | offsetL;
+ pixelPos.byteOffset = x / 7;
+ pixelPos.bitMask = 0x80 | (1 << x % 7);
+
+ return pixelPos;
+}
+
+byte Display::getPixelColor(byte offset, byte color) {
+ if (offset & 1) {
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ return color ^ 0x7f;
+ }
+
+ return color;
+}
+
+void Display::decodeFrameBuffer() {
+ byte *src = _frameBuf;
+ int pitch = _frameBufSurface->pitch;
+ for (int j = 0; j < 8; ++j) {
+ for (int i = 0; i < 8; ++i) {
+ byte *dst = (byte *)_frameBufSurface->getPixels() + pitch * 2 * (i * 8 + j);
+ decodeScanline(dst, pitch, src);
+ src += 40;
+ dst += pitch * 2 * 64;
+ decodeScanline(dst, pitch, src);
+ src += 40;
+ dst += pitch * 2 * 64;
+ decodeScanline(dst, pitch, src);
+ src += 48;
+ dst += pitch * 2 * 64;
+ }
+ }
+}
+
+void Display::drawPixel(byte x, byte y, byte color) {
+ PixelPos p = getPixelPos(x, y);
+ byte c = getPixelColor(p.byteOffset, color);
+ byte *b = _frameBuf + p.rowAddr + p.byteOffset;
+ c ^= *b;
+ c &= p.bitMask;
+ c ^= *b;
+ *b = c;
+}
+
+void Display::moveX(PixelPos &p, byte &color, bool left) {
+ if (left) {
+ byte bit = p.bitMask;
+ bool b = bit & 1;
+ bit >>= 1;
+ if (!b) {
+ bit ^= 0xc0;
+ p.bitMask = bit;
+ return;
+ }
+ --p.byteOffset;
+ if (p.byteOffset & 0x80)
+ p.byteOffset = 39;
+ p.bitMask = 0xc0;
+ } else {
+ byte bit = p.bitMask;
+ bit <<= 1;
+ bit ^= 0x80;
+ if (bit & 0x80) {
+ p.bitMask = bit;
+ return;
+ }
+ p.bitMask = 0x81;
+ ++p.byteOffset;
+ if (p.byteOffset == 40)
+ p.byteOffset = 0;
+ }
+
+ color = getPixelColor(p.byteOffset, color);
+}
+
+void Display::moveY(PixelPos &p, bool down) {
+ if (!down) {
+ if (p.rowAddr & 0x1c00)
+ p.rowAddr -= 0x400;
+ else if (p.rowAddr & 0x380)
+ p.rowAddr += 0x1b80;
+ else {
+ p.rowAddr += 0x1f58;
+ if (!(p.rowAddr & 0x80))
+ p.rowAddr += 0x78; // Wrap around
+ }
+ } else {
+ p.rowAddr += 0x400;
+ if (p.rowAddr & 0x1c00)
+ return;
+ else if ((p.rowAddr & 0x380) != 0x380)
+ p.rowAddr -= 0x1f80;
+ else {
+ p.rowAddr -= 0x2358;
+ if ((p.rowAddr & 0x78) == 0x78)
+ p.rowAddr -= 0x78; // Wrap around
+ }
+ }
+}
+
+void Display::drawNextPixel(Display::PixelPos &p, byte &color, byte bits, byte quadrant) {
+ if (bits & 4) {
+ byte b = (_frameBuf[p.rowAddr + p.byteOffset] ^ color) & p.bitMask;
+ _frameBuf[p.rowAddr + p.byteOffset] ^= b;
+ }
+
+ bits += quadrant;
+
+ if (bits & 1)
+ moveX(p, color, bits & 2);
+ else
+ moveY(p, bits & 2);
+}
+
+void Display::drawRightAngles(Common::Array<byte> &rightAngles, Common::Point p, byte rotation, byte scaling, byte color) {
+ const byte stepping[] = {
+ 0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
+ 0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
+ 0xff
+ };
+
+ PixelPos pos = getPixelPos(p.x, p.y);
+ byte c = getPixelColor(pos.byteOffset, color);
+
+ byte quadrant = rotation >> 4;
+ rotation &= 0xf;
+ byte xStep = stepping[rotation];
+ byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
+
+ for (uint i = 0; i < rightAngles.size(); ++i) {
+ byte b = rightAngles[i];
+
+ do {
+ byte xFrac = 0x80;
+ byte yFrac = 0x80;
+ for (uint j = 0; j < scaling; ++j) {
+ if (xFrac + xStep + 1 > 255)
+ drawNextPixel(pos, c, b, quadrant);
+ xFrac += xStep + 1;
+ if (yFrac + yStep > 255)
+ drawNextPixel(pos, c, b, quadrant + 1);
+ yFrac += yStep;
+ }
+ b >>= 3;
+ } while (b != 0);
+ }
+}
+
+void Display::drawLine(Common::Point p1, Common::Point p2, byte color) {
+ PixelPos p = getPixelPos(p1.x, p1.y);
+ byte c = getPixelColor(p.byteOffset, color);
+
+ int16 deltaX = p2.x - p1.x;
+ byte dir = deltaX >> 8;
+
+ if (deltaX < 0)
+ deltaX = -deltaX;
+
+ int16 err = deltaX;
+
+ int16 deltaY = p2.y - p1.y - 1;
+ dir >>= 1;
+ if (deltaY >= 0) {
+ deltaY = -deltaY - 2;
+ dir |= 0x80;
+ }
+
+ int16 steps = deltaY - deltaX;
+
+ err += deltaY + 1;
+
+ while (1) {
+ byte *b = _frameBuf + p.rowAddr + p.byteOffset;
+ byte d = *b;
+ d ^= c;
+ d &= p.bitMask;
+ d ^= *b;
+ *b = d;
+
+ if (++steps == 0)
+ return;
+
+ if (err < 0) {
+ moveY(p, dir & 0x80);
+ err += deltaX;
+ } else {
+ moveX(p, c, dir & 0x40);
+ err += deltaY + 1;
+ }
+ }
+}
+
+void Display::clear(byte color) {
+ for (uint i = 0; i < kFrameBufSize; ++i)
+ _frameBuf[i] = getPixelColor(i & 1, color);
+}
+
+void Display::updateTextSurface() {
+ for (uint row = 0; row < 24; ++row)
+ for (uint col = 0; col < 40; ++col) {
+ char c = _textBuf[row * 40 + col];
+
+ Common::Rect r(7 * 2, 8 * 2);
+ r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2);
+
+ if (!(c & 0x80)) {
+ if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1))
+ r.translate(0, 4 * 8 * 2);
+ }
+
+ _textBufSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r);
+ }
+}
+
+void Display::printString(const Common::String &str) {
+ Common::String::const_iterator it;
+ for (it = str.begin(); it != str.end(); ++it) {
+ byte b = *it;
+
+ if (b == ('\r' | 0x80))
+ _cursorPos = (_cursorPos / 40 + 1) * 40;
+ else if (b < 0x80 || b >= 0xa0)
+ _textBuf[_cursorPos++] = b;
+
+ if (_cursorPos == kTextBufSize) {
+ memmove(_textBuf, _textBuf + 40, kTextBufSize - 40);
+ memset(_textBuf + kTextBufSize - 40, ' ' | 0x80, 40);
+ _cursorPos -= 40;
+ }
+ }
+
+ updateTextSurface();
+}
+
+void Display::printASCIIString(const Common::String &str) {
+ Common::String aStr;
+
+ Common::String::const_iterator it;
+ for (it = str.begin(); it != str.end(); ++it)
+ aStr += *it | 0x80;
+
+ printString(aStr);
+}
+
+void Display::drawChar(byte c, int x, int y) {
+ byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x;
+
+ for (uint row = 0; row < 8; ++row) {
+ for (uint col = 1; col < 6; ++col)
+ if (font[c][col - 1] & (1 << row)) {
+ buf[col * 2] = 1;
+ buf[col * 2 + 1] = 1;
+
+ if (!_scanlines) {
+ buf[_font->pitch + col * 2] = 1;
+ buf[_font->pitch + col * 2 + 1] = 1;
+ }
+ }
+
+ buf += 2 * _font->pitch;
+ }
+}
+
+void Display::createFont() {
+ _font = new Graphics::Surface;
+ _font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (uint i = 0; i < 4; ++i)
+ for (uint j = 0; j < 16; ++j)
+ drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2);
+
+ // Create inverted font
+ byte *buf = (byte *)_font->getPixels();
+ byte *bufInv = buf + (_font->h / 2) * _font->pitch;
+
+ for (uint row = 0; row < _font->h / 2; ++row) {
+ if (!_scanlines || !(row & 1))
+ for (uint col = 0; col < _font->w; ++col)
+ bufInv[col] = buf[col] ? 0 : 1;
+
+ buf += _font->pitch;
+ bufInv += _font->pitch;
+ }
+}
+
+void Display::updateScreen() {
+ if (_mode == kModeText) {
+ g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h);
+ } else if (_mode == kModeHires) {
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h);
+ } else {
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2);
+ g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2);
+ }
+}
+
+Common::String Display::inputString(byte prompt) {
+ Common::String s;
+
+ if (prompt > 0)
+ printString(Common::String(prompt));
+
+ while (1) {
+ byte b = inputKey();
+
+ if (g_engine->shouldQuit())
+ return 0;
+
+ if (b == 0)
+ continue;
+
+ if (b == ('\r' | 0x80)) {
+ s += b;
+ printString(Common::String(b));
+ return s;
+ }
+
+ if (b < 0xa0) {
+ switch (b) {
+ case Common::KEYCODE_BACKSPACE | 0x80:
+ if (!s.empty()) {
+ --_cursorPos;
+ _textBuf[_cursorPos] = ' ' | 0x80;
+ s.deleteLastChar();
+ }
+ break;
+ };
+ } else {
+ s += b;
+ printString(Common::String(b));
+ }
+ }
+}
+
+byte Display::convertKey(uint16 ascii) {
+ ascii = toupper(ascii);
+
+ if (ascii >= 0x80)
+ return 0;
+
+ ascii |= 0x80;
+
+ if (ascii >= 0x80 && ascii <= 0xe0)
+ return ascii;
+
+ return 0;
+}
+
+byte Display::inputKey() {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ byte orgChar = _textBuf[_cursorPos];
+ _textBuf[_cursorPos] = (orgChar & 0x3f) | 0x40;
+
+ byte key = 0;
+
+ while (!g_engine->shouldQuit() && key == 0) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type != Common::EVENT_KEYDOWN)
+ continue;
+
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ if (event.kbd.keycode == Common::KEYCODE_q)
+ g_engine->quitGame();
+ continue;
+ }
+
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ case Common::KEYCODE_RETURN:
+ key = convertKey(event.kbd.keycode);
+ break;
+ default:
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80)
+ key = convertKey(event.kbd.ascii);
+ };
+ }
+
+ updateTextSurface();
+ updateScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(16);
+ }
+
+ _textBuf[_cursorPos] = orgChar;
+ return key;
+}
+
+void Display::delay(uint32 ms) {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ uint32 start = g_system->getMillis();
+
+ while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
+ switch(event.kbd.keycode) {
+ case Common::KEYCODE_q:
+ g_engine->quitGame();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ updateScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(16);
+ }
+}
+
+void Display::home() {
+ memset(_textBuf, ' ' | 0x80, kTextBufSize);
+ _cursorPos = 0;
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/display.h b/engines/adl/display.h
new file mode 100644
index 0000000000..eabf340573
--- /dev/null
+++ b/engines/adl/display.h
@@ -0,0 +1,103 @@
+/* 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_DISPLAY_H
+#define ADL_DISPLAY_H
+
+#include <common/types.h>
+#include <common/array.h>
+
+namespace Common {
+class ReadStream;
+class String;
+class Point;
+}
+
+namespace Graphics {
+class Surface;
+}
+
+namespace Adl {
+
+class Display {
+public:
+ enum Mode {
+ kModeHires,
+ kModeText,
+ kModeMixed
+ };
+
+ Display();
+ ~Display();
+ void loadFrameBuffer(Common::ReadStream &stream);
+ void decodeFrameBuffer();
+ void printString(const Common::String &str);
+ void printASCIIString(const Common::String &str);
+ void updateScreen();
+ Common::String inputString(byte prompt = 0);
+ void delay(uint32 ms);
+ void setMode(Mode mode) { _mode = mode; }
+ byte inputKey();
+ void home();
+ void drawPixel(byte x, byte y, byte color);
+ void drawLine(Common::Point p1, Common::Point p2, byte color);
+ void clear(byte color);
+ void drawRightAngles(Common::Array<byte> &rightAngles, Common::Point p, byte rotation, byte scaling, byte color);
+
+private:
+ enum {
+ kWidth = 280,
+ kHeight = 192,
+ kFrameBufSize = 0x2000,
+ kTextBufSize = 40 * 24
+ };
+
+ struct PixelPos {
+ uint16 rowAddr;
+ byte byteOffset;
+ byte bitMask;
+ };
+
+ void decodeScanline(byte *dst, int pitch, byte *src);
+ PixelPos getPixelPos(byte x, byte y);
+ byte getPixelColor(byte x, byte color);
+ void drawChar(byte c, int x, int y);
+ void createFont();
+ void updateTextSurface();
+ byte convertKey(uint16 ascii);
+ void moveX(PixelPos &p, byte &color, bool left);
+ void moveY(PixelPos &p, bool down);
+ void drawNextPixel(Display::PixelPos &p, byte &color, byte bits, byte quadrant);
+
+ bool _scanlines;
+ byte *_frameBuf;
+ byte *_textBuf;
+ Graphics::Surface *_frameBufSurface;
+ Graphics::Surface *_textBufSurface;
+ Graphics::Surface *_font;
+ int _cursorPos;
+ Mode _mode;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/module.mk b/engines/adl/module.mk
new file mode 100644
index 0000000000..8f86eeec68
--- /dev/null
+++ b/engines/adl/module.mk
@@ -0,0 +1,19 @@
+MODULE := engines/adl
+
+MODULE_OBJS := \
+ detection.o \
+ display.o \
+ adl.o \
+ adl_v1.o \
+ parser.o
+
+MODULE_DIRS += \
+ engines/adl
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_ADL), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/adl/parser.cpp b/engines/adl/parser.cpp
new file mode 100644
index 0000000000..cdbaf3ba6c
--- /dev/null
+++ b/engines/adl/parser.cpp
@@ -0,0 +1,172 @@
+/* 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 "adl/adl.h"
+#include "adl/parser.h"
+#include "adl/display.h"
+
+#include "engines/engine.h"
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/debug.h"
+#include "common/textconsole.h"
+
+namespace Adl {
+
+Parser::Parser(AdlEngine &engine, Display &display) :
+ _engine(engine),
+ _display(display) {
+
+}
+
+void Parser::loadWords(Common::ReadStream &stream, WordMap &map) {
+ uint index = 0;
+
+ while (1) {
+ ++index;
+
+ byte buf[kWordSize];
+
+ if (stream.read(buf, kWordSize) < kWordSize)
+ error("Error reading word list");
+
+ Common::String word((char *)buf, kWordSize);
+
+ if (!map.contains(word))
+ map[word] = index;
+
+ byte synonyms = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Error reading word list");
+
+ if (synonyms == 0xff)
+ break;
+
+ for (uint i = 0; i < synonyms; ++i) {
+ if (stream.read((char *)buf, kWordSize) < kWordSize)
+ error("Error reading word list");
+
+ word = Common::String((char *)buf, kWordSize);
+
+ if (!map.contains(word))
+ map[word] = index;
+ }
+ }
+}
+
+Common::String Parser::getLine() {
+ // Original engine uses a global here, which isn't reset between
+ // calls and may not match actual mode
+ bool textMode = false;
+
+ while (1) {
+ Common::String line = _display.inputString(A2CHAR('?'));
+
+ if (g_engine->shouldQuit())
+ return "";
+
+ if ((byte)line[0] == ('\r' | 0x80)) {
+ textMode = !textMode;
+ _display.setMode(textMode ? Display::kModeText : Display::kModeMixed);
+ continue;
+ }
+
+ // Remove the return
+ line.deleteLastChar();
+ return line;
+ }
+}
+
+Common::String Parser::getWord(const Common::String &line, uint &index) {
+ Common::String str;
+
+ for (uint i = 0; i < 8; ++i)
+ str += (char)(A2CHAR(' '));
+
+ int copied = 0;
+
+ // Skip initial whitespace
+ while (1) {
+ if (index == line.size())
+ return str;
+ if (line[index] != (char)(A2CHAR(' ')))
+ break;
+ ++index;
+ }
+
+ // Copy up to 8 characters
+ while (1) {
+ if (copied < 8)
+ str.setChar(line[index], copied++);
+
+ index++;
+
+ if (index == line.size() || line[index] == (char)(A2CHAR(' ')))
+ return str;
+ }
+}
+
+void Parser::getInput(uint &verb, uint &noun) {
+ while (1) {
+ _display.printString(_engine.getExeString(STR_COMMON_ENTERCMD));
+ Common::String line = getLine();
+
+ if (g_engine->shouldQuit())
+ return;
+
+ uint index = 0;
+ Common::String verbStr = getWord(line, index);
+ debug("Verb: \"%s\"", appleToAscii(verbStr).c_str());
+
+ if (!_verbs.contains(verbStr)) {
+ Common::String err = _engine.getExeString(STR_COMMON_VERBERR);
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ _display.printString(err);
+ continue;
+ }
+
+ verb = _verbs[verbStr];
+ debug("Verb ID: %i", verb);
+
+ Common::String nounStr = getWord(line, index);
+ debug("Noun: \"%s\"", appleToAscii(nounStr).c_str());
+
+ if (!_nouns.contains(nounStr)) {
+ Common::String err = _engine.getExeString(STR_COMMON_NOUNERR);
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ for (uint i = 0; i < nounStr.size(); ++i)
+ err.setChar(nounStr[i], i + 30);
+ _display.printString(err);
+ continue;
+ }
+
+ noun = _nouns[nounStr];
+ debug("Noun ID: %i", noun);
+ return;
+ }
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/parser.h b/engines/adl/parser.h
new file mode 100644
index 0000000000..3c191d90f6
--- /dev/null
+++ b/engines/adl/parser.h
@@ -0,0 +1,66 @@
+/* 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_PARSER_H
+#define ADL_PARSER_H
+
+#include "common/types.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Common {
+class ReadStream;
+class String;
+}
+
+namespace Adl {
+
+class Display;
+
+class Parser {
+public:
+ Parser(AdlEngine &engine, Display &display);
+
+ void loadVerbs(Common::ReadStream &stream) { loadWords(stream, _verbs); }
+ void loadNouns(Common::ReadStream &stream) { loadWords(stream, _nouns); }
+ void getInput(uint &verb, uint &noun);
+
+private:
+ enum {
+ kWordSize = 8
+ };
+
+ typedef Common::HashMap<Common::String, uint> WordMap;
+
+ void loadWords(Common::ReadStream &stream, WordMap &map);
+ Common::String getLine();
+ Common::String getWord(const Common::String &line, uint &index);
+
+ AdlEngine &_engine;
+ Display &_display;
+ WordMap _verbs;
+ WordMap _nouns;
+};
+
+} // End of namespace Adl
+
+#endif