diff options
Diffstat (limited to 'engines/macventure/script.cpp')
-rw-r--r-- | engines/macventure/script.cpp | 1266 |
1 files changed, 1266 insertions, 0 deletions
diff --git a/engines/macventure/script.cpp b/engines/macventure/script.cpp new file mode 100644 index 0000000000..77bbea1866 --- /dev/null +++ b/engines/macventure/script.cpp @@ -0,0 +1,1266 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * Based on + * WebVenture (c) 2010, Sean Kasun + * https://github.com/mrkite/webventure, http://seancode.com/webventure/ + * + * Used with explicit permission from the author + */ + +#include "common/system.h" + +#include "macventure/macventure.h" +#include "macventure/script.h" +#include "macventure/world.h" +#include "macventure/container.h" + +namespace MacVenture { + +ScriptEngine::ScriptEngine(MacVentureEngine *engine, World *world) { + _engine = engine; + _world = world; + _scripts = new Container(_engine->getFilePath(kFilterPathID)); +} + +ScriptEngine::~ScriptEngine() { + if (_scripts) + delete _scripts; +} + +bool ScriptEngine::runControl(ControlAction action, ObjID source, ObjID destination, Common::Point delta) { + EngineFrame frame; + frame.action = action; + frame.src = source; + frame.dest = destination; + frame.x = delta.x; + frame.y = delta.y; + frame.haltedInSaves = false; + frame.haltedInFirst = false; + frame.haltedInFamily = false; + _frames.push_back(frame); + debugC(3, kMVDebugScript, "Stored frame %d, action: %d src: %d dest: %d point: (%d, %d)", + _frames.size() - 1, frame.action, frame.src, frame.dest, frame.x, frame.y); + + return resume(true); +} + +bool ScriptEngine::resume(bool execAll) { + debugC(3, kMVDebugScript, "Resume Script"); + while (_frames.size()) { + bool fail = execFrame(execAll); + if (fail) { + return true; + } + } + return false; +} + +void ScriptEngine::reset() { + _frames.clear(); +} + +bool ScriptEngine::execFrame(bool execAll) { + bool doFirst = execAll; + bool doFamily = false; + bool fail; + + EngineFrame *frame = &_frames.front(); + + // Do first dispatch script (script 0) + if (frame->haltedInFirst || doFirst) { // We were stuck or it's the first time + frame->haltedInFirst = false; + if (doFirst) { + fail = loadScript(frame, 0); + } else { + fail = resumeFunc(frame); + } + if (fail) { + frame->haltedInFirst = true; + _engine->preparedToRun(); + return true; + } + doFamily = true; + frame->familyIdx = 0; + } + + // Do scripts in the family of player (ObjID 1) + if (frame->haltedInFamily || doFamily) { // We have to do the family or we were stuck here + frame->haltedInFamily = false; + Common::Array<ObjID> family = _world->getFamily(_world->getObjAttr(1, kAttrParentObject), false); + for (uint32 i = frame->familyIdx; i < family.size(); i++) { + if (doFamily) { + fail = loadScript(frame, family[i]); + } else { + fail = resumeFunc(frame); + } + if (fail) { // We are stuck, so we don't shift the frame + frame->haltedInFamily = true; + frame->familyIdx = i; + _engine->preparedToRun(); + return true; + } + doFamily = true; + } + } + + // Halted in saves + if (frame->haltedInSaves) { + frame->haltedInSaves = false; + if (resumeFunc(frame)) { + frame->haltedInSaves = true; + _engine->preparedToRun(); + return true; + } + } + + int highest = 0; + uint localHigh = 0; + do { // Saved function calls + highest = 0; + for (uint i = 0; i < frame->saves.size(); i++) { + if (highest < frame->saves[i].rank) { + highest = frame->saves[i].rank; + localHigh = i; + } + } + if (highest) { + frame->saves[localHigh].rank = 0; + if (loadScript(frame, frame->saves[localHigh].func)) { + frame->haltedInSaves = true; + _engine->preparedToRun(); + return true; + } + } + } while (highest); + + _frames.pop_front(); + return false; +} + +bool ScriptEngine::loadScript(EngineFrame *frame, uint32 scriptID) { + if (_scripts->getItemByteSize(scriptID) > 0) { + debugC(2, kMVDebugScript, "Loading function %d", scriptID); + // Insert the new script at the front + frame->scripts.push_front(ScriptAsset(scriptID, _scripts)); + return runFunc(frame); + } + return false; +} + +bool ScriptEngine::resumeFunc(EngineFrame *frame) { + bool fail = runFunc(frame); + if (fail) { + return fail; + } + frame->scripts.pop_front(); + if (frame->scripts.size()) + return resumeFunc(frame); + return false; +} + +bool ScriptEngine::runFunc(EngineFrame *frame) { + ScriptAsset &script = frame->scripts.front(); + EngineState *state = &frame->state; + byte op; + while (script.hasNext()) { + op = script.fetch(); + debugC(4, kMVDebugScript, "Running operation %d", op); + if (!(op & 0x80)) { + state->push(op); + } else { + switch (op) { + case 0x80: //get attribute + op80GATT(state, frame); + break; + case 0x81: //set attribute + op81SATT(state, frame); + break; + case 0x82: //sum children attribute + op82SUCH(state, frame); + break; + case 0x83: //push selected control + op83PUCT(state, frame); + break; + case 0x84: //push selected object + op84PUOB(state, frame); + break; + case 0x85: //push target + op85PUTA(state, frame); + break; + case 0x86: //push deltax + op86PUDX(state, frame); + break; + case 0x87: //push deltay + op87PUDY(state, frame); + break; + case 0x88: //push immediate.b + op88PUIB(state, frame, &script); + break; + case 0x89: //push immediate + op89PUI(state, frame, &script); + break; + case 0x8a: //get global + op8aGGLO(state, frame); + break; + case 0x8b: //set global + op8bSGLO(state, frame); + break; + case 0x8c: //random + op8cRAND(state, frame); + break; + case 0x8d: //copy + op8dCOPY(state, frame); + break; + case 0x8e: //copyn + op8eCOPYN(state, frame); + break; + case 0x8f: //swap + op8fSWAP(state, frame); + break; + case 0x90: //swapn + op90SWAPN(state, frame); + break; + case 0x91: //pop + op91POP(state, frame); + break; + case 0x92: //copy+1 + op92COPYP(state, frame); + break; + case 0x93: //copy+n + op93COPYPN(state, frame); + break; + case 0x94: //shuffle + op94SHUFF(state, frame); + break; + case 0x95: //sort + op95SORT(state, frame); + break; + case 0x96: //clear stack + op96CLEAR(state, frame); + break; + case 0x97: //get stack size + op97SIZE(state, frame); + break; + case 0x98: //add + op98ADD(state, frame); + break; + case 0x99: //subtract + op99SUB(state, frame); + break; + case 0x9a: //multiply + op9aMUL(state, frame); + break; + case 0x9b: //divide + op9bDIV(state, frame); + break; + case 0x9c: //mod + op9cMOD(state, frame); + break; + case 0x9d: //divmod + op9dDMOD(state, frame); + break; + case 0x9e: //abs + op9eABS(state, frame); + break; + case 0x9f: //neg + op9fNEG(state, frame); + break; + case 0xa0: //and + opa0AND(state, frame); + break; + case 0xa1: //or + opa1OR(state, frame); + break; + case 0xa2: //xor + opa2XOR(state, frame); + break; + case 0xa3: //not + opa3NOT(state, frame); + break; + case 0xa4: //logical and + opa4LAND(state, frame); + break; + case 0xa5: //logical or + opa5LOR(state, frame); + break; + case 0xa6: //logical xor + opa6LXOR(state, frame); + break; + case 0xa7: //logical not + opa7LNOT(state, frame); + break; + case 0xa8: //gt? unsigned + opa8GTU(state, frame); + break; + case 0xa9: //lt? unsigned + opa9LTU(state, frame); + break; + case 0xaa: //gt? signed + opaaGTS(state, frame); + break; + case 0xab: //lt? signed + opabLTS(state, frame); + break; + case 0xac: //eq? + opacEQ(state, frame); + break; + case 0xad: //eq string? + opadEQS(state, frame); + break; + case 0xae: //contains + opaeCONT(state, frame); + break; + case 0xaf: //contains word + opafCONTW(state, frame); + break; + case 0xb0: //bra + opb0BRA(state, frame, &script); + break; + case 0xb1: //bra.b + opb1BRAB(state, frame, &script); + break; + case 0xb2: //beq + opb2BEQ(state, frame, &script); + break; + case 0xb3: //beq.b + opb3BEQB(state, frame, &script); + break; + case 0xb4: //bne + opb4BNE(state, frame, &script); + break; + case 0xb5: //bne.b + opb5BNEB(state, frame, &script); + break; + case 0xb6: //call later + opb6CLAT(state, frame); + break; + case 0xb7: //cancel call + opb7CCA(state, frame); + break; + case 0xb8: //cancel low priority + opb8CLOW(state, frame); + break; + case 0xb9: //cancel high priority + opb9CHI(state, frame); + break; + case 0xba: //cancel priority range + opbaCRAN(state, frame); + break; + case 0xbb: //fork + if (opbbFORK(state, frame)) + return true; + break; + case 0xbc: //call + if (opbcCALL(state, frame, script)) + return true; + break; + case 0xbd: //focus object + opbdFOOB(state, frame); + break; + case 0xbe: //swap objects + opbeSWOB(state, frame); + break; + case 0xbf: //snap object + opbfSNOB(state, frame); + break; + case 0xc0: //toggle exits + opc0TEXI(state, frame); + break; + case 0xc1: //print text + opc1PTXT(state, frame); + break; + case 0xc2: //print newline + opc2PNEW(state, frame); + break; + case 0xc3: //print text+nl + opc3PTNE(state, frame); + break; + case 0xc4: //print nl+text+nl + opc4PNTN(state, frame); + break; + case 0xc5: //print number + opc5PNUM(state, frame); + break; + case 0xc6: //push 2 + opc6P2(state, frame); + break; + case 0xc7: //play sound in background + opc7PLBG(state, frame); + break; + case 0xc8: //play sound and wait + opc8PLAW(state, frame); + break; + case 0xc9: //wait for sound to finish? + opc9WAIT(state, frame); + break; + case 0xca: //get current time + opcaTIME(state, frame); + break; + case 0xcb: //get current day + opcbDAY(state, frame); + break; + case 0xcc: //get children + opccCHLD(state, frame); + break; + case 0xcd: //get num children + opcdNCHLD(state, frame); + break; + case 0xce: //get engine version + opceVERS(state, frame); + break; + case 0xcf: //push scenario number + opcfPSCE(state, frame); + break; + case 0xd0: //push 1 + opd0P1(state, frame); + break; + case 0xd1: //get object dimensions + opd1GOBD(state, frame); + break; + case 0xd2: //get overlap percent + opd2GOVP(state, frame); + break; + case 0xd3: //capture children + opd3CAPC(state, frame); + break; + case 0xd4: //release children + opd4RELC(state, frame); + break; + case 0xd5: //show speech dialog + opd5DLOG(state, frame); + return true; + case 0xd6: //activate command + opd6ACMD(state, frame); + break; + case 0xd7: //lose game + opd7LOSE(state, frame); + break; + case 0xd8: //win game + opd8WIN(state, frame); + break; + case 0xd9: //sleep + opd9SLEEP(state, frame); + return true; + case 0xda: //click to continue + opdaCLICK(state, frame); + return true; + case 0xdb: //run queue + opdbROBQ(state, frame); + break; + case 0xdc: //run sound queue + opdcRSQ(state, frame); + break; + case 0xdd: //run text queue + opddRTQ(state, frame); + break; + case 0xde: //update screen + opdeUPSC(state, frame); + break; + case 0xdf: //flash main window + opdfFMAI(state, frame); + return true; + case 0xe0: //cache graphic and object + ope0CHGR(state, frame); + break; + case 0xe1: //cache sound + ope1CHSO(state, frame); + break; + case 0xe2: //muldiv + ope2MDIV(state, frame); + break; + case 0xe3: //update object + ope3UPOB(state, frame); + break; + case 0xe4: //currently playing event? + ope4PLEV(state, frame); + break; + case 0xe5: //wait for event to finish + ope5WEV(state, frame); + break; + case 0xe6: //get fibonacci (joke) + ope6GFIB(state, frame); + break; + case 0xe7: //calc fibonacci + ope7CFIB(state, frame); + break; + default: + op00NOOP(op); + } + } + } + return false; +} + +int16 ScriptEngine::neg16(int16 val) { + if (val & 0x8000) + val = -((val ^ 0xFFFF) + 1); + return val; +} + +int16 ScriptEngine::neg8(int16 val) { + if (val & 0x80) + val = -((val ^ 0xff) + 1); + return val; +} + +int16 ScriptEngine::sumChildrenAttr(int16 obj, int16 attr, bool recursive) { + int16 sum = 0; + Common::Array<ObjID> children = _world->getChildren(obj, recursive); + for (Common::Array<ObjID>::const_iterator it = children.begin(); it != children.end(); it++) { + sum += _world->getObjAttr(*it, attr); + } + return sum; +} + +void ScriptEngine::ensureNonzeroDivisor(int16 divisor, byte opcode) { + // TODO Untested, since that occassion rarely comes up. + if (divisor == 0) { + error("SCRIPT: Attempt to divide by 0 in operation %x", opcode); + } +} + +void MacVenture::ScriptEngine::op80GATT(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + int16 attr = state->pop(); + state->push(_world->getObjAttr(obj, attr)); +} + +void ScriptEngine::op81SATT(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + int16 attr = state->pop(); + int16 val = neg16(state->pop()); + _world->setObjAttr(obj, attr, val); +} + +void ScriptEngine::op82SUCH(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + int16 attr = state->pop(); + int16 recursive = neg16(state->pop()); + state->push(sumChildrenAttr(obj, attr, recursive)); +} + +void ScriptEngine::op83PUCT(EngineState *state, EngineFrame *frame) { + state->push(frame->action); +} + +void ScriptEngine::op84PUOB(EngineState *state, EngineFrame *frame) { + state->push(frame->src); +} + +void ScriptEngine::op85PUTA(EngineState *state, EngineFrame *frame) { + state->push(frame->dest); +} + +void ScriptEngine::op86PUDX(EngineState *state, EngineFrame *frame) { + state->push(frame->x); +} + +void ScriptEngine::op87PUDY(EngineState *state, EngineFrame *frame) { + state->push(frame->y); +} + +void ScriptEngine::op88PUIB(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + state->push(script->fetch()); +} + +void ScriptEngine::op89PUI(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val <<= 8; + val = val | script->fetch(); + state->push(val); +} + +void ScriptEngine::op8aGGLO(EngineState *state, EngineFrame *frame) { + int16 idx = state->pop(); + state->push(_world->getGlobal(idx)); +} + +void ScriptEngine::op8bSGLO(EngineState *state, EngineFrame *frame) { + int16 idx = state->pop(); + int16 val = neg16(state->pop()); + _world->setGlobal(idx, val); + _engine->gameChanged(); +} + +void ScriptEngine::op8cRAND(EngineState *state, EngineFrame *frame) { + int16 max = state->pop(); + state->push(_engine->randBetween(0, max)); +} + +void ScriptEngine::op8dCOPY(EngineState *state, EngineFrame *frame) { + int16 val = state->pop(); + state->push(val); + state->push(val); +} + +void ScriptEngine::op8eCOPYN(EngineState *state, EngineFrame *frame) { + int16 n = state->pop(); + int16 offs = n - 1; + int16 val; + while (n) { + val = state->peek(offs); + state->push(val); + n--; + } +} + +void ScriptEngine::op8fSWAP(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(b); + state->push(a); +} + +void ScriptEngine::op90SWAPN(EngineState *state, EngineFrame *frame) { + int16 idx = state->pop(); + int16 a = state->peek(idx); + int16 b = state->peek(0); + state->poke(idx, b); + state->poke(0, a); +} + +void ScriptEngine::op91POP(EngineState *state, EngineFrame *frame) { + state->pop(); +} + +void ScriptEngine::op92COPYP(EngineState *state, EngineFrame *frame) { + int16 val = state->peek(1); + state->push(val); +} + +void ScriptEngine::op93COPYPN(EngineState *state, EngineFrame *frame) { + int16 idx = state->pop(); + int16 val = state->peek(idx); + state->push(val); +} + +void ScriptEngine::op94SHUFF(EngineState *state, EngineFrame *frame) { + int16 a = state->pop(); + int16 b = state->pop(); + int16 c = state->pop(); + state->push(a); + state->push(c); + state->push(b); +} + +void ScriptEngine::op95SORT(EngineState *state, EngineFrame *frame) { + int16 step = neg16(state->pop()); + int16 num = neg16(state->pop()); + step %= num; + if (step < 0) { + step += num; + } + int16 end = 0; + int16 start = 0; + for (int16 i = 1; i < num; i++) { + start += step; + if (start >= num) { + start -= num; + } + if (start == end) { + end++; + start = end; + } else { + int16 a = state->peek(end); + int16 b = state->peek(start); + state->poke(end, b); + state->poke(start, a); + } + } +} + +void ScriptEngine::op96CLEAR(EngineState *state, EngineFrame *frame) { + state->clear(); +} + +void ScriptEngine::op97SIZE(EngineState *state, EngineFrame *frame) { + state->push(state->size()); +} + +void ScriptEngine::op98ADD(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a + b); +} + +void ScriptEngine::op99SUB(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a - b); +} + +void ScriptEngine::op9aMUL(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a * b); +} + +void ScriptEngine::op9bDIV(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + ensureNonzeroDivisor(b, 0x9b); + state->push(a / b); +} + +void ScriptEngine::op9cMOD(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a % b); +} + +void ScriptEngine::op9dDMOD(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + ensureNonzeroDivisor(b, 0x9d); + state->push(a % b); + state->push(a / b); +} + +void ScriptEngine::op9eABS(EngineState *state, EngineFrame *frame) { + int16 val = neg16(state->pop()); + if (val < 0) { + val = -val; + } + state->push(val); +} + +void ScriptEngine::op9fNEG(EngineState *state, EngineFrame *frame) { + int16 val = -neg16(state->pop()); + state->push(val); +} + +void ScriptEngine::opa0AND(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a & b); +} + +void ScriptEngine::opa1OR(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a | b); +} + +void ScriptEngine::opa2XOR(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(a ^ b); +} + +void ScriptEngine::opa3NOT(EngineState *state, EngineFrame *frame) { + int16 a = state->pop(); + state->push(a ^ 0xFFFF); +} + +void ScriptEngine::opa4LAND(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push((a && b) ? 0xFFFF : 0); +} + +void ScriptEngine::opa5LOR(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push((a || b) ? 0xFFFF : 0); +} + +void ScriptEngine::opa6LXOR(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push((!a != !b) ? 0xFFFF : 0); +} + +void ScriptEngine::opa7LNOT(EngineState *state, EngineFrame *frame) { + int16 a = state->pop(); + state->push((a == 0) ? 0xFFFF : 0); +} + +void ScriptEngine::opa8GTU(EngineState *state, EngineFrame *frame) { + uint16 b = state->pop(); + uint16 a = state->pop(); + state->push((a > b) ? 0xFFFF : 0); +} + +void ScriptEngine::opa9LTU(EngineState *state, EngineFrame *frame) { + uint16 b = state->pop(); + uint16 a = state->pop(); + state->push((a < b) ? 0xFFFF : 0); +} + +void ScriptEngine::opaaGTS(EngineState *state, EngineFrame *frame) { + // HACK !!! May not need the neg16, since it's already a signed int!! + int16 b = neg16(state->pop()); + int16 a = neg16(state->pop()); + state->push((a > b) ? 0xFFFF : 0); +} + +void ScriptEngine::opabLTS(EngineState *state, EngineFrame *frame) { + // HACK !!! May not need the neg16, since it's already a signed int!! + int16 b = neg16(state->pop()); + int16 a = neg16(state->pop()); + state->push((a < b) ? 0xFFFF : 0); +} + +void ScriptEngine::opacEQ(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push((a == b) ? 0xFFFF : 0); +} + +void ScriptEngine::opadEQS(EngineState *state, EngineFrame *frame) { + Common::String b = _world->getText(state->pop(), 0, 0); // HACK, these destinations might be wrong + Common::String a = _world->getText(state->pop(), 0, 0); + state->push((a == b) ? 1 : 0); +} + +void ScriptEngine::opaeCONT(EngineState *state, EngineFrame *frame) { + Common::String needle = _world->getText(state->pop(), 0, 0); + Common::String haystack = _world->getText(state->pop(), 0, 0); + haystack.toLowercase(); + state->push(haystack.contains(needle) ? 1 : 0); +} + +void ScriptEngine::opafCONTW(EngineState *state, EngineFrame *frame) { + Common::String needle = _world->getText(state->pop(), 0, 0); + Common::String haystack = _world->getText(state->pop(), 0, 0); + haystack.toLowercase(); + state->push(haystack.contains(needle) ? 1 : 0); +} + +void ScriptEngine::opb0BRA(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val <<= 8; + val = val | script->fetch(); + val = neg16(val); + script->branch(val); +} + +void ScriptEngine::opb1BRAB(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val = neg8(val); + script->branch(val); +} + +void ScriptEngine::opb2BEQ(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val <<= 8; + val = val | script->fetch(); + val = neg16(val); + int16 b = state->pop(); + if (b != 0) { + script->branch(val); + } +} + +void ScriptEngine::opb3BEQB(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val = neg8(val); + int16 b = state->pop(); + if (b != 0) { + script->branch(val); + } +} + +void ScriptEngine::opb4BNE(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val <<= 8; + val = val | script->fetch(); + val = neg16(val); + int16 b = state->pop(); + if (b == 0) { + script->branch(val); + } +} + +void ScriptEngine::opb5BNEB(EngineState *state, EngineFrame *frame, ScriptAsset *script) { + int16 val = script->fetch(); + val = neg8(val); + int16 b = state->pop(); + if (b == 0) { + script->branch(val); + } +} + +void ScriptEngine::opb6CLAT(EngineState *state, EngineFrame *frame) { + int16 rank = state->pop(); + int16 func = state->pop(); + frame->saves.push_back(FunCall(func, rank)); +} + +void ScriptEngine::opb7CCA(EngineState *state, EngineFrame *frame) { + int16 func = state->pop(); + for (uint i = 0; i < frame->saves.size(); i++) { + if (frame->saves[i].func == func) + frame->saves[i].rank = 0; + } +} + +void ScriptEngine::opb8CLOW(EngineState *state, EngineFrame *frame) { + int16 hi = state->pop(); + for (uint i = 0; i < frame->saves.size(); i++) + if (frame->saves[i].rank <= hi) + frame->saves[i].rank = 0; +} + +void ScriptEngine::opb9CHI(EngineState *state, EngineFrame *frame) { + int16 lo = state->pop(); + for (uint i = 0; i < frame->saves.size(); i++) { + if (frame->saves[i].rank >= lo) { + frame->saves[i].rank = 0; + } + } +} + +void ScriptEngine::opbaCRAN(EngineState *state, EngineFrame *frame) { + int16 hi = state->pop(); + int16 lo = state->pop(); + for (uint i = 0; i < frame->saves.size(); i++) { + if (frame->saves[i].rank >= lo && + frame->saves[i].rank <= hi) { + frame->saves[i].rank = 0; + } + } +} + +bool ScriptEngine::opbbFORK(EngineState *state, EngineFrame *frame) { + EngineFrame newframe; + newframe.action = (ControlAction)state->pop(); + newframe.src = state->pop(); + newframe.dest = state->pop(); + newframe.x = state->pop(); + newframe.y = state->pop(); + newframe.haltedInFamily = false; + newframe.haltedInFirst = false; + newframe.haltedInSaves = false; + _frames.push_front(newframe); + if (execFrame(true)) { + return true; + } + return false; +} + +bool ScriptEngine::opbcCALL(EngineState *state, EngineFrame *frame, ScriptAsset &script) { + int16 id = state->pop(); + ScriptAsset newfun = ScriptAsset(id, _scripts); + ScriptAsset current = script; + debugC(2, kMVDebugScript, "Call function: %d", id); + if (loadScript(frame, id)) + return true; + frame->scripts.pop_front(); + script = frame->scripts.front(); + debugC(2, kMVDebugScript, "Return from fuction %d", id); + return false; +} + +void ScriptEngine::opbdFOOB(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + _engine->enqueueObject(kFocusWindow, obj); +} + +void ScriptEngine::opbeSWOB(EngineState *state, EngineFrame *frame) { + ObjID from = state->pop(); + ObjID to = state->pop(); + _engine->enqueueObject(kUpdateWindow, from, to); + _world->setObjAttr(to, kAttrContainerOpen, _world->getObjAttr(from, 6)); + _world->setObjAttr(from, kAttrContainerOpen, 0); + Common::Array<ObjID> children = _world->getChildren(from, true); + for (uint i = 0; i < children.size(); i++) { + _world->setObjAttr(children[i], 0, to); + } +} + +void ScriptEngine::opbfSNOB(EngineState *state, EngineFrame *frame) { + _engine->enqueueObject(kAnimateBack, frame->src); +} + +void ScriptEngine::opc0TEXI(EngineState *state, EngineFrame *frame) { + _engine->enqueueObject(kHightlightExits, 0); +} + +void ScriptEngine::opc1PTXT(EngineState *state, EngineFrame *frame) { + int16 tid = state->pop(); + _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid); +} + +void ScriptEngine::opc2PNEW(EngineState *state, EngineFrame *frame) { + _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0); +} + +void ScriptEngine::opc3PTNE(EngineState *state, EngineFrame *frame) { + int16 tid = state->pop(); + _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid); + _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0); +} + +void ScriptEngine::opc4PNTN(EngineState *state, EngineFrame *frame) { + int16 tid = state->pop(); + _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0); + _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid); + _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0); +} + +void ScriptEngine::opc5PNUM(EngineState *state, EngineFrame *frame) { + int16 tid = state->pop(); + _engine->enqueueText(kTextNumber, frame->dest, frame->src, tid); +} + +void ScriptEngine::opc6P2(EngineState *state, EngineFrame *frame) { + state->push(2); +} + +void ScriptEngine::opc7PLBG(EngineState *state, EngineFrame *frame) { + int16 target = state->pop(); + _engine->enqueueSound(kSoundPlay, target); +} + +void ScriptEngine::opc8PLAW(EngineState *state, EngineFrame *frame) { + int16 target = state->pop(); + _engine->enqueueSound(kSoundPlayAndWait, target); +} + +void ScriptEngine::opc9WAIT(EngineState *state, EngineFrame *frame) { + _engine->enqueueSound(kSoundWait, 0); +} + +void ScriptEngine::opcaTIME(EngineState *state, EngineFrame *frame) { + TimeDate t; + g_system->getTimeAndDate(t); + + int year = 1900 + t.tm_year; + int month = 1 + t.tm_mon; + + state->push(year); + state->push(month); + state->push(t.tm_mday); + state->push(t.tm_hour); + state->push(t.tm_min); + state->push(t.tm_sec); + + debugC(2, kMVDebugScript, "Saved time: Y[%d] M[%d] D[%d] h[%d] m[%d] s[%d]", year, month, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); +} + +void ScriptEngine::opcbDAY(EngineState *state, EngineFrame *frame) { + TimeDate t; + g_system->getTimeAndDate(t); + + int weekday = 1 + t.tm_wday; + weekday = 1; + state->push(weekday); + + debugC(2, kMVDebugScript, "Current day of week: %d", weekday); +} + +void ScriptEngine::opccCHLD(EngineState *state, EngineFrame *frame) { + bool recursive = state->pop() != 0; + int16 obj = state->pop(); + Common::Array<ObjID> children = _world->getChildren(obj, recursive); + for (Common::Array<ObjID>::const_iterator it = children.begin(); it != children.end(); it++) { + state->push(*it); + } + state->push(children.size()); +} + +void ScriptEngine::opcdNCHLD(EngineState *state, EngineFrame *frame) { + bool recursive = state->pop() != 0; + int16 obj = state->pop(); + Common::Array<ObjID> children = _world->getChildren(obj, recursive); + state->push(children.size()); +} + +void ScriptEngine::opceVERS(EngineState *state, EngineFrame *frame) { + state->push(86); // Engine version is irrelevant. Like this in the original. +} + +void ScriptEngine::opcfPSCE(EngineState *state, EngineFrame *frame) { + state->push(0); // Any value greater than 0 indicates "Release". +} + +void ScriptEngine::opd0P1(EngineState *state, EngineFrame *frame) { + state->push(1); +} + +void ScriptEngine::opd1GOBD(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + Common::Rect bounds = _engine->getObjBounds(obj); + state->push(bounds.width()); + state->push(bounds.height()); +} + +void ScriptEngine::opd2GOVP(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + state->push(_engine->getOverlapPercent(b, a)); +} + +void ScriptEngine::opd3CAPC(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + _world->captureChildren(obj); +} + +void ScriptEngine::opd4RELC(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + _world->releaseChildren(obj); +} + +void ScriptEngine::opd5DLOG(EngineState *state, EngineFrame *frame) { + int16 txt = state->pop(); + if (_engine->showTextEntry(txt, frame->src, frame->dest)) { + state->push(0xFF); + } else { + state->push(0x00); + } +} + +void ScriptEngine::opd6ACMD(EngineState *state, EngineFrame *frame) { + _engine->selectControl((ControlAction)state->pop()); +} + +void ScriptEngine::opd7LOSE(EngineState *state, EngineFrame *frame) { + _engine->loseGame(); +} + +void ScriptEngine::opd8WIN(EngineState *state, EngineFrame *frame) { + _engine->winGame(); +} + +void ScriptEngine::opd9SLEEP(EngineState *state, EngineFrame *frame) { + int16 ticks = state->pop(); + g_system->delayMillis((ticks / 60) * 1000); + _engine->preparedToRun(); +} + +void ScriptEngine::opdaCLICK(EngineState *state, EngineFrame *frame) { + _engine->updateState(false); + _engine->clickToContinue(); +} + +void ScriptEngine::opdbROBQ(EngineState *state, EngineFrame *frame) { + _engine->runObjQueue(); +} + +void ScriptEngine::opdcRSQ(EngineState *state, EngineFrame *frame) { + _engine->playSounds(true); +} + +void ScriptEngine::opddRTQ(EngineState *state, EngineFrame *frame) { + _engine->printTexts(); +} + +void ScriptEngine::opdeUPSC(EngineState *state, EngineFrame *frame) { + _engine->updateState(true); +} + +void ScriptEngine::opdfFMAI(EngineState *state, EngineFrame *frame) { + int16 ticks = state->pop(); + g_system->delayMillis((ticks / 60) * 1000); + _engine->revert(); +} + +void ScriptEngine::ope0CHGR(EngineState *state, EngineFrame *frame) { + state->pop(); +} + +void ScriptEngine::ope1CHSO(EngineState *state, EngineFrame *frame) { + state->pop(); +} + +void ScriptEngine::ope2MDIV(EngineState *state, EngineFrame *frame) { + int16 b = state->pop(); + int16 a = state->pop(); + a *= b; + int16 c = state->pop(); + ensureNonzeroDivisor(c, 0xe2); + a /= c; + state->push(a); +} + +void ScriptEngine::ope3UPOB(EngineState *state, EngineFrame *frame) { + int16 obj = state->pop(); + _world->updateObj(obj); +} + +void ScriptEngine::ope4PLEV(EngineState *state, EngineFrame *frame) { + state->push(0); +} + +void ScriptEngine::ope5WEV(EngineState *state, EngineFrame *frame) { + op00NOOP(0xe5); +} + +void ScriptEngine::ope6GFIB(EngineState *state, EngineFrame *frame) { + state->push(0); + op00NOOP(0xe6); +} + +void ScriptEngine::ope7CFIB(EngineState *state, EngineFrame *frame) { + state->pop(); + op00NOOP(0xe7); +} + +void ScriptEngine::op00NOOP(byte op) { + warning("SCRIPT: Opcode not implemented => %x", op); +} + + + +ScriptAsset::ScriptAsset(ObjID id, Container *container) { + _id = id; + _container = container; + _ip = 0x0; + loadInstructions(); +} + +void ScriptAsset::reset() { + _ip = 0x0; +} + +uint8 ScriptAsset::fetch() { + uint8 ins = _instructions[_ip]; + _ip++; + return ins; +} + +bool ScriptAsset::hasNext() { + return _ip < _instructions.size(); +} + +void ScriptAsset::branch(int16 amount) { + _ip += amount; +} + +ObjID ScriptAsset::getId() { + return _id; +} + +void ScriptAsset::loadInstructions() { + uint32 amount = _container->getItemByteSize(_id); + Common::SeekableReadStream *res = _container->getItem(_id); + for (uint i = 0; i < amount; i++) { + _instructions.push_back(res->readByte()); + } + delete res; + debugC(2, kMVDebugScript, "Load %d instructions for script %d", amount, _id); +} + +} // End of namespace MacVenture |