diff options
-rw-r--r-- | engines/mohawk/livingbooks.cpp | 64 | ||||
-rw-r--r-- | engines/mohawk/livingbooks.h | 31 | ||||
-rw-r--r-- | engines/mohawk/livingbooks_code.cpp | 386 | ||||
-rw-r--r-- | engines/mohawk/livingbooks_code.h | 94 | ||||
-rw-r--r-- | engines/mohawk/module.mk | 1 |
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 \ |