aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk
diff options
context:
space:
mode:
authorAlyssa Milburn2011-04-07 00:30:09 +0200
committerAlyssa Milburn2011-04-07 00:30:09 +0200
commitbbe2c437a86c6435012d5699dad8e9b4c88ca60c (patch)
tree156e2d60269b2c784259a016b614b9fe4f405220 /engines/mohawk
parent3bbeee90c02e613ab2fcf870a26dbcfb3aad60e1 (diff)
downloadscummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.tar.gz
scummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.tar.bz2
scummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.zip
MOHAWK: Replace most of the LBCode interpreter.
Diffstat (limited to 'engines/mohawk')
-rw-r--r--engines/mohawk/livingbooks.cpp18
-rw-r--r--engines/mohawk/livingbooks_code.cpp904
-rw-r--r--engines/mohawk/livingbooks_code.h169
3 files changed, 792 insertions, 299 deletions
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 3f53eae4b8..3d22683739 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -2690,24 +2690,6 @@ void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) {
debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16));
}
-bool LBValue::operator==(const LBValue &x) const {
- if (type != x.type) return false;
-
- switch (type) {
- case kLBValueString:
- return string == x.string;
-
- case kLBValueInteger:
- return integer == x.integer;
- default:
- error("Unknown type when testing for equality");
- }
-}
-
-bool LBValue::operator!=(const LBValue &x) const {
- return !(*this == x);
-}
-
enum LBTokenType {
kLBNoToken,
kLBNameToken,
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
index a11ac1cfe9..c0718d941a 100644
--- a/engines/mohawk/livingbooks_code.cpp
+++ b/engines/mohawk/livingbooks_code.cpp
@@ -28,18 +28,76 @@
namespace Mohawk {
+bool LBValue::operator==(const LBValue &x) const {
+ if (type != x.type) {
+ if (isNumeric() && x.isNumeric())
+ return toDouble() == x.toDouble();
+ else
+ return false;
+ }
+
+ switch (type) {
+ case kLBValueString:
+ return string == x.string;
+ case kLBValueInteger:
+ return integer == x.integer;
+ case kLBValueReal:
+ return real == x.real;
+ case kLBValuePoint:
+ return point == x.point;
+ case kLBValueRect:
+ return rect == x.rect;
+ case kLBValueItemPtr:
+ return item == x.item;
+ default:
+ error("Unknown type when testing for equality");
+ }
+}
+
+bool LBValue::operator!=(const LBValue &x) const {
+ return !(*this == x);
+}
+
+bool LBValue::isNumeric() const {
+ if (type == kLBValueInteger || type == kLBValueReal)
+ return true;
+
+ // TODO: string checks
+
+ return false;
+}
+
+bool LBValue::isZero() const {
+ return toInt() == 0; // FIXME
+}
+
+int LBValue::toInt() const {
+ return integer; // FIXME
+}
+
+double LBValue::toDouble() const {
+ return real; // FIXME
+}
+
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);
+ _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()) {
+ if (bcodStream->pos() + 1 == bcodStream->size()) {
+ warning("ran out of bytes while reading strings");
+ break;
+ }
uint16 unknown = bcodStream->readUint16();
if (unknown != 0) {
warning("unknown was %04x, not zero, while reading strings", unknown);
@@ -48,287 +106,661 @@ LBCode::LBCode(MohawkEngine_LivingBooks *vm) : _vm(vm) {
break;
}
Common::String string = _vm->readString(bcodStream);
- strings[pos] = string;
+ _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;
+ 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");
+LBValue LBCode::runCode(LBItem *src, uint32 offset) {
+ // TODO: re-entrancy issues?
+ _currSource = src;
+ _currOffset = offset;
- byte numParams = data[offset];
- offset++;
+ return runCode(kTokenEndOfFile);
+}
- if (!numParams) {
- debugN("()\n");
- return params;
+void LBCode::nextToken() {
+ if (_currOffset + 1 >= _size) {
+ // TODO
+ warning("went off the end of code");
+ _currToken = kTokenEndOfFile;
+ _currValue = LBValue();
+ return;
}
- byte nextToken = data[offset];
- offset++;
- if (nextToken != kLBCodeTokenOpenBracket)
- error("missing ( before code parameter list (got %02x)", nextToken);
- debugN("(");
+ _currToken = _data[_currOffset++];
- 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(", ");
+ // We slurp any value associated with the parameter here too, to simplify things.
+ switch (_currToken) {
+ case kTokenIdentifier:
+ {
+ uint16 offset = READ_BE_UINT16(_data + _currOffset);
+ // TODO: check string exists
+ _currValue = _strings[offset];
+ _currOffset += 2;
}
+ break;
- 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);
- }
+ case kTokenLiteral:
+ {
+ byte literalType = _data[_currOffset++];
+ switch (literalType) {
+ case kLBCodeLiteralInteger:
+ _currValue = READ_BE_UINT16(_data + _currOffset);
+ _currOffset += 2;
break;
+ default:
+ error("unknown kTokenLiteral type %02x", 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 kTokenConstMode:
+ case kTokenConstEventId:
+ case 0x5e: // TODO: ??
+ case kTokenKeycode:
+ _currValue = READ_BE_UINT16(_data + _currOffset);
+ _currOffset += 2;
+ 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 kTokenGeneralCommand:
+ case kTokenItemCommand:
+ case kTokenNotifyCommand:
+ case kTokenPropListCommand:
+ case kTokenRectCommand:
+ _currValue = _data[_currOffset++];
+ //_currValue = READ_BE_UINT16(_data + _currOffset);
+ //_currOffset += 2;
+ 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 kTokenString:
+ {
+ uint16 offset = READ_BE_UINT16(_data + _currOffset);
+ // TODO: check string exists
+ _currValue = _strings[offset];
+ _currOffset += 2;
+ }
+ break;
- case 0x31:
- {
- // TODO
- uint16 intValue = READ_BE_UINT16(data + offset);
- offset += 2;
- nextValue.type = kLBValueInteger;
- nextValue.integer = intValue;
- debugN("%d", nextValue.integer);
- }
- break;
+ default:
+ _currValue = LBValue();
+ break;
+ }
+}
- case 0x4d:
- // TODO
- runCodeCommand(src, offset);
- break;
+LBValue LBCode::runCode(byte terminator) {
+ LBValue result;
- case 0x5f:
- // keycode
- nextValue.type = kLBValueInteger;
- nextValue.integer = data[offset];
- debugN("%d", nextValue.integer);
- offset++;
- offset++; // TODO
+ while (true) {
+ nextToken();
+ if (_currToken == kTokenEndOfFile)
break;
+ parseStatement();
+ if (_stack.size())
+ result = _stack.pop();
+ if (_currToken == terminator || _currToken == kTokenEndOfFile)
+ break;
+ if (_currToken != kTokenEndOfStatement && _currToken != kTokenEndOfFile)
+ error("missing EOS");
+ debugN("\n");
+ }
- default:
- error("unknown token %02x in code parameter", nextToken);
- }
+ return result;
+}
- params.push_back(nextValue);
+void LBCode::parseStatement() {
+ // FIXME: logical operators
+ parseComparisons();
+}
+
+void LBCode::parseComparisons() {
+ parseConcat();
+
+ if (_currToken != kTokenEquals && _currToken != kTokenLessThan && _currToken != kTokenGreaterThan &&
+ _currToken != kTokenLessThanEq && _currToken != kTokenGreaterThanEq && _currToken != kTokenNotEq)
+ return;
+ byte comparison = _currToken;
+ switch (comparison) {
+ case kTokenEquals:
+ debugN(" == ");
+ break;
+ case kTokenLessThan:
+ debugN(" < ");
+ break;
+ case kTokenGreaterThan:
+ debugN(" > ");
+ break;
+ case kTokenLessThanEq:
+ debugN(" <= ");
+ break;
+ case kTokenGreaterThanEq:
+ debugN(" >= ");
+ break;
+ case kTokenNotEq:
+ debugN(" != ");
+ break;
}
- nextToken = data[offset];
- offset++;
- if (nextToken != kLBCodeTokenCloseBracket)
- error("missing ) after code parameter list (got %02x)", nextToken);
- debugN(")");
+ nextToken();
+ parseConcat();
+
+ if (_stack.size() < 2)
+ error("comparison didn't get enough values");
+ LBValue val2 = _stack.pop();
+ LBValue val1 = _stack.pop();
+ bool result;
+ // FIXME: should work for non-integers!!
+ switch (comparison) {
+ case kTokenEquals:
+ result = (val1 == val2);
+ break;
+ case kTokenLessThan:
+ result = (val1.integer < val2.integer);
+ break;
+ case kTokenGreaterThan:
+ result = (val1.integer > val2.integer);
+ break;
+ case kTokenLessThanEq:
+ result = (val1.integer <= val2.integer);
+ break;
+ case kTokenGreaterThanEq:
+ result = (val1.integer >= val2.integer);
+ break;
+ case kTokenNotEq:
+ result = (val1 != val2);
+ break;
+ }
- return params;
+ debugN(" [--> %s]", result ? "true" : "false");
+ _stack.push(result ? 1 : 0);
}
-void LBCode::runCodeCommand(LBItem *src, uint32 &offset) {
- if (offset + 1 >= size)
- error("went off the end of code");
+void LBCode::parseConcat() {
+ parseArithmetic1();
+ // FIXME: string concat
+}
- byte commandType = data[offset];
- offset++;
+void LBCode::parseArithmetic1() {
+ parseArithmetic2();
+ // FIXME: -/+ math operators
+}
- switch (commandType) {
- case 0x23:
- {
- debugN("setViewOrigin");
- Common::Array<LBValue> params = readParams(src, offset);
- // TODO
- }
- break;
+void LBCode::parseArithmetic2() {
+ // FIXME: other math operators
+ parseMain();
+}
- case 0x36:
- {
- debugN("setWorld");
- Common::Array<LBValue> params = readParams(src, offset);
- // TODO
- }
- break;
+void LBCode::parseMain() {
+ byte prefix = 0;
+ if (_currToken == kTokenMinus || _currToken == kTokenPlus) {
+ debugN("%s", _currToken == kTokenMinus ? "-" : "+");
+ prefix = _currToken;
+ nextToken();
+ }
- case 0x42:
+ switch (_currToken) {
+ case kTokenIdentifier:
+ assert(_currValue.type == kLBValueString);
{
- 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;
+ Common::String varname = _currValue.string;
+ debugN("%s", varname.c_str());
+ nextToken();
+ if (varname == "self") {
+ _stack.push(LBValue(_currSource));
+ if (_currToken == kTokenAssign)
+ error("attempted assignment to self");
+ } else if (_currToken == kTokenAssign) {
+ debugN(" = ");
+ nextToken();
+ parseStatement();
+ if (!_stack.size())
+ error("assignment failed");
+ LBValue *val = &_vm->_variables[varname];
+ *val = _stack.pop();
+ _stack.push(*val);
} 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;
+ _stack.push(_vm->_variables[varname]);
}
+ // FIXME: pre/postincrement
}
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
- }
+ case kTokenLiteral:
+ case kTokenConstMode:
+ case kTokenConstEventId:
+ case 0x5e: // TODO: ??
+ case kTokenKeycode:
+ assert(_currValue.type == kLBValueInteger);
+ debugN("%d", _currValue.integer);
+ _stack.push(_currValue);
+ nextToken();
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
- }
+ case kTokenString:
+ assert(_currValue.type == kLBValueString);
+ debugN("\"%s\"", _currValue.string.c_str());
+ _stack.push(_currValue);
+ nextToken();
break;
- case 0x52:
- {
- debugN("key");
- Common::Array<LBValue> params = readParams(src, offset);
- // TODO
- }
+ case kTokenOpenBracket:
+ debugN("(");
+ nextToken();
+ parseStatement();
+ if (_currToken != kTokenCloseBracket)
+ error("no kTokenCloseBracket (%02x), multiple entries?", _currToken);
+ debugN(")");
+ nextToken();
break;
- case 0x5E:
- {
- debugN("setPageFade");
- Common::Array<LBValue> params = readParams(src, offset);
- // TODO
- }
+ case kTokenNot:
+ debugN("!");
+ nextToken();
+ // not parseStatement, ! takes predecence over logical ops
+ parseComparisons();
+ if (!_stack.size())
+ error("not op failed");
+ _stack.push(_stack.pop().isZero() ? 1 : 0);
+ break;
+
+ case kTokenGeneralCommand:
+ runGeneralCommand();
+ break;
+
+ case kTokenItemCommand:
+ runItemCommand();
+ break;
+
+ case kTokenNotifyCommand:
+ runNotifyCommand();
break;
default:
- error("unknown command %02x in code", commandType);
+ error("unknown token %02x in code", _currToken);
+ }
+
+ if (prefix) {
+ if (!_stack.size())
+ error("+/- prefix failed");
+ LBValue val = _stack.pop();
+ assert(val.isNumeric());
+ // FIXME
+ if (prefix == kTokenMinus)
+ val.integer--;
+ else
+ val.integer++;
+ _stack.push(val);
}
}
-void LBCode::runCodeItemCommand(LBItem *src, uint32 &offset) {
- if (offset + 1 >= size)
+Common::Array<LBValue> LBCode::readParams() {
+ Common::Array<LBValue> params;
+
+ if (_currOffset + 1 >= _size)
error("went off the end of code");
- byte commandType = data[offset];
- offset++;
+ byte numParams = _data[_currOffset++];
- 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
+ if (!numParams) {
+ debugN("()\n");
+ nextToken();
+ return params;
+ }
+
+ nextToken();
+ if (_currToken != kTokenOpenBracket)
+ error("missing ( before code parameter list (got %02x)", _currToken);
+ nextToken();
+ debugN("(");
+
+ for (uint i = 0; i < numParams; i++) {
+ if (i != 0) {
+ if (_currToken != ',')
+ error("missing , between code parameters (got %02x)", _currToken);
+ debugN(", ");
+ nextToken();
}
- break;
- default:
- error("unknown item command %02x in code", commandType);
+ parseStatement();
+ if (!_stack.size())
+ error("stack empty");
+ LBValue nextValue = _stack.pop();
+
+ params.push_back(nextValue);
}
+
+ if (_currToken != kTokenCloseBracket)
+ error("missing ) after code parameter list (got %02x)", _currToken);
+ nextToken();
+ debugN(")");
+
+ return params;
}
-void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
- if (offset + 1 >= size)
- error("went off the end of code");
+struct CodeCommandInfo {
+ const char *name;
+ typedef void (LBCode::*CommandFunc)(const Common::Array<LBValue> &params);
+ CommandFunc func;
+};
+
+#define NUM_GENERAL_COMMANDS 129
+CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = {
+ { "eval", 0 },
+ { "random", 0 },
+ { "stringLen", 0 },
+ { "substring", 0 },
+ { "max", 0 },
+ { "min", 0 },
+ { "abs", 0 },
+ { "getRect", 0 }, // also "makeRect"
+ { "makePt", 0 }, // also "makePair"
+ { "topleft", 0 },
+ { "bottomright", 0 },
+ { "mousePos", 0 },
+ { "top", 0 },
+ { "left", 0 },
+ { "bottom", 0 },
+ // 0x10
+ { "right", 0 },
+ { "xpos", 0 },
+ { "ypos", 0 },
+ { "playFrom", 0 },
+ { "move", 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { "setDragParams", 0 },
+ { "resetDragParams", 0 },
+ { "enableRollover", &LBCode::cmdUnimplemented /* FIXME */ },
+ { "setCursor", 0 },
+ { "width", 0 },
+ { "height", 0 },
+ { "getFrameBounds", 0 }, // also "getFrameRect"
+ { "traceRect", 0 },
+ { "sqrt", 0 },
+ // 0x20
+ { "deleteVar", 0 },
+ { "saveVars", 0 },
+ { "scriptLink", 0 },
+ { "setViewOrigin", &LBCode::cmdUnimplemented },
+ { "rectSect", 0 },
+ { "getViewOrigin", 0 },
+ { "getViewRect", 0 },
+ { "getPage", 0 },
+ { "getWorldRect", 0 },
+ { "isWorldWrap", 0 },
+ { "newList", 0 },
+ { "deleteList", 0 },
+ { "add", 0 },
+ { 0, 0 },
+ { "addAt", 0 },
+ { "getAt", 0 },
+ // 0x30
+ { 0, 0 },
+ { "getIndex", 0 },
+ { "setAt", 0 },
+ { "listLen", 0 },
+ { "deleteAt", 0 },
+ { "clearList", 0 },
+ { "setWorld", 0 },
+ { "setProperty", 0 },
+ { "getProperty", 0 },
+ { "copyList", 0 },
+ { "invoke", 0 },
+ { "exec", 0 },
+ { "return", 0 },
+ { "sendSync", 0 },
+ { "moveViewOrigin", 0 },
+ { "addToGroup", 0 },
+ // 0x40
+ { "removeFromGroup", 0 },
+ { "clearGroup", 0 },
+ { "setPlayParams", &LBCode::cmdSetPlayParams },
+ { "autoEvent", 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { "getID", 0 },
+ { "setCursorPosition", 0 },
+ { "getTime", 0 },
+ { "logWriteLn", 0 },
+ { "logWrite", 0 },
+ { "getLanguage", 0 },
+ { "setLanguage", 0 },
+ { "getSequence", 0 },
+ { "setSequence", 0 },
+ { "getFileSpec", 0 },
+ // 0x50
+ { "setKeyEvent", &LBCode::cmdSetKeyEvent },
+ { "setHitTest", &LBCode::cmdSetHitTest },
+ { "key", &LBCode::cmdKey },
+ { "deleteKeyEvent", 0 },
+ { "setDisplay", &LBCode::cmdUnimplemented },
+ { "getDisplay", 0 },
+ { 0, 0 },
+ { "lbxCreate", 0 },
+ { "lbxFunc", 0 },
+ { "waitCursor", 0 },
+ { "debugBreak", 0 },
+ { "menuItemEnable", 0 },
+ { "showChannel", 0 },
+ { "hideChannel", 0 },
+ { "setPageFade", 0 },
+ { "normalize", 0 },
+ // 0x60 (v5+)
+ { "addEvent", 0 },
+ { "setCueEvent", 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { "getName", 0 },
+ { "getProperties", 0 },
+ { "createItem", 0 },
+ { "setProperties", 0 },
+ { "alert", 0 },
+ { "getUniqueID", 0 },
+ { "isNumeric", 0 },
+ { "setKeyFocus", 0 },
+ { "getKeyFocus", 0 },
+ { "isItem", 0 },
+ { "itemHit", 0 },
+ { "getItem ", 0 },
+ // 0x70
+ { 0, 0 },
+ { "setCascade", 0 },
+ { "getCascade", 0 },
+ { "getRes", 0 },
+ { "setRes", 0 },
+ { "getFilename", 0 },
+ { "resEnumNames", 0 },
+ { "isList", 0 },
+ { "resetRect", 0 },
+ { "setVolume", 0 },
+ { "getVolume", 0 },
+ { "pause", 0 },
+ { "getTextWidth", 0 },
+ { "setItemVolume", 0 },
+ { "setSoundLoop", 0 },
+ // 0x80
+ { "setClipboard", 0 },
+ { "getResDuration", 0 }
+};
+
+void LBCode::runGeneralCommand() {
+ byte commandType = _currValue.integer;
+
+ if (commandType == 0 || commandType > NUM_GENERAL_COMMANDS)
+ error("bad command type 0x%02x in runGeneralCommand", commandType);
+
+ CodeCommandInfo &info = generalCommandInfo[commandType - 1];
+ debugN("%s", info.name);
+ Common::Array<LBValue> params = readParams();
+
+ if (!info.func)
+ error("general command '%s' (0x%02x) unimplemented", info.name, commandType);
+ (this->*(info.func))(params);
+}
+
+void LBCode::cmdUnimplemented(const Common::Array<LBValue> &params) {
+ warning("unimplemented command called");
+}
+
+void LBCode::cmdSetPlayParams(const Common::Array<LBValue> &params) {
+ if (params.size() > 8)
+ error("too many parameters (%d) to setPlayParams", params.size());
+ if (!params.size())
+ error("no target for setPlayParams");
+
+ if (params[0].type != kLBValueItemPtr)
+ error("first param to setPlayParams wasn't item");
+ LBItem *target = params[0].item;
+
+ // 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;
+ }
+}
+
+void LBCode::cmdSetKeyEvent(const Common::Array<LBValue> &params) {
+ if (params.size() != 2)
+ error("incorrect number of parameters (%d) to setKeyEvent", params.size());
+
+ // FIXME: params[0] is key, params[1] is opcode id
+ warning("ignoring setKeyEvent");
+}
+
+void LBCode::cmdSetHitTest(const Common::Array<LBValue> &params) {
+ if (params.size() > 2)
+ error("incorrect number of parameters (%d) to setHitTest", params.size());
+ warning("ignoring setHitTest");
+}
+
+void LBCode::cmdKey(const Common::Array<LBValue> &params) {
+ _stack.push(0); // FIXME
+ warning("ignoring Key");
+}
+
+#define NUM_ITEM_COMMANDS 34
+CodeCommandInfo itemCommandInfo[NUM_ITEM_COMMANDS] = {
+ { "clone", 0 },
+ { "destroy", 0 },
+ { "dragBeginFrom", 0 },
+ { "dragEnd", 0 },
+ { "enableLocal", 0 },
+ { "enable", 0 },
+ { "showLocal", 0 },
+ { "show", 0 },
+ { "getFrame", 0 },
+ { "getParent", 0 },
+ { "getPosition" , 0 },
+ { "getText", 0 },
+ { "getZNext", 0 },
+ { "getZPrev", 0 },
+ { "hitTest", 0 },
+ // 0x10
+ { "isAmbient", 0 },
+ { "isEnabled", 0 },
+ { "isMuted", 0 },
+ { "isPlaying", &LBCode::itemIsPlaying },
+ { "isVisible", 0 },
+ { "isLoaded", 0 },
+ { "isDragging", 0 },
+ { "load", 0 },
+ { "moveTo", 0 },
+ { "mute", 0 },
+ { "play", 0 },
+ { "seek", 0 },
+ { "seekToFrame", 0 },
+ { "setParent", &LBCode::itemSetParent },
+ { "setZOrder", 0 },
+ { "setText", 0 },
+ // 0x20
+ { "stop", 0 },
+ { "unload", 0 },
+ { "unloadSync", 0}
+};
+
+void LBCode::runItemCommand() {
+ byte commandType = _currValue.integer;
+
+ if (commandType == 0 || commandType > NUM_ITEM_COMMANDS)
+ error("bad command type 0x%02x in runItemCommand", commandType);
+
+ CodeCommandInfo &info = itemCommandInfo[commandType - 1];
+ debugN("%s", info.name);
+ Common::Array<LBValue> params = readParams();
+
+ if (!info.func)
+ error("item command '%s' (0x%02x) unimplemented", info.name, commandType);
+ (this->*(info.func))(params);
+}
+
+void LBCode::itemIsPlaying(const Common::Array<LBValue> &params) {
+ // TODO
+ warning("ignoring isPlaying");
+ _stack.push(0);
+}
+
+void LBCode::itemSetParent(const Common::Array<LBValue> &params) {
+ if (params.size() > 2)
+ error("incorrect number of parameters (%d) to setParent", params.size());
+ // TODO
+ warning("ignoring setParent");
+}
- byte commandType = data[offset];
- offset++;
+void LBCode::runNotifyCommand() {
+ byte commandType = _currValue.integer;
switch (commandType) {
case kLBNotifyChangePage:
{
debugN("goto");
- Common::Array<LBValue> params = readParams(src, offset);
+ Common::Array<LBValue> params = readParams();
// TODO: type-checking
+ NotifyEvent notifyEvent(kLBNotifyChangePage, 0);
switch (params.size()) {
- case 1:
- _vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, params[0].integer));
+ case 4:
+ notifyEvent.type = kLBNotifyChangeMode; // FIXME: type 8?
+ notifyEvent.newUnknown = params[0].integer; // FIXME: this is newLanguage
+ notifyEvent.newMode = params[1].integer;
+ notifyEvent.newPage = params[2].integer;
+ notifyEvent.newSubpage = params[3].integer;
break;
case 2:
- // FIXME
- case 4:
- // FIXME
+ notifyEvent.type = kLBNotifyChangeMode;
+ // FIXME: newPage and newSubpage?
+ error("can't handle goto with 2 params");
+ break;
+
+ case 1:
+ notifyEvent.param = params[0].integer;
+ break;
+
+ case 0:
+ // FIXME: use cur page?
+ error("can't handle goto with 0 params");
+ break;
default:
error("incorrect number of parameters (%d) to goto", params.size());
}
+ _vm->addNotifyEvent(notifyEvent);
}
break;
@@ -336,7 +768,7 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
case kLBNotifyGotoQuit:
{
debugN(commandType == kLBNotifyGoToControls ? "gotocontrol" : "gotoquit");
- Common::Array<LBValue> params = readParams(src, offset);
+ Common::Array<LBValue> params = readParams();
if (params.size() != 0)
error("incorrect number of parameters (%d) to notify", params.size());
_vm->addNotifyEvent(NotifyEvent(commandType, 0));
@@ -346,7 +778,7 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
case kLBNotifyIntroDone:
{
debugN("startphasemain");
- Common::Array<LBValue> params = readParams(src, offset);
+ Common::Array<LBValue> params = readParams();
if (params.size() != 0)
error("incorrect number of parameters (%d) to startphasemain", params.size());
_vm->addNotifyEvent(NotifyEvent(kLBNotifyIntroDone, 1));
@@ -358,50 +790,4 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
}
}
-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
index 7960e79035..71174cc09e 100644
--- a/engines/mohawk/livingbooks_code.h
+++ b/engines/mohawk/livingbooks_code.h
@@ -26,6 +26,8 @@
#ifndef MOHAWK_LIVINGBOOKS_CODE_H
#define MOHAWK_LIVINGBOOKS_CODE_H
+#include "common/rect.h"
+#include "common/stack.h"
#include "common/substream.h"
namespace Mohawk {
@@ -35,18 +37,70 @@ class LBItem;
enum LBValueType {
kLBValueString,
- kLBValueInteger
+ kLBValueInteger,
+ kLBValueReal,
+ kLBValuePoint,
+ kLBValueRect,
+ kLBValueItemPtr
};
struct LBValue {
- LBValue() { type = kLBValueInteger; integer = 0; }
+ LBValue() {
+ type = kLBValueInteger;
+ integer = 0;
+ }
+ LBValue(int val) {
+ type = kLBValueInteger;
+ integer = val;
+ }
+ LBValue(const Common::String &str) {
+ type = kLBValueString;
+ string = str;
+ }
+ LBValue(LBItem *itm) {
+ type = kLBValueItemPtr;
+ item = itm;
+ }
+ LBValue(const LBValue &val) {
+ type = val.type;
+ switch (type) {
+ case kLBValueString:
+ string = val.string;
+ break;
+ case kLBValueInteger:
+ integer = val.integer;
+ break;
+ case kLBValueReal:
+ real = val.real;
+ break;
+ case kLBValuePoint:
+ point = val.point;
+ break;
+ case kLBValueRect:
+ rect = val.rect;
+ break;
+ case kLBValueItemPtr:
+ item = val.item;
+ break;
+ }
+ }
LBValueType type;
Common::String string;
int integer;
+ double real;
+ Common::Point point;
+ Common::Rect rect;
+ LBItem *item;
bool operator==(const LBValue &x) const;
bool operator!=(const LBValue &x) const;
+
+ bool isNumeric() const;
+ bool isZero() const;
+
+ int toInt() const;
+ double toDouble() const;
};
enum {
@@ -54,18 +108,63 @@ enum {
};
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 // ","
+ kTokenIdentifier = 0x1,
+ kTokenLiteral = 0x5,
+ kTokenString = 0x6,
+ kTokenEndOfStatement = 0x7,
+ kTokenEndOfFile = 0x8,
+ kTokenConcat = 0xb,
+ kTokenSingleQuote = 0xc, // ??
+ kTokenDoubleQuote = 0xd, // ??
+ kTokenMultiply = 0xe,
+ kTokenOpenBracket = 0xf,
+ kTokenCloseBracket = 0x10,
+ kTokenMinus = 0x11,
+ kTokenMinusMinus = 0x12,
+ kTokenPlusEquals = 0x13,
+ kTokenPlus = 0x14,
+ kTokenPlusPlus = 0x15,
+ kTokenEquals = 0x16,
+ kTokenMinusEquals = 0x17,
+ kTokenMultiplyEquals = 0x18,
+ kTokenDivideEquals = 0x19,
+ kTokenListStart = 0x1a,
+ kTokenListEnd = 0x1b,
+ kTokenColon = 0x1c, // ??
+ kTokenLessThan = 0x1d,
+ kTokenGreaterThan = 0x1e,
+ kTokenAndEquals = 0x1f,
+ kTokenDotOperator = 0x20,
+ kTokenDivide = 0x21,
+ kTokenAssign = 0x22,
+ kTokenLessThanEq = 0x23,
+ kTokenGreaterThanEq = 0x24,
+ kTokenNotEq = 0x25,
+ kTokenQuote = 0x27, // ??
+ kTokenAnd = 0x2a,
+ kTokenComma = 0x2c,
+ kTokenConstMode = 0x31,
+ kTokenIntDivide = 0x32,
+ kTokenModulo = 0x34,
+ kTokenNot = 0x35,
+ kTokenOr = 0x37,
+ kTokenTrue = 0x39,
+ kTokenFalse = 0x3a,
+ kTokenConstDataType = 0x3b, // ??
+ kTokenConstItemType = 0x3c, // ??
+ kTokenConstEventId = 0x42,
+ kTokenConstScriptOpcode = 0x43, // ??
+ kTokenConstScriptParam = 0x44, // ??
+ kTokenGeneralCommand = 0x4d,
+ kTokenItemCommand = 0x4e,
+ kTokenNotifyCommand = 0x4f,
+ // 0x5e?!
+ kTokenKeycode = 0x5f,
+
+ // v5 only:
+ kTokenLocal = 0x61,
+ kTokenPropListCommand = 0x70,
+ kTokenRectCommand = 0x71
};
class LBCode {
@@ -73,20 +172,46 @@ public:
LBCode(MohawkEngine_LivingBooks *vm);
~LBCode();
- void runCode(LBItem *src, uint32 offset);
+ LBValue runCode(LBItem *src, uint32 offset);
protected:
MohawkEngine_LivingBooks *_vm;
- uint32 size;
- byte *data;
+ uint32 _size;
+ byte *_data;
+ Common::HashMap<uint16, Common::String> _strings;
+
+ uint32 _currOffset;
+ LBItem *_currSource;
+
+ Common::Stack<LBValue> _stack;
+ byte _currToken;
+ LBValue _currValue;
- Common::HashMap<uint16, Common::String> strings;
+ void nextToken();
- 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);
+ LBValue runCode(byte terminator);
+ void parseStatement();
+ void parseComparisons();
+ void parseConcat();
+ void parseArithmetic1();
+ void parseArithmetic2();
+ void parseMain();
+
+ Common::Array<LBValue> readParams();
+ void runGeneralCommand();
+ void runItemCommand();
+ void runNotifyCommand();
+
+public:
+ void cmdUnimplemented(const Common::Array<LBValue> &params);
+ void cmdSetPlayParams(const Common::Array<LBValue> &params);
+ void cmdSetKeyEvent(const Common::Array<LBValue> &params);
+ void cmdSetHitTest(const Common::Array<LBValue> &params);
+ void cmdKey(const Common::Array<LBValue> &params);
+
+ void itemSetParent(const Common::Array<LBValue> &params);
+ void itemIsPlaying(const Common::Array<LBValue> &params);
};
} // End of namespace Mohawk