diff options
| author | Alyssa Milburn | 2011-07-07 09:24:10 +0200 |
|---|---|---|
| committer | Alyssa Milburn | 2011-07-07 09:24:10 +0200 |
| commit | 66e81d633f987310f12038147ae01b8ce4f640f7 (patch) | |
| tree | 6035a7124789c3e6cdff5f603e9933529b7efdc7 /engines/mohawk | |
| parent | affaa1f4d6cf5f27f654029133b1aec7b9eca4b5 (diff) | |
| parent | 72da8ef5adf82d8a65da299207f30af5058ca8a9 (diff) | |
| download | scummvm-rg350-66e81d633f987310f12038147ae01b8ce4f640f7.tar.gz scummvm-rg350-66e81d633f987310f12038147ae01b8ce4f640f7.tar.bz2 scummvm-rg350-66e81d633f987310f12038147ae01b8ce4f640f7.zip | |
Merge remote-tracking branch 'origin/master' into soltys_wip2
Diffstat (limited to 'engines/mohawk')
| -rw-r--r-- | engines/mohawk/console.cpp | 21 | ||||
| -rw-r--r-- | engines/mohawk/cursors.cpp | 11 | ||||
| -rw-r--r-- | engines/mohawk/cursors.h | 2 | ||||
| -rw-r--r-- | engines/mohawk/detection_tables.h | 15 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks.cpp | 261 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks.h | 3 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks_code.cpp | 491 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks_code.h | 12 | ||||
| -rw-r--r-- | engines/mohawk/myst_scripts.cpp | 1 | ||||
| -rw-r--r-- | engines/mohawk/myst_stacks/mechanical.cpp | 20 | ||||
| -rw-r--r-- | engines/mohawk/resource.cpp | 3 | ||||
| -rw-r--r-- | engines/mohawk/sound.cpp | 13 |
12 files changed, 530 insertions, 323 deletions
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index 05012bec3d..e7dc84606c 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -700,14 +700,25 @@ bool LivingBooksConsole::Cmd_DrawImage(int argc, const char **argv) { } bool LivingBooksConsole::Cmd_ChangePage(int argc, const char **argv) { - if (argc == 1) { - DebugPrintf("Usage: changePage <page> [<mode>]\n"); + if (argc < 2 || argc > 3) { + DebugPrintf("Usage: changePage <page>[.<subpage>] [<mode>]\n"); return true; } - if (_vm->tryLoadPageStart(argc == 2 ? _vm->getCurMode() : (LBMode)atoi(argv[2]), atoi(argv[1]))) - return false; - DebugPrintf("no such page %d\n", atoi(argv[1])); + int page, subpage = 0; + if (sscanf(argv[1], "%d.%d", &page, &subpage) == 0) { + DebugPrintf("Usage: changePage <page>[.<subpage>] [<mode>]\n"); + return true; + } + LBMode mode = argc == 2 ? _vm->getCurMode() : (LBMode)atoi(argv[2]); + if (subpage == 0) { + if (_vm->tryLoadPageStart(mode, page)) + return false; + } else { + if (_vm->loadPage(mode, page, subpage)) + return false; + } + DebugPrintf("no such page %d.%d\n", page, subpage); return true; } diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index 3284a3228f..78e099ccfe 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -252,6 +252,17 @@ void LivingBooksCursorManager_v2::setCursor(uint16 id) { } } +void LivingBooksCursorManager_v2::setCursor(const Common::String &name) { + if (!_sysArchive) + return; + + uint16 id = _sysArchive->findResourceID(ID_TCUR, name); + if (id == 0xffff) + error("Could not find cursor '%s'", name.c_str()); + else + setCursor(id); +} + PECursorManager::PECursorManager(const Common::String &appName) { _exe = new Common::PEResources(); diff --git a/engines/mohawk/cursors.h b/engines/mohawk/cursors.h index d92b6b4285..7bfa491904 100644 --- a/engines/mohawk/cursors.h +++ b/engines/mohawk/cursors.h @@ -56,6 +56,7 @@ public: virtual void showCursor(); virtual void hideCursor(); virtual void setCursor(uint16 id); + virtual void setCursor(const Common::String &name) {} virtual void setDefaultCursor(); virtual bool hasSource() const { return false; } @@ -157,6 +158,7 @@ public: ~LivingBooksCursorManager_v2(); void setCursor(uint16 id); + void setCursor(const Common::String &name); bool hasSource() const { return _sysArchive != 0; } private: diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 2243dd1c1d..01eac0aaba 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -1474,6 +1474,21 @@ static const MohawkGameDescription gameDescriptions[] = { 0 }, + { + { + "arthurrace", + "", + AD_ENTRY1("BookOutline", "f0a9251824a648fce1b49cb7c1a0ba67"), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_UNSTABLE, + Common::GUIO_NONE + }, + GType_LIVINGBOOKSV3, + 0, + 0 + }, + // From zerep in bug #3287894 { { diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 06500bc725..f9d18ff7ff 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -87,8 +87,14 @@ void LBPage::open(Archive *mhk, uint16 baseId) { _baseId = baseId; _vm->addArchive(_mhk); - if (_vm->hasResource(ID_BCOD, baseId)) + if (!_vm->hasResource(ID_BCOD, baseId)) { + // assume that BCOD is mandatory for v4/v5 + if (_vm->getGameType() == GType_LIVINGBOOKSV4 || _vm->getGameType() == GType_LIVINGBOOKSV5) + error("missing BCOD resource (id %d)", baseId); + _code = new LBCode(_vm, 0); + } else { _code = new LBCode(_vm, baseId); + } loadBITL(baseId); for (uint i = 0; i < _items.size(); i++) @@ -2300,8 +2306,6 @@ void LBItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian * { assert(size == 4); uint offset = stream->readUint32(); - if (!_page->_code) - error("no BCOD?"); _page->_code->runCode(this, offset); } break; @@ -2823,8 +2827,6 @@ int LBItem::runScriptEntry(LBScriptEntry *entry) { break; case kLBOpSendExpression: - if (!_page->_code) - error("no BCOD?"); _page->_code->runCode(this, entry->offset); break; @@ -2858,8 +2860,6 @@ int LBItem::runScriptEntry(LBScriptEntry *entry) { case kLBOpJumpUnlessExpression: case kLBOpBreakExpression: case kLBOpJumpToExpression: - if (!_page->_code) - error("no BCOD?"); { LBValue r = _page->_code->runCode(this, entry->offset); // FIXME @@ -2884,257 +2884,24 @@ void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) { debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16)); } -enum LBTokenType { - kLBNoToken, - kLBNameToken, - kLBStringToken, - kLBOperatorToken, - kLBIntegerToken, - kLBEndToken -}; - -static Common::String readToken(const Common::String &source, uint &pos, LBTokenType &type) { - Common::String token; - type = kLBNoToken; - - bool done = false; - while (pos < source.size() && !done) { - if (type == kLBStringToken) { - if (source[pos] == '"') { - pos++; - return token; - } - - token += source[pos]; - pos++; - continue; - } - - switch (source[pos]) { - case ' ': - pos++; - done = true; - break; - - case ')': - if (type == kLBNoToken) { - type = kLBEndToken; - return Common::String(); - } - done = true; - break; - - case ';': - if (type == kLBNoToken) { - pos++; - type = kLBEndToken; - return Common::String(); - } - done = true; - break; - - case '@': - // FIXME - error("found @ in string '%s', not supported yet", source.c_str()); - - case '+': - case '-': - case '!': - case '=': - case '>': - case '<': - if (type == kLBNoToken) - type = kLBOperatorToken; - if (type == kLBOperatorToken) - token += source[pos]; - else - done = true; - break; - - case '"': - if (type == kLBNoToken) - type = kLBStringToken; - else - done = true; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (type == kLBNoToken) - type = kLBIntegerToken; - if (type == kLBNameToken || type == kLBIntegerToken) - token += source[pos]; - else - done = true; - break; - - default: - if (type == kLBNoToken) - type = kLBNameToken; - if (type == kLBNameToken) - token += source[pos]; - else - done = true; - break; - } - - if (!done) - pos++; - } - - if (type == kLBStringToken) - error("readToken: ran out of input while parsing string from '%s'", source.c_str()); - - if (!token.size()) { - assert(type == kLBNoToken); - type = kLBEndToken; - } - - return token; -} - -LBValue LBItem::parseValue(const Common::String &source, uint &pos) { - LBTokenType type, postOpType; - Common::String preOp, postOp; - - Common::String str = readToken(source, pos, type); - if (type == kLBOperatorToken) { - preOp = str; - str = readToken(source, pos, type); - } - - LBValue value; - if (type == kLBStringToken) { - value.type = kLBValueString; - value.string = str; - } else if (type == kLBIntegerToken) { - value.type = kLBValueInteger; - value.integer = atoi(str.c_str()); - } else if (type == kLBNameToken) { - value = _vm->_variables[str]; - } else { - error("expected string/integer as value in '%s', got '%s'", source.c_str(), str.c_str()); - } - - uint readAheadPos = pos; - postOp = readToken(source, readAheadPos, postOpType); - if (postOpType != kLBEndToken) { - if (postOpType != kLBOperatorToken) - error("expected operator after '%s' in '%s', got '%s'", str.c_str(), source.c_str(), postOp.c_str()); - // might be a comparison operator, caller will handle other cases if valid - if (postOp == "-" || postOp == "+") { - pos = readAheadPos; - LBValue nextValue = parseValue(source, pos); - if (value.type != kLBValueInteger || nextValue.type != kLBValueInteger) - error("expected integer for arthmetic operator in '%s'", source.c_str()); - if (postOp == "+") - value.integer += nextValue.integer; - else if (postOp == "-") - value.integer -= nextValue.integer; - } - } - - if (preOp.size()) { - if (preOp == "!") { - if (value.type == kLBValueInteger) - value.integer = !value.integer; - else - error("expected integer after ! operator in '%s'", source.c_str()); - } else { - error("expected valid operator before '%s' in '%s', got '%s'", str.c_str(), source.c_str(), preOp.c_str()); - } - } - - return value; -} - void LBItem::runCommand(const Common::String &command) { - uint pos = 0; - LBTokenType type; + LBCode tempCode(_vm, 0); debug(2, "running command '%s'", command.c_str()); - while (pos < command.size()) { - Common::String varname = readToken(command, pos, type); - if (type != kLBNameToken) - error("expected name as lvalue of command '%s', got '%s'", command.c_str(), varname.c_str()); - Common::String op = readToken(command, pos, type); - if (type != kLBOperatorToken || (op != "=" && op != "++" && op != "--")) - error("expected assignment/postincrement/postdecrement operator for command '%s', got '%s'", command.c_str(), op.c_str()); - - if (op == "=") { - LBValue value = parseValue(command, pos); - _vm->_variables[varname] = value; - } else { - if (_vm->_variables[varname].type != kLBValueInteger) - error("expected integer after postincrement/postdecrement operator in '%s'", command.c_str()); - if (op == "++") - _vm->_variables[varname].integer++; - else if (op == "--") - _vm->_variables[varname].integer--; - } - - if (pos < command.size() && command[pos] == ';') - pos++; - } + uint offset = tempCode.parseCode(command); + tempCode.runCode(this, offset); } bool LBItem::checkCondition(const Common::String &condition) { - uint pos = 0; - LBTokenType type; + LBCode tempCode(_vm, 0); debug(3, "checking condition '%s'", condition.c_str()); - if (condition.size() <= pos || condition[pos] != '(') - error("bad condition '%s' (started wrong)", condition.c_str()); - pos++; - - LBValue value1 = parseValue(condition, pos); - - Common::String op = readToken(condition, pos, type); - if (type == kLBEndToken) { - if (condition.size() != pos + 1 || condition[pos] != ')') - error("bad condition '%s' (ended wrong)", condition.c_str()); - - if (value1.type == kLBValueInteger) - return value1.integer; - else - error("expected comparison operator for condition '%s'", condition.c_str()); - } - if (type != kLBOperatorToken || (op != "!=" && op != "==" && op != ">" && op != "<" && op != ">=" && op != "<=")) - error("expected comparison operator for condition '%s', got '%s'", condition.c_str(), op.c_str()); - - LBValue value2 = parseValue(condition, pos); - - if (condition.size() != pos + 1 || condition[pos] != ')') - error("bad condition '%s' (ended wrong)", condition.c_str()); - - if (op == "!=") - return (value1 != value2); - else if (op == "==") - return (value1 == value2); - - if (value1.type != kLBValueInteger || value2.type != kLBValueInteger) - error("evaluation operator %s in condition '%s' expected two integer operands!", op.c_str(), condition.c_str()); - - if (op == ">") - return (value1.integer > value2.integer); - else if (op == ">=") - return (value1.integer >= value2.integer); - else if (op == "<") - return (value1.integer < value2.integer); - else if (op == "<=") - return (value1.integer <= value2.integer); + uint offset = tempCode.parseCode(condition); + LBValue result = tempCode.runCode(this, offset); - return false; // unreachable + return result.toInt(); } LBSoundItem::LBSoundItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) { diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index ed198a60c1..ad2fe56a52 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -434,7 +434,6 @@ protected: void runScript(uint event, uint16 data = 0, uint16 from = 0); int runScriptEntry(LBScriptEntry *entry); - LBValue parseValue(const Common::String &command, uint &pos); void runCommand(const Common::String &command); bool checkCondition(const Common::String &condition); @@ -689,6 +688,7 @@ public: LBMode getCurMode() { return _curMode; } bool tryLoadPageStart(LBMode mode, uint page); + bool loadPage(LBMode mode, uint page, uint subpage); void prevPage(); void nextPage(); @@ -717,7 +717,6 @@ private: Common::Queue<DelayedEvent> _eventQueue; LBItem *_focus; void destroyPage(); - bool loadPage(LBMode mode, uint page, uint subpage); void updatePage(); uint16 _lastSoundOwner, _lastSoundId; diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp index e72318d86a..e9ef2516e2 100644 --- a/engines/mohawk/livingbooks_code.cpp +++ b/engines/mohawk/livingbooks_code.cpp @@ -127,6 +127,12 @@ Common::Rect LBValue::toRect() const { } LBCode::LBCode(MohawkEngine_LivingBooks *vm, uint16 baseId) : _vm(vm) { + if (!baseId) { + _data = NULL; + _size = 0; + return; + } + Common::SeekableSubReadStreamEndian *bcodStream = _vm->wrapStreamEndian(ID_BCOD, baseId); uint32 totalSize = bcodStream->readUint32(); @@ -172,12 +178,8 @@ LBValue LBCode::runCode(LBItem *src, uint32 offset) { } void LBCode::nextToken() { - if (_currOffset + 1 >= _size) { - // TODO - warning("went off the end of code"); - _currToken = kTokenEndOfFile; - _currValue = LBValue(); - return; + if (_currOffset >= _size) { + error("went off the end of code"); } _currToken = _data[_currOffset++]; @@ -186,6 +188,8 @@ void LBCode::nextToken() { switch (_currToken) { case kTokenIdentifier: { + if (_currOffset + 2 > _size) + error("went off the end of code reading identifier"); uint16 offset = READ_BE_UINT16(_data + _currOffset); // TODO: check string exists _currValue = _strings[offset]; @@ -195,9 +199,13 @@ void LBCode::nextToken() { case kTokenLiteral: { + if (_currOffset + 1 > _size) + error("went off the end of code reading literal"); byte literalType = _data[_currOffset++]; switch (literalType) { case kLBCodeLiteralInteger: + if (_currOffset + 2 > _size) + error("went off the end of code reading literal integer"); _currValue = READ_BE_UINT16(_data + _currOffset); _currOffset += 2; break; @@ -211,6 +219,8 @@ void LBCode::nextToken() { case kTokenConstEventId: case 0x5e: // TODO: ?? case kTokenKeycode: + if (_currOffset + 2 > _size) + error("went off the end of code reading immediate"); _currValue = READ_BE_UINT16(_data + _currOffset); _currOffset += 2; break; @@ -227,6 +237,8 @@ void LBCode::nextToken() { case kTokenString: { + if (_currOffset + 2 > _size) + error("went off the end of code reading string"); uint16 offset = READ_BE_UINT16(_data + _currOffset); // TODO: check string exists _currValue = _strings[offset]; @@ -265,27 +277,27 @@ LBValue LBCode::runCode(byte terminator) { void LBCode::parseStatement() { parseComparisons(); - if (_currToken != kTokenAnd && _currToken != kTokenOr) - return; - byte op = _currToken; - if (op == kTokenAnd) - debugN(" && "); - else - debugN(" || "); + while (_currToken == kTokenAnd || _currToken == kTokenOr) { + byte op = _currToken; + if (op == kTokenAnd) + debugN(" && "); + else + debugN(" || "); - nextToken(); - parseComparisons(); + nextToken(); + parseComparisons(); - LBValue val2 = _stack.pop(); - LBValue val1 = _stack.pop(); - bool result; - if (op == kTokenAnd) - result = !val1.isZero() && !val2.isZero(); - else - result = !val1.isZero() || !val2.isZero(); + LBValue val2 = _stack.pop(); + LBValue val1 = _stack.pop(); + bool result; + if (op == kTokenAnd) + result = !val1.isZero() && !val2.isZero(); + else + result = !val1.isZero() || !val2.isZero(); - debugN(" [--> %s]", result ? "true" : "false"); - _stack.push(result); + debugN(" [--> %s]", result ? "true" : "false"); + _stack.push(result); + } } void LBCode::parseComparisons() { @@ -353,49 +365,95 @@ void LBCode::parseComparisons() { void LBCode::parseConcat() { parseArithmetic1(); - if (_currToken != kTokenConcat) - return; - - debugN(" & "); - nextToken(); - parseArithmetic1(); + while (_currToken == kTokenConcat) { + debugN(" & "); + nextToken(); + parseArithmetic1(); - LBValue val2 = _stack.pop(); - LBValue val1 = _stack.pop(); - Common::String result = val1.toString() + val2.toString(); - debugN(" [--> \"%s\"]", result.c_str()); - _stack.push(result); + LBValue val2 = _stack.pop(); + LBValue val1 = _stack.pop(); + Common::String result = val1.toString() + val2.toString(); + debugN(" [--> \"%s\"]", result.c_str()); + _stack.push(result); + } } void LBCode::parseArithmetic1() { parseArithmetic2(); - if (_currToken != kTokenMinus && _currToken != kTokenPlus) - return; - - byte op = _currToken; - if (op == kTokenMinus) - debugN(" - "); - else if (op == kTokenPlus) - debugN(" + "); + while (_currToken == kTokenMinus || _currToken == kTokenPlus) { + byte op = _currToken; + if (op == kTokenMinus) + debugN(" - "); + else if (op == kTokenPlus) + debugN(" + "); - nextToken(); - parseArithmetic2(); - - LBValue val2 = _stack.pop(); - LBValue val1 = _stack.pop(); - LBValue result; - // TODO: cope with non-integers - if (op == kTokenMinus) - result = val1.toInt() - val2.toInt(); - else - result = val1.toInt() + val2.toInt(); - _stack.push(result); + nextToken(); + parseArithmetic2(); + + LBValue val2 = _stack.pop(); + LBValue val1 = _stack.pop(); + LBValue result; + // TODO: cope with non-integers + if (op == kTokenMinus) + result = val1.toInt() - val2.toInt(); + else + result = val1.toInt() + val2.toInt(); + debugN(" [--> %d]", result.toInt()); + _stack.push(result); + } } void LBCode::parseArithmetic2() { - // FIXME: other math operators parseMain(); + + while (true) { + byte op = _currToken; + switch (op) { + case kTokenMultiply: + debugN(" * "); + break; + case kTokenDivide: + debugN(" / "); + break; + case kTokenIntDivide: + debugN(" div "); + break; + case kTokenModulo: + debugN(" %% "); + break; + default: + return; + } + + nextToken(); + parseMain(); + + LBValue val2 = _stack.pop(); + LBValue val1 = _stack.pop(); + LBValue result; + // TODO: cope with non-integers + if (op == kTokenMultiply) { + result = val1.toInt() * val2.toInt(); + } else if (val2.toInt() == 0) { + result = 1; + } else { + switch (op) { + case kTokenDivide: + // TODO: fp divide + result = val1.toInt() / val2.toInt(); + break; + case kTokenIntDivide: + result = val1.toInt() / val2.toInt(); + break; + case kTokenModulo: + result = val1.toInt() % val2.toInt(); + break; + } + } + + _stack.push(result); + } } void LBCode::parseMain() { @@ -549,6 +607,16 @@ void LBCode::parseMain() { } } +LBItem *LBCode::resolveItem(const LBValue &value) { + if (value.type == kLBValueItemPtr) + return value.item; + if (value.type == kLBValueString) + return _vm->getItemByName(value.string); + if (value.type == kLBValueInteger) + return _vm->getItemById(value.integer); + return NULL; +} + Common::Array<LBValue> LBCode::readParams() { Common::Array<LBValue> params; @@ -616,8 +684,8 @@ struct CodeCommandInfo { #define NUM_GENERAL_COMMANDS 129 CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = { - { "eval", 0 }, - { "random", 0 }, + { "eval", &LBCode::cmdEval }, + { "random", &LBCode::cmdRandom }, { "stringLen", 0 }, { "substring", 0 }, { "max", 0 }, @@ -773,6 +841,26 @@ void LBCode::cmdUnimplemented(const Common::Array<LBValue> ¶ms) { warning("unimplemented command called"); } +void LBCode::cmdEval(const Common::Array<LBValue> ¶ms) { + // FIXME: v4 eval is different? + if (params.size() != 1) + error("incorrect number of parameters (%d) to eval", params.size()); + + LBCode tempCode(_vm, 0); + + uint offset = tempCode.parseCode(params[0].toString()); + _stack.push(tempCode.runCode(_currSource, offset)); +} + +void LBCode::cmdRandom(const Common::Array<LBValue> ¶ms) { + if (params.size() != 2) + error("incorrect number of parameters (%d) to random", params.size()); + + int min = params[0].toInt(); + int max = params[1].toInt(); + _stack.push(_vm->_rnd->getRandomNumberRng(min, max)); +} + void LBCode::cmdGetRect(const Common::Array<LBValue> ¶ms) { if (params.size() < 2) { _stack.push(getRectFromParams(params)); @@ -915,7 +1003,7 @@ CodeCommandInfo itemCommandInfo[NUM_ITEM_COMMANDS] = { { "moveTo", &LBCode::itemMoveTo }, { "mute", 0 }, { "play", 0 }, - { "seek", 0 }, + { "seek", &LBCode::itemSeek }, { "seekToFrame", 0 }, { "setParent", &LBCode::itemSetParent }, { "setZOrder", 0 }, @@ -951,6 +1039,17 @@ void LBCode::itemMoveTo(const Common::Array<LBValue> ¶ms) { warning("ignoring moveTo"); } +void LBCode::itemSeek(const Common::Array<LBValue> ¶ms) { + if (params.size() != 2) + error("incorrect number of parameters (%d) to seek", params.size()); + + LBItem *item = resolveItem(params[0]); + if (!item) + error("attempted seek on invalid item (%s)", params[0].toString().c_str()); + uint seekTo = params[1].toInt(); + item->seek(seekTo); +} + void LBCode::itemSetParent(const Common::Array<LBValue> ¶ms) { if (params.size() > 2) error("incorrect number of parameters (%d) to setParent", params.size()); @@ -1035,4 +1134,278 @@ void LBCode::runNotifyCommand() { } } +/* + * Helper function for parseCode/parseCodeSymbol: + * Returns an unused string id. + */ +uint LBCode::nextFreeString() { + for (uint i = 0; i <= 0xffff; i++) { + if (!_strings.contains(i)) + return i; + } + + error("nextFreeString couldn't find a space"); +} + +/* + * Helper function for parseCode: + * Given a name, appends the appropriate data to the provided code array and + * returns true if it's a function, or false otherwise. + */ +bool LBCode::parseCodeSymbol(const Common::String &name, uint &pos, Common::Array<byte> &code) { + // first, check whether the name matches a known function + for (uint i = 0; i < 2; i++) { + byte cmdToken; + CodeCommandInfo *cmdInfo; + uint cmdCount; + + switch (i) { + case 0: + cmdInfo = generalCommandInfo; + cmdToken = kTokenGeneralCommand; + cmdCount = NUM_GENERAL_COMMANDS; + break; + case 1: + cmdInfo = itemCommandInfo; + cmdToken = kTokenItemCommand; + cmdCount = NUM_ITEM_COMMANDS; + break; + } + + for (uint n = 0; n < cmdCount; n++) { + const char *cmdName = cmdInfo[n].name; + if (!cmdName) + continue; + if (!name.equalsIgnoreCase(cmdName)) + continue; + + // found a matching function + code.push_back(cmdToken); + code.push_back(n + 1); + return true; + } + } + + // not a function, so must be an identifier + code.push_back(kTokenIdentifier); + + uint stringId = nextFreeString(); + _strings[stringId] = name; + + char tmp[2]; + WRITE_BE_UINT16(tmp, (int16)stringId); + code.push_back(tmp[0]); + code.push_back(tmp[1]); + + return false; +} + +/* + * Parse a string for later execution, and return the offset where it was + * stored. + */ +uint LBCode::parseCode(const Common::String &source) { + struct LBCodeOperator { + byte token; + byte op; + byte lookahead1; + byte lookahead1Op; + byte lookahead2; + byte lookahead2Op; + }; + + #define NUM_LB_OPERATORS 11 + static const LBCodeOperator operators[NUM_LB_OPERATORS] = { + { '+', kTokenPlus, '+', kTokenPlusPlus, '=', kTokenPlusEquals }, + { '-', kTokenMinus, '-', kTokenMinusMinus, '=', kTokenMinusEquals }, + { '/', kTokenDivide, '=', kTokenDivideEquals, 0, 0 }, + { '*', kTokenMultiply, '=', kTokenMultiplyEquals, 0, 0 }, + { '=', kTokenAssign, '=', kTokenEquals, 0, 0 }, + { '>', kTokenGreaterThan, '=', kTokenGreaterThanEq, 0, 0 }, + { '<', kTokenLessThan, '=', kTokenLessThanEq, 0, 0 }, + { '!', kTokenNot, '=', kTokenNotEq, 0, 0 }, + { '&', kTokenConcat, '&', kTokenAnd, '=', kTokenAndEquals }, + { '|', 0, '|', kTokenOr, 0, 0 }, + { ';', kTokenEndOfStatement, 0, 0, 0, 0 } + }; + + uint pos = 0; + Common::Array<byte> code; + Common::Array<uint> counterPositions; + bool wasFunction = false; + + while (pos < source.size()) { + byte token = source[pos]; + byte lookahead = 0; + if (pos + 1 < source.size()) + lookahead = source[pos + 1]; + pos++; + + if (token != ' ' && token != '(' && wasFunction) + error("while parsing script '%s', encountered incomplete function call", source.c_str()); + + // First, we check for simple operators. + for (uint i = 0; i < NUM_LB_OPERATORS; i++) { + if (token != operators[i].token) + continue; + if (lookahead) { + if (lookahead == operators[i].lookahead1) { + code.push_back(operators[i].lookahead1Op); + token = 0; + } else if (lookahead == operators[i].lookahead2) { + code.push_back(operators[i].lookahead2Op); + token = 0; + } + if (!token) { + pos++; + break; + } + } + if (operators[i].op) { + code.push_back(operators[i].op); + token = 0; + } + break; + } + if (!token) + continue; + + // Then, we check for more complex tokens. + switch (token) { + // whitespace + case ' ': + // ignore + break; + // literal string + case '"': + case '\'': + { + Common::String tempString; + while (pos < source.size()) { + if (source[pos] == token) + break; + tempString += source[pos++]; + } + if (pos++ == source.size()) + error("while parsing script '%s', string had no end", source.c_str()); + + code.push_back(kTokenString); + + uint stringId = nextFreeString(); + _strings[stringId] = tempString; + + char tmp[2]; + WRITE_BE_UINT16(tmp, (int16)stringId); + code.push_back(tmp[0]); + code.push_back(tmp[1]); + } + break; + // open bracket + case '(': + if (wasFunction) { + // function call parameters + wasFunction = false; + // we will need to back-patch the parameter count, + // if parameters are encountered + counterPositions.push_back(code.size()); + code.push_back(1); + // if the next token is a ) then there are no + // parameters, otherwise start with 1 and increment + // if/when we encounter commas + for (uint i = pos; i < source.size(); i++) { + if (source[i] == ' ') + continue; + if (source[i] != ')') + break; + code[code.size() - 1] = 0; + break; + } + } else { + // brackets around expression + counterPositions.push_back(0); + } + code.push_back(kTokenOpenBracket); + break; + // close bracket + case ')': + if (counterPositions.empty()) + error("while parsing script '%s', encountered unmatched )", source.c_str()); + counterPositions.pop_back(); + code.push_back(kTokenCloseBracket); + break; + // comma (seperating function params) + case ',': + { + if (counterPositions.empty()) + error("while parsing script '%s', encountered unexpected ,", source.c_str()); + code.push_back(kTokenComma); + uint counterPos = counterPositions.back(); + if (!counterPos) + error("while parsing script '%s', encountered , outside parameter list", source.c_str()); + code[counterPos]++; + } + break; + // old-style explicit function call + case '@': + { + Common::String tempString; + while (pos < source.size()) { + if (!isalpha(source[pos]) && !isdigit(source[pos])) + break; + tempString += source[pos++]; + } + wasFunction = parseCodeSymbol(tempString, pos, code); + if (!wasFunction) + error("while parsing script '%s', encountered explicit function call to unknown function '%s'", + source.c_str(), tempString.c_str()); + } + break; + default: + if (isdigit(token)) { + const char *in = source.c_str() + pos - 1; + // FIXME: handle floats? + char *endptr; + long int intValue = strtol(in, &endptr, 0); + assert(endptr > in); + pos += (endptr - in) - 1; + + // FIXME: handle storing longs if needed + code.push_back(kTokenLiteral); + code.push_back(kLBCodeLiteralInteger); + char tmp[2]; + WRITE_BE_UINT16(tmp, (int16)intValue); + code.push_back(tmp[0]); + code.push_back(tmp[1]); + } else if (isalpha(token)) { + Common::String tempString; + tempString += token; + while (pos < source.size()) { + if (!isalpha(source[pos]) && !isdigit(source[pos])) + break; + tempString += source[pos++]; + } + wasFunction = parseCodeSymbol(tempString, pos, code); + } else { + error("while parsing script '%s', couldn't parse '%c'", source.c_str(), token); + } + } + } + + if (wasFunction) + error("while parsing script '%s', encountered incomplete function call", source.c_str()); + if (counterPositions.size()) + error("while parsing script '%s', unmatched (", source.c_str()); + + code.push_back(kTokenEndOfFile); + + uint codeOffset = _size; + byte *newData = new byte[_size + code.size()]; + memcpy(newData, _data, _size); + memcpy(newData, &code[0], code.size()); + delete[] _data; + _data = newData; + _size += code.size(); + return codeOffset; +} + } // End of namespace Mohawk diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h index 9602e2d22d..9c58ed7a46 100644 --- a/engines/mohawk/livingbooks_code.h +++ b/engines/mohawk/livingbooks_code.h @@ -181,6 +181,7 @@ public: ~LBCode(); LBValue runCode(LBItem *src, uint32 offset); + uint parseCode(const Common::String &source); protected: MohawkEngine_LivingBooks *_vm; @@ -206,6 +207,7 @@ protected: void parseArithmetic2(); void parseMain(); + LBItem *resolveItem(const LBValue &value); Common::Array<LBValue> readParams(); Common::Rect getRectFromParams(const Common::Array<LBValue> ¶ms); @@ -213,8 +215,13 @@ protected: void runItemCommand(); void runNotifyCommand(); + uint nextFreeString(); + bool parseCodeSymbol(const Common::String &name, uint &pos, Common::Array<byte> &code); + public: void cmdUnimplemented(const Common::Array<LBValue> ¶ms); + void cmdEval(const Common::Array<LBValue> ¶ms); + void cmdRandom(const Common::Array<LBValue> ¶ms); void cmdGetRect(const Common::Array<LBValue> ¶ms); void cmdTopLeft(const Common::Array<LBValue> ¶ms); void cmdBottomRight(const Common::Array<LBValue> ¶ms); @@ -228,9 +235,10 @@ public: void cmdSetHitTest(const Common::Array<LBValue> ¶ms); void cmdKey(const Common::Array<LBValue> ¶ms); - void itemSetParent(const Common::Array<LBValue> ¶ms); - void itemMoveTo(const Common::Array<LBValue> ¶ms); void itemIsPlaying(const Common::Array<LBValue> ¶ms); + void itemMoveTo(const Common::Array<LBValue> ¶ms); + void itemSeek(const Common::Array<LBValue> ¶ms); + void itemSetParent(const Common::Array<LBValue> ¶ms); }; } // End of namespace Mohawk diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index 17f6de534f..307be2dd05 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -560,6 +560,7 @@ void MystScriptParser::o_playSoundBlocking(uint16 op, uint16 var, uint16 argc, u debugC(kDebugScript, "Opcode %d: playSoundBlocking", op); debugC(kDebugScript, "\tsoundId: %d", soundId); + _vm->_sound->stopSound(); _vm->_sound->playSoundBlocking(soundId); } diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index d6dd1b5407..12d9dc7e2f 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -102,6 +102,7 @@ void Mechanical::setupOpcodes() { void Mechanical::disablePersistentScripts() { _fortressSimulationRunning = false; + _elevatorRotationLeverMoving = false; _elevatorGoingMiddle = false; _birdSinging = false; _fortressRotationRunning = false; @@ -126,10 +127,10 @@ void Mechanical::runPersistentScripts() { uint16 Mechanical::getVar(uint16 var) { switch(var) { - case 0: // Sirrus's Secret Panel State - return _state.sirrusPanelState; - case 1: // Achenar's Secret Panel State + case 0: // Achenar's Secret Panel State return _state.achenarPanelState; + case 1: // Sirrus's Secret Panel State + return _state.sirrusPanelState; case 2: // Achenar's Secret Room Crate Lid Open and Blue Page Present if (_state.achenarCrateOpened) { if (_globals.bluePagesInBook & 4 || _globals.heldPage == 3) @@ -195,16 +196,21 @@ uint16 Mechanical::getVar(uint16 var) { void Mechanical::toggleVar(uint16 var) { switch(var) { - case 0: // Sirrus's Secret Panel State - _state.sirrusPanelState ^= 1; - case 1: // Achenar's Secret Panel State + case 0: // Achenar's Secret Panel State _state.achenarPanelState ^= 1; + break; + case 1: // Sirrus's Secret Panel State + _state.sirrusPanelState ^= 1; + break; case 3: // Achenar's Secret Room Crate State _state.achenarCrateOpened ^= 1; + break; case 4: // Myst Book Room Staircase State _mystStaircaseState ^= 1; + break; case 10: // Fortress Staircase State _state.staircaseState ^= 1; + break; case 16: // Code Lock Shape #1 - Left case 17: // Code Lock Shape #2 case 18: // Code Lock Shape #3 @@ -242,6 +248,7 @@ bool Mechanical::setVarValue(uint16 var, uint16 value) { switch (var) { case 13: _elevatorPosition = value; + break; case 14: // Elevator going down when at top _elevatorGoingDown = value; break; @@ -724,6 +731,7 @@ void Mechanical::birdSing_run() { uint32 time = _vm->_system->getMillis(); if (_birdSingEndTime < time) { _bird->pauseMovie(true); + _vm->_sound->stopSound(); _birdSinging = false; } } diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp index 9b39692958..f01375bacf 100644 --- a/engines/mohawk/resource.cpp +++ b/engines/mohawk/resource.cpp @@ -294,7 +294,7 @@ bool MohawkArchive::openStream(Common::SeekableReadStream *stream) { // We need to do this because of the way Mohawk is set up (this is much more "proper" // than passing _stream at the right offset). We may want to do that in the future, though. if (tag == ID_TMOV) { - if (index == fileTable.size() - 1) + if (index == fileTable.size()) res.size = stream->size() - fileTable[index - 1].offset; else res.size = fileTable[index].offset - fileTable[index - 1].offset; @@ -304,7 +304,6 @@ bool MohawkArchive::openStream(Common::SeekableReadStream *stream) { debug(4, "Entry[%02x]: ID = %04x (%d) Index = %04x", j, id, id, index); } - // Return to next TypeTable entry stream->seek(absOffset + (i + 1) * 8 + 4); diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp index 6144c89e21..791b18db49 100644 --- a/engines/mohawk/sound.cpp +++ b/engines/mohawk/sound.cpp @@ -141,6 +141,19 @@ Audio::SoundHandle *Sound::replaceSoundMyst(uint16 id, byte volume, bool loop) { && name.equals(_vm->getResourceName(ID_MSND, convertMystID(_handles[i].id)))) return &_handles[i].handle; + // The original engine also forces looping for those sounds + switch (id) { + case 2205: + case 2207: + case 5378: + case 7220: + case 9119: // Elevator engine sound in mechanical age is looping. + case 9120: + case 9327: + loop = true; + break; + } + stopSound(); return playSound(id, volume, loop); } |
