aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk
diff options
context:
space:
mode:
authorAlyssa Milburn2011-07-07 09:24:10 +0200
committerAlyssa Milburn2011-07-07 09:24:10 +0200
commit66e81d633f987310f12038147ae01b8ce4f640f7 (patch)
tree6035a7124789c3e6cdff5f603e9933529b7efdc7 /engines/mohawk
parentaffaa1f4d6cf5f27f654029133b1aec7b9eca4b5 (diff)
parent72da8ef5adf82d8a65da299207f30af5058ca8a9 (diff)
downloadscummvm-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.cpp21
-rw-r--r--engines/mohawk/cursors.cpp11
-rw-r--r--engines/mohawk/cursors.h2
-rw-r--r--engines/mohawk/detection_tables.h15
-rw-r--r--engines/mohawk/livingbooks.cpp261
-rw-r--r--engines/mohawk/livingbooks.h3
-rw-r--r--engines/mohawk/livingbooks_code.cpp491
-rw-r--r--engines/mohawk/livingbooks_code.h12
-rw-r--r--engines/mohawk/myst_scripts.cpp1
-rw-r--r--engines/mohawk/myst_stacks/mechanical.cpp20
-rw-r--r--engines/mohawk/resource.cpp3
-rw-r--r--engines/mohawk/sound.cpp13
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> &params) {
warning("unimplemented command called");
}
+void LBCode::cmdEval(const Common::Array<LBValue> &params) {
+ // 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> &params) {
+ 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> &params) {
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> &params) {
warning("ignoring moveTo");
}
+void LBCode::itemSeek(const Common::Array<LBValue> &params) {
+ 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> &params) {
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> &params);
@@ -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> &params);
+ void cmdEval(const Common::Array<LBValue> &params);
+ void cmdRandom(const Common::Array<LBValue> &params);
void cmdGetRect(const Common::Array<LBValue> &params);
void cmdTopLeft(const Common::Array<LBValue> &params);
void cmdBottomRight(const Common::Array<LBValue> &params);
@@ -228,9 +235,10 @@ public:
void cmdSetHitTest(const Common::Array<LBValue> &params);
void cmdKey(const Common::Array<LBValue> &params);
- void itemSetParent(const Common::Array<LBValue> &params);
- void itemMoveTo(const Common::Array<LBValue> &params);
void itemIsPlaying(const Common::Array<LBValue> &params);
+ void itemMoveTo(const Common::Array<LBValue> &params);
+ void itemSeek(const Common::Array<LBValue> &params);
+ void itemSetParent(const Common::Array<LBValue> &params);
};
} // 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);
}