aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/mohawk/livingbooks.cpp64
-rw-r--r--engines/mohawk/livingbooks.h31
-rw-r--r--engines/mohawk/livingbooks_code.cpp386
-rw-r--r--engines/mohawk/livingbooks_code.h94
-rw-r--r--engines/mohawk/module.mk1
5 files changed, 540 insertions, 36 deletions
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 880e551199..b269788755 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -80,6 +80,8 @@ MohawkEngine_LivingBooks::MohawkEngine_LivingBooks(OSystem *syst, const MohawkGa
_alreadyShowedIntro = false;
+ _code = NULL;
+
_rnd = new Common::RandomSource();
g_eventRec.registerRandomSource(*_rnd, "livingbooks");
@@ -260,6 +262,9 @@ void MohawkEngine_LivingBooks::destroyPage() {
_eventQueue.clear();
+ delete _code;
+ _code = NULL;
+
for (uint32 i = 0; i < _items.size(); i++)
delete _items[i];
_items.clear();
@@ -330,8 +335,11 @@ bool MohawkEngine_LivingBooks::loadPage(LBMode mode, uint page, uint subpage) {
_cursor->showCursor();
_gfx->setPalette(1000);
- loadBITL(1000);
+ if (hasResource(ID_BCOD, 1000))
+ _code = new LBCode(this);
+
+ loadBITL(1000);
for (uint32 i = 0; i < _items.size(); i++)
_items[i]->init();
@@ -1875,6 +1883,8 @@ void LBItem::readData(uint16 type, uint16 size, Common::SeekableSubReadStreamEnd
_delayMin = stream->readUint16();
_delayMax = stream->readUint16();
_timingMode = stream->readUint16();
+ if (_timingMode > 7)
+ error("encountered timing mode %04x", _timingMode);
_periodMin = stream->readUint16();
_periodMax = stream->readUint16();
_relocPoint.x = stream->readSint16();
@@ -1903,16 +1913,16 @@ void LBItem::readData(uint16 type, uint16 size, Common::SeekableSubReadStreamEnd
error("0x6f had wrong size (%d)", size);
uint u1 = stream->readUint16();
uint u2 = stream->readUint16();
- uint u3 = stream->readUint16();
- uint u4 = stream->readUint16();
- uint u5 = stream->readUint16();
+ uint event = stream->readUint16();
+ uint opcode = stream->readUint16();
+ uint param = stream->readUint16();
uint u6 = stream->readUint16();
uint u7 = stream->readUint16();
uint u8 = stream->readUint16();
uint u9 = stream->readUint16();
// FIXME: this is scripting stuff
- debug(2, "0x6f: item %s, unknowns: %04x, %04x, %04x, %04x, %04x, %04x, %04x, %04x, %04x\n",
- _desc.c_str(), u1, u2, u3, u4, u5, u6, u7, u8, u9);
+ warning("0x6f: unknown: item %s, unknowns: %04x, %04x, event %04x, opcode %04x, param %04x, unknowns %04x, %04x, %04x, %04x",
+ _desc.c_str(), u1, u2, event, opcode, param, u6, u7, u8, u9);
}
break;
@@ -1951,13 +1961,33 @@ void LBItem::readData(uint16 type, uint16 size, Common::SeekableSubReadStreamEnd
if (size != 10)
error("0x7d had wrong size (%d)", size);
uint u1 = stream->readUint16();
- uint u2 = stream->readUint16();
+ uint key = stream->readUint16();
uint u3 = stream->readUint16();
- uint u4 = stream->readUint16();
- uint u5 = stream->readUint16();
- // FIXME: this is scripting stuff
- debug(2, "0x7d: item %s, unknowns: %04x, %04x, %04x, %04x, %04x\n",
- _desc.c_str(), u1, u2, u3, u4, u5);
+ uint target = stream->readUint16();
+ byte event = stream->readByte();
+ byte u4 = stream->readByte();
+ // FIXME: this is scripting stuff: what to run when key is pressed
+ warning("0x7d: unknown: item %s, unknown %04x, key %04x, unknown %04x, target %d, event %02x, unknown %02x",
+ _desc.c_str(), u1, key, u3, target, event, u4);
+ }
+ break;
+
+ case kLBUnknown80:
+ {
+ assert(size == 2);
+ uint id = stream->readUint16();
+ warning("0x80: unknown: item %s, id %04x", _desc.c_str(), id);
+ // FIXME
+ }
+ break;
+
+ case kLBUnknown194:
+ {
+ assert(size == 4);
+ uint offset = stream->readUint32();
+ if (!_vm->_code)
+ error("no BCOD?");
+ _vm->_code->runCode(this, offset);
}
break;
@@ -2009,7 +2039,7 @@ bool LBItem::contains(Common::Point point) {
}
void LBItem::update() {
- if (_neverEnabled || !_enabled || !_globalEnabled)
+ if (_phase != 0x7FFF && (_neverEnabled || !_enabled || !_globalEnabled))
return;
if (_nextTime == 0 || _nextTime > (uint32)(_vm->_system->getMillis() / 16))
@@ -2047,7 +2077,7 @@ bool LBItem::togglePlaying(bool playing, bool restart) {
_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventDone));
return true;
}
- if (!_neverEnabled && _enabled && _globalEnabled && !_playing) {
+ if (((!_neverEnabled && _enabled && _globalEnabled) || _phase == 0x7FFF) && !_playing) {
_playing = togglePlaying(true, restart);
if (_playing) {
_nextTime = 0;
@@ -2387,9 +2417,9 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) {
break;
case kLBOpSendExpression:
- // FIXME
- warning("ignoring kLBOpSendExpression (event 0x%04x, param 0x%04x, target '%s')",
- entry->event, entry->param, target->_desc.c_str());
+ if (!_vm->_code)
+ error("no BCOD?");
+ _vm->_code->runCode(this, entry->offset);
break;
case kLBOpRunSubentries:
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index ffc97a25f3..8ce1b30f4a 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -38,6 +38,8 @@
#include "sound/mixer.h"
+#include "livingbooks_code.h"
+
namespace Mohawk {
enum NodeState {
@@ -86,7 +88,8 @@ enum {
kLBPaletteAItem = 0x44, // unused?
kLBPaletteItem = 0x45,
kLBProxyItem = 0x46,
- kLBXDataFileItem = 0x3e9
+ kLBXDataFileItem = 0x3e9,
+ kLBDiscDectectorItem = 0xfa1
};
enum {
@@ -163,9 +166,11 @@ enum {
kLBSetAmbient = 0x7b,
kLBUnknown7C = 0x7c, // unused?
kLBUnknown7D = 0x7d,
- kLBUnknown7E = 0x7e, // unused?
+ kLBUnknown7E = 0x7e, // unused? (rect flag)
kLBSetParent = 0x7f, // unused?
- kLBUnknown80 = 0x80 // unused?
+ kLBUnknown80 = 0x80, // unused? TODO: sets +36
+ // from here, rugrats
+ kLBUnknown194 = 0x194
};
enum {
@@ -321,23 +326,9 @@ protected:
Common::Array<Common::Point> _shapeOffsets;
};
-enum LBValueType {
- kLBValueString,
- kLBValueInteger
-};
-
-struct LBValue {
- LBValue() { type = kLBValueInteger; integer = 0; }
-
- LBValueType type;
- Common::String string;
- int integer;
-
- bool operator==(const LBValue &x) const;
- bool operator!=(const LBValue &x) const;
-};
-
class LBItem {
+ friend class LBCode;
+
public:
LBItem(MohawkEngine_LivingBooks *vm, Common::Rect rect);
virtual ~LBItem();
@@ -599,6 +590,8 @@ public:
void prevPage();
void nextPage();
+ LBCode *_code;
+
// TODO: make private
Common::HashMap<Common::String, LBValue> _variables;
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
new file mode 100644
index 0000000000..187c940cee
--- /dev/null
+++ b/engines/mohawk/livingbooks_code.cpp
@@ -0,0 +1,386 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "mohawk/livingbooks.h"
+#include "mohawk/resource.h"
+
+namespace Mohawk {
+
+LBCode::LBCode(MohawkEngine_LivingBooks *vm) : _vm(vm) {
+ Common::SeekableSubReadStreamEndian *bcodStream = _vm->wrapStreamEndian(ID_BCOD, 1000);
+ uint32 totalSize = bcodStream->readUint32();
+ if (totalSize != (uint32)bcodStream->size())
+ error("BCOD had size %d, but claimed to be of size %d", bcodStream->size(), totalSize);
+ size = bcodStream->readUint32();
+ if (size + 8 > totalSize)
+ error("BCOD code was of size %d, beyond size %d", size, totalSize);
+ data = new byte[size];
+ bcodStream->read(data, size);
+ uint16 pos = 0;
+ while (bcodStream->pos() < bcodStream->size()) {
+ uint16 unknown = bcodStream->readUint16();
+ if (unknown != 0) {
+ warning("unknown was %04x, not zero, while reading strings", unknown);
+ if (bcodStream->pos() != bcodStream->size())
+ error(".. and there was more data afterwards");
+ break;
+ }
+ Common::String string = _vm->readString(bcodStream);
+ strings[pos] = string;
+ debug(2, "read '%s' from BCOD at 0x%04x", string.c_str(), pos);
+ pos += 2 + string.size() + 1;
+ }
+}
+
+LBCode::~LBCode() {
+ delete[] data;
+}
+
+Common::Array<LBValue> LBCode::readParams(LBItem *src, uint32 &offset) {
+ Common::Array<LBValue> params;
+
+ if (offset + 1 >= size)
+ error("went off the end of code");
+
+ byte numParams = data[offset];
+ offset++;
+
+ if (!numParams) {
+ debugN("()\n");
+ return params;
+ }
+
+ byte nextToken = data[offset];
+ offset++;
+ if (nextToken != kLBCodeTokenOpenBracket)
+ error("missing ( before code parameter list (got %02x)", nextToken);
+ debugN("(");
+
+ for (uint i = 0; i < numParams; i++) {
+ if (i != 0) {
+ nextToken = data[offset];
+ offset++;
+ if (nextToken != ',')
+ error("missing , between code parameters (got %02x)", nextToken);
+ debugN(", ");
+ }
+
+ nextToken = data[offset];
+ offset++;
+
+ LBValue nextValue;
+
+ switch (nextToken) {
+ case kLBCodeTokenLiteral:
+ {
+ byte literalType = data[offset];
+ offset++;
+ if (literalType == kLBCodeLiteralInteger) {
+ uint16 intValue = READ_BE_UINT16(data + offset);
+ offset += 2;
+ nextValue.type = kLBValueInteger;
+ nextValue.integer = intValue;
+ debugN("%d", nextValue.integer);
+ } else
+ error("unknown literal type %02x in code", literalType);
+ }
+ break;
+
+ case kLBCodeTokenString:
+ {
+ uint16 stringOffset = READ_BE_UINT16(data + offset);
+ offset += 2;
+ // TODO: check string exists
+ nextValue.type = kLBValueString;
+ nextValue.string = strings[stringOffset];
+ debugN("\"%s\"", nextValue.string.c_str());
+ }
+ break;
+
+ case kLBCodeTokenChar:
+ {
+ uint16 stringOffset = READ_BE_UINT16(data + offset);
+ offset += 2;
+ // TODO: check string exists
+ nextValue.type = kLBValueString;
+ nextValue.string = strings[stringOffset];
+ debugN("'%s'", nextValue.string.c_str());
+ }
+ break;
+
+ case kLBCodeTokenLong: // FIXME: wrong?
+ {
+ uint32 intValue = READ_BE_UINT32(data + offset);
+ offset += 4;
+ nextValue.type = kLBValueInteger;
+ nextValue.integer = intValue;
+ debugN("%d", nextValue.integer);
+ }
+ break;
+
+ case 0x31:
+ {
+ // TODO
+ uint16 intValue = READ_BE_UINT16(data + offset);
+ offset += 2;
+ nextValue.type = kLBValueInteger;
+ nextValue.integer = intValue;
+ debugN("%d", nextValue.integer);
+ }
+ break;
+
+ case 0x4d:
+ // TODO
+ runCodeCommand(src, offset);
+ break;
+
+ case 0x5f:
+ // keycode
+ nextValue.type = kLBValueInteger;
+ nextValue.integer = data[offset];
+ debugN("%d", nextValue.integer);
+ offset++;
+ offset++; // TODO
+ break;
+
+ default:
+ error("unknown token %02x in code parameter", nextToken);
+ }
+
+ params.push_back(nextValue);
+ }
+
+ nextToken = data[offset];
+ offset++;
+ if (nextToken != kLBCodeTokenCloseBracket)
+ error("missing ) after code parameter list (got %02x)", nextToken);
+ debugN(")");
+
+ return params;
+}
+
+void LBCode::runCodeCommand(LBItem *src, uint32 &offset) {
+ if (offset + 1 >= size)
+ error("went off the end of code");
+
+ byte commandType = data[offset];
+ offset++;
+
+ switch (commandType) {
+ case 0x23:
+ {
+ debugN("setViewOrigin");
+ Common::Array<LBValue> params = readParams(src, offset);
+ // TODO
+ }
+ break;
+
+ case 0x36:
+ {
+ debugN("setWorld");
+ Common::Array<LBValue> params = readParams(src, offset);
+ // TODO
+ }
+ break;
+
+ case 0x42:
+ {
+ debugN("setPlayParams");
+ Common::Array<LBValue> params = readParams(src, offset);
+ if (params.size() > 8)
+ error("too many parameters (%d) to setPlayParams", params.size());
+ if (!params.size())
+ error("no target for setPlayParams");
+ LBItem *target;
+ if (params[0].string.equalsIgnoreCase("self")) {
+ target = src;
+ } else {
+ error("didn't understand target '%s'", params[0].string.c_str());
+ }
+ // TODO: type-checking
+ switch (params.size()) {
+ case 8:
+ target->_soundMode = params[7].integer;
+ case 7:
+ target->_controlMode = params[6].integer;
+ case 6:
+ // TODO: _relocPoint?
+ case 5:
+ // TODO: _periodMin/Max
+ case 4:
+ target->_timingMode = params[3].integer;
+ case 3:
+ // TODO: _delayMin/Max
+ case 2:
+ target->_loopMode = params[1].integer;
+ }
+ }
+ break;
+
+ case 0x50:
+ {
+ debugN("setKeyEvent");
+ Common::Array<LBValue> params = readParams(src, offset);
+ if (params.size() != 2)
+ error("incorrect number of parameters (%d) to setKeyEvent", params.size());
+ // FIXME: params[0] is key, params[1] is opcode id
+ }
+ break;
+
+ case 0x51:
+ {
+ debugN("setHitTest");
+ Common::Array<LBValue> params = readParams(src, offset);
+ if (params.size() > 2)
+ error("incorrect number of parameters (%d) to setHitTest", params.size());
+ // TODO
+ }
+ break;
+
+ case 0x52:
+ {
+ debugN("key");
+ Common::Array<LBValue> params = readParams(src, offset);
+ // TODO
+ }
+ break;
+
+ case 0x5E:
+ {
+ debugN("setPageFade");
+ Common::Array<LBValue> params = readParams(src, offset);
+ // TODO
+ }
+ break;
+
+ default:
+ error("unknown command %02x in code", commandType);
+ }
+}
+
+void LBCode::runCodeItemCommand(LBItem *src, uint32 &offset) {
+ if (offset + 1 >= size)
+ error("went off the end of code");
+
+ byte commandType = data[offset];
+ offset++;
+
+ switch (commandType) {
+ case 0x1d:
+ {
+ debugN("setParent");
+ Common::Array<LBValue> params = readParams(src, offset);
+ if (params.size() > 2)
+ error("incorrect number of parameters (%d) to setParent", params.size());
+ // TODO
+ }
+ break;
+
+ default:
+ error("unknown item command %02x in code", commandType);
+ }
+}
+
+void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
+ if (offset + 1 >= size)
+ error("went off the end of code");
+
+ byte commandType = data[offset];
+ offset++;
+
+ switch (commandType) {
+ case 0x3:
+ {
+ debugN("goto");
+ Common::Array<LBValue> params = readParams(src, offset);
+ // TODO: type-checking
+ switch (params.size()) {
+ case 1:
+ _vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, params[0].integer));
+ break;
+
+ case 2:
+ // FIXME
+ case 4:
+ // FIXME
+
+ default:
+ error("incorrect number of parameters (%d) to goto", params.size());
+ }
+ }
+ break;
+
+ default:
+ error("unknown notify command %02x in code", commandType);
+ }
+}
+
+void LBCode::runCode(LBItem *src, uint32 offset) {
+ while (true) {
+ if (offset + 1 >= size) {
+ warning("went off the end of code");
+ return;
+ }
+
+ byte tokenType = data[offset];
+ offset++;
+
+ switch (tokenType) {
+ case 0x01: // FIXME
+ case kLBCodeTokenEndOfFile:
+ return;
+
+ case 0x4D:
+ runCodeCommand(src, offset);
+ break;
+
+ case 0x4E:
+ runCodeItemCommand(src, offset);
+ break;
+
+ case 0x4F:
+ runCodeNotifyCommand(src, offset);
+ break;
+
+ default:
+ debugN("at %04x: %02x ", offset - 1, tokenType);
+ for (uint i = 0; i < size; i++)
+ debugN("%02x ", data[offset++]);
+ debugN("\n");
+ error("unknown token %02x in code", tokenType);
+ }
+
+ byte nextToken = data[offset];
+ offset++;
+ if (nextToken != kLBCodeTokenEndOfStatement)
+ warning("missing EndOfStatement after code statement (got %04x)", nextToken);
+ if (nextToken == kLBCodeTokenEndOfFile)
+ return;
+
+ debugN("\n");
+ }
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h
new file mode 100644
index 0000000000..7960e79035
--- /dev/null
+++ b/engines/mohawk/livingbooks_code.h
@@ -0,0 +1,94 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MOHAWK_LIVINGBOOKS_CODE_H
+#define MOHAWK_LIVINGBOOKS_CODE_H
+
+#include "common/substream.h"
+
+namespace Mohawk {
+
+class MohawkEngine_LivingBooks;
+class LBItem;
+
+enum LBValueType {
+ kLBValueString,
+ kLBValueInteger
+};
+
+struct LBValue {
+ LBValue() { type = kLBValueInteger; integer = 0; }
+
+ LBValueType type;
+ Common::String string;
+ int integer;
+
+ bool operator==(const LBValue &x) const;
+ bool operator!=(const LBValue &x) const;
+};
+
+enum {
+ kLBCodeLiteralInteger = 0x1
+};
+
+enum {
+ kLBCodeTokenString = 0x1,
+ kLBCodeTokenLiteral = 0x5,
+ kLBCodeTokenChar = 0x6,
+ kLBCodeTokenEndOfStatement = 0x7,
+ kLBCodeTokenEndOfFile = 0x8,
+ kLBCodeTokenOpenBracket = 0xf,
+ kLBCodeTokenCloseBracket = 0x10,
+ kLBCodeTokenLong = 0x11,
+
+ kLBCodeTokenEquals = 0x22, // TODO: maybe..
+ kLBCodeTokenQuote = 0x27, // "'"
+ kLBCodeTokenComma = 0x2c // ","
+};
+
+class LBCode {
+public:
+ LBCode(MohawkEngine_LivingBooks *vm);
+ ~LBCode();
+
+ void runCode(LBItem *src, uint32 offset);
+
+protected:
+ MohawkEngine_LivingBooks *_vm;
+
+ uint32 size;
+ byte *data;
+
+ Common::HashMap<uint16, Common::String> strings;
+
+ Common::Array<LBValue> readParams(LBItem *src, uint32 &offset);
+ void runCodeCommand(LBItem *src, uint32 &offset);
+ void runCodeItemCommand(LBItem *src, uint32 &offset);
+ void runCodeNotifyCommand(LBItem *src, uint32 &offset);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index ea3859fe24..efecb1bf98 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS = \
dialogs.o \
graphics.o \
livingbooks.o \
+ livingbooks_code.o \
mohawk.o \
myst.o \
myst_areas.o \