From 32f73fd34cfbae2c80ab2d502617a45511f486f2 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Fri, 2 Jan 2009 13:59:34 +0000 Subject: Rewrote the SAGA script system to use an opcode table, like in other engines svn-id: r35670 --- engines/saga/script.cpp | 821 ++++++++++++++++++++++++++++++++++++++++++++++- engines/saga/script.h | 156 ++++----- engines/saga/sfuncs.cpp | 18 +- engines/saga/sthread.cpp | 531 +----------------------------- 4 files changed, 925 insertions(+), 601 deletions(-) diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp index 1384f1bf47..62c7c3e368 100644 --- a/engines/saga/script.cpp +++ b/engines/saga/script.cpp @@ -41,7 +41,8 @@ namespace Saga { - +#define RID_SCENE1_VOICE_START 57 +#define RID_SCENE1_VOICE_END 186 // Initializes the scripting module. // Loads script resource look-up table, initializes script data system @@ -161,6 +162,7 @@ Script::Script(SagaEngine *vm) : _vm(vm) { _vm->loadStrings(_mainStrings, stringsPointer, stringsLength); free(stringsPointer); + setupScriptOpcodeList(); setupScriptFuncList(); } @@ -179,6 +181,823 @@ Script::~Script() { free(_commonBuffer); } +// Script opcodes +#define OPCODE(x) {&Script::x, #x} + +void Script::setupScriptOpcodeList() { + static const ScriptOpDescription SAGA1ScriptOpcodes[] = { + OPCODE(opDummy), // 00: Undefined + // Internal operations + OPCODE(opNextBlock), // 01: Continue execution at next block + OPCODE(opDup), // 02: Duplicate 16-bit value on stack + OPCODE(opDrop), // 03: Drop 16-bit value on stack + // Primary values + OPCODE(opZero), // 04: Push a zero on the stack + OPCODE(opOne), // 05: Push a one on the stack + OPCODE(opConstInt), // 06: Constant integer + OPCODE(opDummy), // 07: Constant ID reference (unused) + OPCODE(opStrLit), // 08: String literal + OPCODE(opDummy), // 09: Symbol address (unused) + OPCODE(opDummy), // 10: Symbol contents (unused) + // References within this module + OPCODE(opGetFlag), // 11: Read flag bit + OPCODE(opGetInt), // 12: Read integer + OPCODE(opDummy), // 13: Read string (unused) + OPCODE(opDummy), // 14: Read id (unused) + OPCODE(opPutFlag), // 15: Write flag bit + OPCODE(opPutInt), // 16: Write integer + OPCODE(opDummy), // 17: Write string (unused) + OPCODE(opDummy), // 18: Write id (unused) + // Void versions, which consume their arguments + OPCODE(opPutFlagV), // 19: Write flag bit + OPCODE(opPutIntV), // 20: Write integer + OPCODE(opDummy), // 21: Write string (unused) + OPCODE(opDummy), // 22: Write id (unused) + // Function calling + OPCODE(opCall), // 23: Call function + OPCODE(opCcall), // 24: Call C function + OPCODE(opCcallV), // 25: Call C function (void) + OPCODE(opEnter), // 26: Enter a function + OPCODE(opReturn), // 27: Return from a function + OPCODE(opReturnV), // 28: Return from a function (void) + // Branching + OPCODE(opJmp), // 29 + OPCODE(opJmpTrueV), // 30: Test argument and consume it + OPCODE(opJmpFalseV), // 31: Test argument and consume it + OPCODE(opJmpTrue), // 32: Test argument but don't consume it + OPCODE(opJmpFalse), // 33: Test argument but don't consume it + OPCODE(opJmpSwitch), // 34: Switch (integer) + OPCODE(opDummy), // 35: Switch (string) (unused) + OPCODE(opJmpRandom), // 36: Random jump + // Unary operators + OPCODE(opNegate), // 37 + OPCODE(opNot), // 38 + OPCODE(opCompl), // 39 + OPCODE(opIncV), // 40: Increment, don't push + OPCODE(opDecV), // 41: Increment, don't push + OPCODE(opPostInc), // 42 + OPCODE(opPostDec), // 43 + // Arithmetic + OPCODE(opAdd), // 44 + OPCODE(opSub), // 45 + OPCODE(opMul), // 46 + OPCODE(opDiv), // 47 + OPCODE(opMod), // 48 + // Conditional + OPCODE(opDummy), // 49: opConditional (unused) + OPCODE(opDummy), // 50: opComma (unused) + // Comparison + OPCODE(opEq), // 51 + OPCODE(opNe), // 52 + OPCODE(opGt), // 53 + OPCODE(opLt), // 54 + OPCODE(opGe), // 55 + OPCODE(opLe), // 56 + // String comparison + OPCODE(opDummy), // 57: opStrEq (unused) + OPCODE(opDummy), // 58: opStrNe (unused) + OPCODE(opDummy), // 59: opStrGt (unused) + OPCODE(opDummy), // 60: opStrLt (unused) + OPCODE(opDummy), // 61: opStrGe (unused) + OPCODE(opDummy), // 62: opStrLe (unused) + // Shift + OPCODE(opRsh), // 63 + OPCODE(opLsh), // 64 + // Bitwise + OPCODE(opAnd), // 65 + OPCODE(opOr), // 66 + OPCODE(opXor), // 67 + // Logical + OPCODE(opLAnd), // 68 + OPCODE(opLOr), // 69 + OPCODE(opLXor), // 70 + // String manipulation + OPCODE(opDummy), // 71: opStrCat, string concatenation (unused) + OPCODE(opDummy), // 72: opStrFormat, string formatting (unused) + // Assignment + OPCODE(opDummy), // 73: assign (unused) + OPCODE(opDummy), // 74: += (unused) + OPCODE(opDummy), // 75: -= (unused) + OPCODE(opDummy), // 76: *= (unused) + OPCODE(opDummy), // 77: /= (unused) + OPCODE(opDummy), // 78: %= (unused) + OPCODE(opDummy), // 79: <<= (unused) + OPCODE(opDummy), // 80: >>= (unused) + OPCODE(opDummy), // 81: and (unused) + OPCODE(opDummy), // 82: or (unused) + // Special + OPCODE(opSpeak), // 83 + OPCODE(opDialogBegin), // 84 + OPCODE(opDialogEnd), // 85 + OPCODE(opReply), // 86 + OPCODE(opAnimate) // 87 + }; + + static const ScriptOpDescription SAGA2ScriptOpcodes[] = { + OPCODE(opDummy), // 00: Undefined + // Internal operations + OPCODE(opNextBlock), // 01: Continue execution at next block + OPCODE(opDup), // 02: Duplicate 16-bit value on stack + OPCODE(opDrop), // 03: Drop 16-bit value on stack + // Primary values + OPCODE(opZero), // 04: Push a zero on the stack + OPCODE(opOne), // 05: Push a one on the stack + OPCODE(opConstInt), // 06: Constant integer + OPCODE(opDummy), // 07: Constant ID reference (unused) + OPCODE(opStrLit), // 08: String literal + OPCODE(opDummy), // 09: Symbol address (unused) + OPCODE(opDummy), // 10: Symbol contents (unused) + OPCODE(opDummy), // 11: Reference to "this" (unused) + OPCODE(opDummy), // 12: Dereference of an ID (unused) + // References within this module + OPCODE(opGetFlag), // 13: Read flag bit + OPCODE(opGetByte), // 14: Read byte + OPCODE(opGetInt), // 15: Read integer + OPCODE(opDummy), // 16: Read string (unused) + OPCODE(opDummy), // 17: Read id (unused) + OPCODE(opPutFlag), // 18: Write flag bit + OPCODE(opPutByte), // 19: Write byte + OPCODE(opPutInt), // 20: Write integer + OPCODE(opDummy), // 21: Write string (unused) + OPCODE(opDummy), // 22: Write id (unused) + OPCODE(opDummy), // 23: Push effective address (unused) + // Void versions, which consume their arguments + OPCODE(opPutFlagV), // 24: Write flag bit + OPCODE(opPutByteV), // 25: Write byte + OPCODE(opPutIntV), // 26: Write integer + OPCODE(opDummy), // 27: Write string (unused) + OPCODE(opDummy), // 28: Write id (unused) + // Function calling + OPCODE(opCallNear), // 29: Call function in the same segment + OPCODE(opCallFar), // 30: Call function in other segment + OPCODE(opCcall), // 31: Call C function + OPCODE(opCcallV), // 32: Call C function (void) + OPCODE(opCallMember), // 33: Call member function + OPCODE(opCallMemberV), // 34: Call member function (void) + OPCODE(opEnter), // 35: Enter a function + OPCODE(opReturn), // 36: Return from a function + OPCODE(opReturnV), // 37: Return from a function (void) + // Branching + OPCODE(opJmp), // 38 + OPCODE(opJmpTrueV), // 39: Test argument and consume it + OPCODE(opJmpFalseV), // 40: Test argument and consume it + OPCODE(opJmpTrue), // 41: Test argument but don't consume it + OPCODE(opJmpFalse), // 42: Test argument but don't consume it + OPCODE(opJmpSwitch), // 43: Switch (integer) + OPCODE(opDummy), // 44: Switch (string) (unused) + OPCODE(opJmpRandom), // 45: Random jump + // Unary operators + OPCODE(opNegate), // 46 + OPCODE(opNot), // 47 + OPCODE(opCompl), // 48 + OPCODE(opIncV), // 49: Increment, don't push + OPCODE(opDecV), // 50: Increment, don't push + OPCODE(opPostInc), // 51 + OPCODE(opPostDec), // 52 + // Arithmetic + OPCODE(opAdd), // 53 + OPCODE(opSub), // 54 + OPCODE(opMul), // 55 + OPCODE(opDiv), // 56 + OPCODE(opMod), // 57 + // Conditional + OPCODE(opDummy), // 58: opConditional (unused) + OPCODE(opDummy), // 59: opComma (unused) + // Comparison + OPCODE(opEq), // 60 + OPCODE(opNe), // 61 + OPCODE(opGt), // 62 + OPCODE(opLt), // 63 + OPCODE(opGe), // 64 + OPCODE(opLe), // 65 + // String comparison + OPCODE(opDummy), // 66: opStrEq (unused) + OPCODE(opDummy), // 67: opStrNe (unused) + OPCODE(opDummy), // 68: opStrGt (unused) + OPCODE(opDummy), // 69: opStrLt (unused) + OPCODE(opDummy), // 70: opStrGe (unused) + OPCODE(opDummy), // 71: opStrLe (unused) + // Shift + OPCODE(opRsh), // 72 + OPCODE(opLsh), // 73 + // Bitwise + OPCODE(opAnd), // 74 + OPCODE(opOr), // 75 + OPCODE(opXor), // 76 + // Logical + OPCODE(opLAnd), // 77 + OPCODE(opLOr), // 78 + OPCODE(opLXor), // 79 + // String manipulation + OPCODE(opDummy), // 80: opStrCat, string concatenation (unused) + OPCODE(opDummy), // 81: opStrFormat, string formatting (unused) + // Assignment + OPCODE(opDummy), // 82: assign (unused) + OPCODE(opDummy), // 83: += (unused) + OPCODE(opDummy), // 84: -= (unused) + OPCODE(opDummy), // 85: *= (unused) + OPCODE(opDummy), // 86: /= (unused) + OPCODE(opDummy), // 87: %= (unused) + OPCODE(opDummy), // 88: <<= (unused) + OPCODE(opDummy), // 89: >>= (unused) + OPCODE(opDummy), // 90: and (unused) + OPCODE(opDummy), // 91: or (unused) + // Special + OPCODE(opSpeak), // 92 + OPCODE(opDialogBegin), // 93 + OPCODE(opDialogEnd), // 94 + OPCODE(opReply), // 95 + OPCODE(opAnimate), // 96 + OPCODE(opJmpSeedRandom),// 97: Seeded random jump + OPCODE(opDummy) // 98: Get seeded export number (unused) + }; + + if (!_vm->isSaga2()) { + _scriptOpsList = SAGA1ScriptOpcodes; + } else { + _scriptOpsList = SAGA2ScriptOpcodes; + } +} + + +void Script::opDup(SCRIPTOP_PARAMS) { + thread->push(thread->stackTop()); +} + +void Script::opDrop(SCRIPTOP_PARAMS) { + thread->pop(); +} + +void Script::opZero(SCRIPTOP_PARAMS) { + thread->push(0); +} + +void Script::opOne(SCRIPTOP_PARAMS) { + thread->push(1); +} + +void Script::opConstInt(SCRIPTOP_PARAMS) { + thread->push(scriptS->readSint16LE()); +} + +void Script::opStrLit(SCRIPTOP_PARAMS) { + thread->push(scriptS->readSint16LE()); +} + +void Script::opGetFlag(SCRIPTOP_PARAMS) { + byte *addr = thread->baseAddress(scriptS->readByte()); + int16 iparam1 = scriptS->readSint16LE(); + addr += (iparam1 >> 3); + iparam1 = (1 << (iparam1 & 7)); + thread->push((*addr) & iparam1 ? 1 : 0); +} + +void Script::opGetByte(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opGetByte"); +} + +void Script::opGetInt(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + thread->push(readUint16(addr, mode)); + debug(8, "0x%X", readUint16(addr, mode)); +} + +void Script::opPutFlag(SCRIPTOP_PARAMS) { + byte *addr = thread->baseAddress(scriptS->readByte()); + int16 iparam1 = scriptS->readSint16LE(); + addr += (iparam1 >> 3); + iparam1 = (1 << (iparam1 & 7)); + if (thread->stackTop()) { + *addr |= iparam1; + } else { + *addr &= ~iparam1; + } +} + +void Script::opPutByte(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opPutByte"); +} + +void Script::opPutInt(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + writeUint16(addr, thread->stackTop(), mode); +} + +void Script::opPutFlagV(SCRIPTOP_PARAMS) { + byte *addr = thread->baseAddress(scriptS->readByte()); + int16 iparam1 = scriptS->readSint16LE(); + addr += (iparam1 >> 3); + iparam1 = (1 << (iparam1 & 7)); + if (thread->pop()) { + *addr |= iparam1; + } else { + *addr &= ~iparam1; + } +} + +void Script::opPutByteV(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opPutByteV"); +} + +void Script::opPutIntV(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + writeUint16(addr, thread->pop(), mode); +} + +void Script::opCall(SCRIPTOP_PARAMS) { + byte argumentsCount = scriptS->readByte(); + int16 iparam1 = scriptS->readByte(); + if (iparam1 != kAddressModule) { + error("Script::runThread iparam1 != kAddressModule"); + } + byte *addr = thread->baseAddress(iparam1); + iparam1 = scriptS->readSint16LE(); + addr += iparam1; + thread->push(argumentsCount); + + // NOTE: The original pushes the program + // counter as a pointer here. But I don't think + // we will have to do that. + thread->push(scriptS->pos()); + // NOTE2: program counter is 32bit - so we should "emulate" it size - because kAddressStack relies on it + thread->push(0); + thread->_instructionOffset = iparam1; +} + +void Script::opCallNear(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opCallNear"); +} + +void Script::opCallFar(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opCallFar"); +} + +void Script::opCcall(SCRIPTOP_PARAMS) { + byte argumentsCount = scriptS->readByte(); + uint16 functionNumber = scriptS->readUint16LE(); + if (functionNumber >= ((_vm->getGameId() == GID_IHNM) ? + IHNM_SCRIPT_FUNCTION_MAX : ITE_SCRIPT_FUNCTION_MAX)) { + error("Script::opCcall() Invalid script function number (%d)", functionNumber); + } + + debug(2, "Calling #%d %s argCount=%i", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName, argumentsCount); + ScriptFunctionType scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction; + uint16 checkStackTopIndex = thread->_stackTopIndex + argumentsCount; + (this->*scriptFunction)(thread, argumentsCount, stopParsing); + if (stopParsing) + return; + + if (scriptFunction == &Saga::Script::sfScriptGotoScene || + scriptFunction == &Saga::Script::sfVsetTrack) { + stopParsing = true; // cause abortAllThreads called and _this_ thread destroyed + return; + } + + thread->_stackTopIndex = checkStackTopIndex; + + thread->push(thread->_returnValue); // return value + + if (thread->_flags & kTFlagAsleep) + breakOut = true; // break out of loop! +} + +void Script::opCallMember(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opCallMember"); +} + +void Script::opCallMemberV(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opCallMemberV"); +} + +void Script::opCcallV(SCRIPTOP_PARAMS) { + byte argumentsCount = scriptS->readByte(); + uint16 functionNumber = scriptS->readUint16LE(); + if (functionNumber >= ((_vm->getGameId() == GID_IHNM) ? + IHNM_SCRIPT_FUNCTION_MAX : ITE_SCRIPT_FUNCTION_MAX)) { + error("Script::opCcallV() Invalid script function number (%d)", functionNumber); + } + + debug(2, "Calling #%d %s argCount=%i", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName, argumentsCount); + ScriptFunctionType scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction; + uint16 checkStackTopIndex = thread->_stackTopIndex + argumentsCount; + (this->*scriptFunction)(thread, argumentsCount, stopParsing); + if (stopParsing) + return; + + if (scriptFunction == &Saga::Script::sfScriptGotoScene || + scriptFunction == &Saga::Script::sfVsetTrack) { + stopParsing = true; + return; // cause abortAllThreads called and _this_ thread destroyed + } + + thread->_stackTopIndex = checkStackTopIndex; + + if (thread->_flags & kTFlagAsleep) + breakOut = true; // break out of loop! +} + +void Script::opEnter(SCRIPTOP_PARAMS) { + thread->push(thread->_frameIndex); + thread->_frameIndex = thread->_stackTopIndex; + thread->_stackTopIndex -= (scriptS->readSint16LE() / 2); +} + +void Script::opReturn(SCRIPTOP_PARAMS) { + thread->_returnValue = thread->pop(); // return value + + thread->_stackTopIndex = thread->_frameIndex; + thread->_frameIndex = thread->pop(); + if (thread->pushedSize() == 0) { + thread->_flags |= kTFlagFinished; + stopParsing = true; + return; + } else { + thread->pop(); //cause it 0 + thread->_instructionOffset = thread->pop(); + + // Pop all the call parameters off the stack + int16 iparam1 = thread->pop(); + while (iparam1--) { + thread->pop(); + } + + thread->push(thread->_returnValue); + } +} + +void Script::opReturnV(SCRIPTOP_PARAMS) { + thread->_stackTopIndex = thread->_frameIndex; + thread->_frameIndex = thread->pop(); + if (thread->pushedSize() == 0) { + thread->_flags |= kTFlagFinished; + stopParsing = true; + return; + } else { + thread->pop(); //cause it 0 + thread->_instructionOffset = thread->pop(); + + // Pop all the call parameters off the stack + int16 iparam1 = thread->pop(); + while (iparam1--) { + thread->pop(); + } + } +} + +void Script::opJmp(SCRIPTOP_PARAMS) { + thread->_instructionOffset = scriptS->readUint16LE(); +} + +void Script::opJmpTrueV(SCRIPTOP_PARAMS) { + uint16 jmpOffset1 = scriptS->readUint16LE(); + if (thread->pop()) + thread->_instructionOffset = jmpOffset1; +} + +void Script::opJmpFalseV(SCRIPTOP_PARAMS) { + uint16 jmpOffset1 = scriptS->readUint16LE(); + if (!thread->pop()) + thread->_instructionOffset = jmpOffset1; +} + +void Script::opJmpTrue(SCRIPTOP_PARAMS) { + uint16 jmpOffset1 = scriptS->readUint16LE(); + if (thread->stackTop()) + thread->_instructionOffset = jmpOffset1; +} + +void Script::opJmpFalse(SCRIPTOP_PARAMS) { + uint16 jmpOffset1 = scriptS->readUint16LE(); + if (!thread->stackTop()) + thread->_instructionOffset = jmpOffset1; +} + +void Script::opJmpSwitch(SCRIPTOP_PARAMS) { + int16 iparam1 = scriptS->readSint16LE(); + int16 iparam2 = thread->pop(); + int16 iparam3; + + while (iparam1--) { + iparam3 = scriptS->readUint16LE(); + thread->_instructionOffset = scriptS->readUint16LE(); + if (iparam3 == iparam2) + break; + } + + if (iparam1 < 0) + thread->_instructionOffset = scriptS->readUint16LE(); +} + +void Script::opJmpRandom(SCRIPTOP_PARAMS) { + // Supposedly the number of possible branches. + // The original interpreter ignores it. + scriptS->readUint16LE(); + int16 iparam1 = scriptS->readSint16LE(); + iparam1 = _vm->_rnd.getRandomNumber(iparam1 - 1); + int16 iparam2; + + while (1) { + iparam2 = scriptS->readSint16LE(); + thread->_instructionOffset = scriptS->readUint16LE(); + + iparam1 -= iparam2; + if (iparam1 < 0) + break; + } +} + +void Script::opNegate(SCRIPTOP_PARAMS) { + thread->push(-thread->pop()); +} + +void Script::opNot(SCRIPTOP_PARAMS) { + thread->push(!thread->pop()); +} + +void Script::opCompl(SCRIPTOP_PARAMS) { + thread->push(~thread->pop()); +} + +void Script::opIncV(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + iparam1 = readUint16(addr, mode); + writeUint16(addr, iparam1 + 1, mode); +} + +void Script::opDecV(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + iparam1 = readUint16(addr, mode); + writeUint16(addr, iparam1 - 1, mode); +} + +void Script::opPostInc(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + iparam1 = readUint16(addr, mode); + thread->push(iparam1); + writeUint16(addr, iparam1 + 1, mode); +} + +void Script::opPostDec(SCRIPTOP_PARAMS) { + byte mode = scriptS->readByte(); + byte *addr = thread->baseAddress(mode); + int16 iparam1 = scriptS->readSint16LE(); + addr += iparam1; + iparam1 = readUint16(addr, mode); + thread->push(iparam1); + writeUint16(addr, iparam1 - 1, mode); +} + +void Script::opAdd(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 + iparam2); +} + +void Script::opSub(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 - iparam2); +} + +void Script::opMul(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 * iparam2); +} + +void Script::opDiv(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 / iparam2); +} + +void Script::opMod(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 % iparam2); +} + +void Script::opEq(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 == iparam2) ? 1 : 0); +} + +void Script::opNe(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 != iparam2) ? 1 : 0); +} + +void Script::opGt(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 > iparam2) ? 1 : 0); +} + +void Script::opLt(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 < iparam2) ? 1 : 0); +} + +void Script::opGe(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 >= iparam2) ? 1 : 0); +} + +void Script::opLe(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 <= iparam2) ? 1 : 0); +} + +void Script::opRsh(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 >> iparam2); +} + +void Script::opLsh(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 << iparam2); +} + +void Script::opAnd(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 & iparam2); +} + +void Script::opOr(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 | iparam2); +} + +void Script::opXor(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(iparam1 ^ iparam2); +} + +void Script::opLAnd(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 && iparam2) ? 1 : 0); +} + +void Script::opLOr(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push((iparam1 || iparam2) ? 1 : 0); +} + +void Script::opLXor(SCRIPTOP_PARAMS) { + int16 iparam2 = thread->pop(); + int16 iparam1 = thread->pop(); + thread->push(((iparam1 && !iparam2) || (!iparam1 && iparam2)) ? 1 : 0); +} + +void Script::opSpeak(SCRIPTOP_PARAMS) { + if (_vm->_actor->isSpeaking()) { + thread->wait(kWaitTypeSpeech); + stopParsing = false; + return; + } + + int stringsCount = scriptS->readByte(); + uint16 actorId = scriptS->readUint16LE(); + uint16 speechFlags = scriptS->readByte(); + int sampleResourceId = -1; + int16 first; + const char *strings[ACTOR_SPEECH_STRING_MAX]; + + scriptS->readUint16LE(); // x,y skip + + if (stringsCount == 0) + error("opSpeak stringsCount == 0"); + + if (stringsCount > ACTOR_SPEECH_STRING_MAX) + error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount); + + int16 iparam1 = first = thread->stackTop(); + for (int i = 0; i < stringsCount; i++) { + iparam1 = thread->pop(); + strings[i] = thread->_strings->getString(iparam1); + } + + // now data contains last string index + + if (_vm->getFeatures() & GF_OLD_ITE_DOS) { // special ITE dos + if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) && + (iparam1 >= 288) && (iparam1 <= (RID_SCENE1_VOICE_END - RID_SCENE1_VOICE_START + 288))) { + sampleResourceId = RID_SCENE1_VOICE_START + iparam1 - 288; + } + } else { + if (thread->_voiceLUT->voicesCount > first) + sampleResourceId = thread->_voiceLUT->voices[first]; + } + + if (sampleResourceId < 0 || sampleResourceId > 4000) + sampleResourceId = -1; + + if (_vm->getGameId() == GID_ITE && !sampleResourceId) + sampleResourceId = -1; + + _vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags); + + if (!(speechFlags & kSpeakAsync)) { + thread->wait(kWaitTypeSpeech); + } +} + +void Script::opDialogBegin(SCRIPTOP_PARAMS) { + if (_conversingThread) { + thread->wait(kWaitTypeDialogBegin); + stopParsing = false; + return; + } + _conversingThread = thread; + _vm->_interface->converseClear(); +} + +void Script::opDialogEnd(SCRIPTOP_PARAMS) { + if (thread == _conversingThread) { + _vm->_interface->activate(); + _vm->_interface->setMode(kPanelConverse); + thread->wait(kWaitTypeDialogEnd); + stopParsing = false; + return; + } +} + +void Script::opReply(SCRIPTOP_PARAMS) { + const char *str; + byte replyNum = scriptS->readByte(); + byte flags = scriptS->readByte(); + int16 iparam1 = 0; + int strID = thread->pop(); + + if (flags & kReplyOnce) { + iparam1 = scriptS->readSint16LE(); + byte *addr = thread->_staticBase + (iparam1 >> 3); + if (*addr & (1 << (iparam1 & 7))) { + return; + } + } + + str = thread->_strings->getString(strID); + if (_vm->_interface->converseAddText(str, strID, replyNum, flags, iparam1)) + warning("Error adding ConverseText (%s, %d, %d, %d)", str, replyNum, flags, iparam1); +} + +void Script::opAnimate(SCRIPTOP_PARAMS) { + scriptS->readUint16LE(); + scriptS->readUint16LE(); + thread->_instructionOffset += scriptS->readByte(); +} + +void Script::opJmpSeedRandom(SCRIPTOP_PARAMS) { + // SAGA 2 opcode + // TODO + warning("opJmpSeedRandom"); +} + void Script::loadModule(int scriptModuleNumber) { byte *resourcePointer; size_t resourceLength; diff --git a/engines/saga/script.h b/engines/saga/script.h index 8d9e17af20..9785ef9a77 100644 --- a/engines/saga/script.h +++ b/engines/saga/script.h @@ -109,75 +109,6 @@ enum ThreadWaitTypes { kWaitTypeWakeUp = 11 // IHNM. wait until get waken up }; -enum OpCodes { - opNextBlock = 0x01, - opDup = 0x02, - opDrop = 0x03, - opZero = 0x04, - opOne = 0x05, - opConstint = 0x06, -//... - opStrlit = 0x08, -//... - opGetFlag = 0x0B, - opGetInt = 0x0C, -//... - opPutFlag = 0x0F, - opPutInt = 0x10, - //... - opPutFlagV = 0x13, - opPutIntV = 0x14, -//... - opCall = 0x17, - opCcall = 0x18, - opCcallV = 0x19, - opEnter = 0x1A, - opReturn = 0x1B, - opReturnV = 0x1C, - opJmp = 0x1D, - opJmpTrueV = 0x1E, - opJmpFalseV = 0x1F, - opJmpTrue = 0x20, - opJmpFalse = 0x21, - opJmpSwitch = 0x22, -//... - opJmpRandom = 0x24, - opNegate = 0x25, - opNot = 0x26, - opCompl = 0x27, - opIncV = 0x28, - opDecV = 0x29, - opPostInc = 0x2A, - opPostDec = 0x2B, - opAdd = 0x2C, - opSub = 0x2D, - opMul = 0x2E, - opDiv = 0x2F, - opMod = 0x30, -//... - opEq = 0x33, - opNe = 0x34, - opGt = 0x35, - opLt = 0x36, - opGe = 0x37, - opLe = 0x38, -//... - opRsh = 0x3F, - opLsh = 0x40, - opAnd = 0x41, - opOr = 0x42, - opXor = 0x43, - opLAnd = 0x44, - opLOr = 0x45, - opLXor = 0x46, -//... - opSpeak = 0x53, - opDialogBegin = 0x54, - opDialogEnd = 0x55, - opReply = 0x56, - opAnimate = 0x57 -}; - enum CycleFlags { kCyclePong = 1 << 0, kCycleOnce = 1 << 1, @@ -342,7 +273,7 @@ public: typedef SortedList ScriptThreadList; - +#define SCRIPTOP_PARAMS ScriptThread *thread, MemoryReadStream *scriptS, bool &stopParsing, bool &breakOut #define SCRIPTFUNC_PARAMS ScriptThread *thread, int nArgs, bool &disContinue class Script { @@ -472,7 +403,7 @@ private: void loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength); // runThread returns true if we should break running of other threads - bool runThread(ScriptThread *thread, uint instructionLimit); + bool runThread(ScriptThread *thread); void setThreadEntrypoint(ScriptThread *thread, int entrypointNumber); public: @@ -480,6 +411,85 @@ public: private: + // Script opcodes ------------------------------------------------------------ + typedef void (Script::*ScriptOpType)(SCRIPTOP_PARAMS); + struct ScriptOpDescription { + ScriptOpType scriptOp; + const char *scriptOpName; + }; + const ScriptOpDescription *_scriptOpsList; + + void setupScriptOpcodeList(); + void opDummy(SCRIPTOP_PARAMS) { warning("Dummy opcode called"); } + void opNextBlock(SCRIPTOP_PARAMS) { + thread->_instructionOffset = (((thread->_instructionOffset) >> 10) + 1) << 10; + } + void opDup(SCRIPTOP_PARAMS); + void opDrop(SCRIPTOP_PARAMS); + void opZero(SCRIPTOP_PARAMS); + void opOne(SCRIPTOP_PARAMS); + void opConstInt(SCRIPTOP_PARAMS); + void opStrLit(SCRIPTOP_PARAMS); + void opGetFlag(SCRIPTOP_PARAMS); + void opGetByte(SCRIPTOP_PARAMS); // SAGA 2 + void opGetInt(SCRIPTOP_PARAMS); + void opPutFlag(SCRIPTOP_PARAMS); + void opPutByte(SCRIPTOP_PARAMS); // SAGA 2 + void opPutInt(SCRIPTOP_PARAMS); + void opPutFlagV(SCRIPTOP_PARAMS); + void opPutByteV(SCRIPTOP_PARAMS); + void opPutIntV(SCRIPTOP_PARAMS); + void opCall(SCRIPTOP_PARAMS); // SAGA 1 + void opCallNear(SCRIPTOP_PARAMS); // SAGA 2 + void opCallFar(SCRIPTOP_PARAMS); // SAGA 2 + void opCcall(SCRIPTOP_PARAMS); + void opCcallV(SCRIPTOP_PARAMS); + void opCallMember(SCRIPTOP_PARAMS); // SAGA 2 + void opCallMemberV(SCRIPTOP_PARAMS); // SAGA 2 + void opEnter(SCRIPTOP_PARAMS); + void opReturn(SCRIPTOP_PARAMS); + void opReturnV(SCRIPTOP_PARAMS); + void opJmp(SCRIPTOP_PARAMS); + void opJmpTrueV(SCRIPTOP_PARAMS); + void opJmpFalseV(SCRIPTOP_PARAMS); + void opJmpTrue(SCRIPTOP_PARAMS); + void opJmpFalse(SCRIPTOP_PARAMS); + void opJmpSwitch(SCRIPTOP_PARAMS); + void opJmpRandom(SCRIPTOP_PARAMS); + void opNegate(SCRIPTOP_PARAMS); + void opNot(SCRIPTOP_PARAMS); + void opCompl(SCRIPTOP_PARAMS); + void opIncV(SCRIPTOP_PARAMS); + void opDecV(SCRIPTOP_PARAMS); + void opPostInc(SCRIPTOP_PARAMS); + void opPostDec(SCRIPTOP_PARAMS); + void opAdd(SCRIPTOP_PARAMS); + void opSub(SCRIPTOP_PARAMS); + void opMul(SCRIPTOP_PARAMS); + void opDiv(SCRIPTOP_PARAMS); + void opMod(SCRIPTOP_PARAMS); + void opEq(SCRIPTOP_PARAMS); + void opNe(SCRIPTOP_PARAMS); + void opGt(SCRIPTOP_PARAMS); + void opLt(SCRIPTOP_PARAMS); + void opGe(SCRIPTOP_PARAMS); + void opLe(SCRIPTOP_PARAMS); + void opRsh(SCRIPTOP_PARAMS); + void opLsh(SCRIPTOP_PARAMS); + void opAnd(SCRIPTOP_PARAMS); + void opOr(SCRIPTOP_PARAMS); + void opXor(SCRIPTOP_PARAMS); + void opLAnd(SCRIPTOP_PARAMS); + void opLOr(SCRIPTOP_PARAMS); + void opLXor(SCRIPTOP_PARAMS); + void opSpeak(SCRIPTOP_PARAMS); + void opDialogBegin(SCRIPTOP_PARAMS); + void opDialogEnd(SCRIPTOP_PARAMS); + void opReply(SCRIPTOP_PARAMS); + void opAnimate(SCRIPTOP_PARAMS); + void opJmpSeedRandom(SCRIPTOP_PARAMS); + + // Script functions ---------------------------------------------------------- typedef void (Script::*ScriptFunctionType)(SCRIPTFUNC_PARAMS); struct ScriptFunctionDescription { @@ -488,7 +498,7 @@ private: }; const ScriptFunctionDescription *_scriptFunctionsList; - void setupScriptFuncList(void); + void setupScriptFuncList(); void sfPutString(SCRIPTFUNC_PARAMS); void sfWait(SCRIPTFUNC_PARAMS); @@ -515,7 +525,7 @@ private: void sfScriptOpenDoor(SCRIPTFUNC_PARAMS); void sfScriptCloseDoor(SCRIPTFUNC_PARAMS); void sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS); - void SF_cycleColors(SCRIPTFUNC_PARAMS); + void sfCycleColors(SCRIPTFUNC_PARAMS); void sfDoCenterActor(SCRIPTFUNC_PARAMS); void sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS); void sfScriptWalkToAsync(SCRIPTFUNC_PARAMS); diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp index 26933a36ea..9c93687a48 100644 --- a/engines/saga/sfuncs.cpp +++ b/engines/saga/sfuncs.cpp @@ -54,8 +54,8 @@ namespace Saga { #define OPCODE(x) {&Script::x, #x} -void Script::setupScriptFuncList(void) { - static const ScriptFunctionDescription ITEscriptFunctionsList[ITE_SCRIPT_FUNCTION_MAX] = { +void Script::setupScriptFuncList() { + static const ScriptFunctionDescription ITEScriptFunctionsList[ITE_SCRIPT_FUNCTION_MAX] = { OPCODE(sfPutString), OPCODE(sfWait), OPCODE(sfTakeObject), @@ -80,7 +80,7 @@ void Script::setupScriptFuncList(void) { OPCODE(sfScriptOpenDoor), OPCODE(sfScriptCloseDoor), OPCODE(sfSetBgdAnimSpeed), - OPCODE(SF_cycleColors), + OPCODE(sfCycleColors), OPCODE(sfDoCenterActor), OPCODE(sfStartBgdAnimSpeed), OPCODE(sfScriptWalkToAsync), @@ -136,7 +136,7 @@ void Script::setupScriptFuncList(void) { OPCODE(sfPlayVoice) }; -static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCTION_MAX] = { +static const ScriptFunctionDescription IHNMScriptFunctionsList[IHNM_SCRIPT_FUNCTION_MAX] = { OPCODE(sfNull), OPCODE(sfWait), OPCODE(sfTakeObject), @@ -161,7 +161,7 @@ static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCT OPCODE(sfScriptOpenDoor), OPCODE(sfScriptCloseDoor), OPCODE(sfSetBgdAnimSpeed), - OPCODE(SF_cycleColors), + OPCODE(sfCycleColors), OPCODE(sfDoCenterActor), OPCODE(sfStartBgdAnimSpeed), OPCODE(sfScriptWalkToAsync), @@ -244,9 +244,9 @@ static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCT OPCODE(sfDisableAbortSpeeches) }; if (_vm->getGameId() == GID_IHNM) - _scriptFunctionsList = IHNMscriptFunctionsList; + _scriptFunctionsList = IHNMScriptFunctionsList; else - _scriptFunctionsList = ITEscriptFunctionsList; + _scriptFunctionsList = ITEScriptFunctionsList; } // Script function #0 (0x00) @@ -688,8 +688,8 @@ void Script::sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS) { } // Script function #24 (0x18) -void Script::SF_cycleColors(SCRIPTFUNC_PARAMS) { - SF_stub("SF_cycleColors", thread, nArgs); +void Script::sfCycleColors(SCRIPTFUNC_PARAMS) { + SF_stub("sfCycleColors", thread, nArgs); error("Please, report this to sev"); } diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp index ab1c771cbc..9e015179d9 100644 --- a/engines/saga/sthread.cpp +++ b/engines/saga/sthread.cpp @@ -37,9 +37,6 @@ namespace Saga { -#define RID_SCENE1_VOICE_START 57 -#define RID_SCENE1_VOICE_END 186 - ScriptThread *Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) { ScriptThread *newThread; @@ -165,7 +162,7 @@ void Script::executeThreads(uint msec) { } if (!(thread->_flags & kTFlagWaiting)) { - if (runThread(thread, STHREAD_TIMESLICE)) { + if (runThread(thread)) { break; } } @@ -196,535 +193,30 @@ void Script::completeThread(void) { executeThreads(0); } -bool Script::runThread(ScriptThread *thread, uint instructionLimit) { - const char*operandName; - uint instructionCount; +bool Script::runThread(ScriptThread *thread) { uint16 savedInstructionOffset; - - byte *addr; - byte mode; - uint16 jmpOffset1; - int16 iparam1; - int16 iparam2; - int16 iparam3; - - bool disContinue; - byte argumentsCount; - uint16 functionNumber; - uint16 checkStackTopIndex; - ScriptFunctionType scriptFunction; - + bool stopParsing = false; + bool breakOut = false; int operandChar; - int i; MemoryReadStream scriptS(thread->_moduleBase, thread->_moduleBaseSize); scriptS.seek(thread->_instructionOffset); - for (instructionCount = 0; instructionCount < instructionLimit; instructionCount++) { + for (uint instructionCount = 0; instructionCount < STHREAD_TIMESLICE; instructionCount++) { if (thread->_flags & (kTFlagAsleep)) break; savedInstructionOffset = thread->_instructionOffset; operandChar = scriptS.readByte(); - -#define CASEOP(opName) case opName: \ - if (operandChar == opName) { \ - operandName = #opName; \ - debug(2, "%s", operandName); \ - } - debug(8, "Executing thread offset: %u (%x) stack: %d", thread->_instructionOffset, operandChar, thread->pushedSize()); - operandName=""; - switch (operandChar) { - CASEOP(opNextBlock) - // Some sort of "jump to the start of the next memory - // page" instruction, I think. - thread->_instructionOffset = (((thread->_instructionOffset) >> 10) + 1) << 10; - break; - -// STACK INSTRUCTIONS - CASEOP(opDup) - thread->push(thread->stackTop()); - break; - CASEOP(opDrop) - thread->pop(); - break; - CASEOP(opZero) - thread->push(0); - break; - CASEOP(opOne) - thread->push(1); - break; - CASEOP(opConstint) - CASEOP(opStrlit) - iparam1 = scriptS.readSint16LE(); - thread->push(iparam1); - debug(8, "0x%X", iparam1); - break; - -// DATA INSTRUCTIONS - CASEOP(opGetFlag) - addr = thread->baseAddress(scriptS.readByte()); - iparam1 = scriptS.readSint16LE(); - addr += (iparam1 >> 3); - iparam1 = (1 << (iparam1 & 7)); - thread->push((*addr) & iparam1 ? 1 : 0); - break; - CASEOP(opGetInt) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - thread->push(readUint16(addr, mode)); - debug(8, "0x%X", readUint16(addr, mode)); - break; - CASEOP(opPutFlag) - addr = thread->baseAddress(scriptS.readByte()); - iparam1 = scriptS.readSint16LE(); - addr += (iparam1 >> 3); - iparam1 = (1 << (iparam1 & 7)); - if (thread->stackTop()) { - *addr |= iparam1; - } else { - *addr &= ~iparam1; - } - break; - CASEOP(opPutInt) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - writeUint16(addr, thread->stackTop(), mode); - break; - CASEOP(opPutFlagV) - addr = thread->baseAddress(scriptS.readByte()); - iparam1 = scriptS.readSint16LE(); - addr += (iparam1 >> 3); - iparam1 = (1 << (iparam1 & 7)); - if (thread->pop()) { - *addr |= iparam1; - } else { - *addr &= ~iparam1; - } - break; - CASEOP(opPutIntV) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - writeUint16(addr, thread->pop(), mode); - break; - -// FUNCTION CALL INSTRUCTIONS - CASEOP(opCall) - argumentsCount = scriptS.readByte(); - iparam1 = scriptS.readByte(); - if (iparam1 != kAddressModule) { - error("Script::runThread iparam1 != kAddressModule"); - } - addr = thread->baseAddress(iparam1); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - thread->push(argumentsCount); - - jmpOffset1 = scriptS.pos(); - // NOTE: The original pushes the program - // counter as a pointer here. But I don't think - // we will have to do that. - thread->push(jmpOffset1); - // NOTE2: program counter is 32bit - so we should "emulate" it size - because kAddressStack relies on it - thread->push(0); - thread->_instructionOffset = iparam1; - - break; - CASEOP(opCcall) - CASEOP(opCcallV) - argumentsCount = scriptS.readByte(); - functionNumber = scriptS.readUint16LE(); - if (functionNumber >= ((_vm->getGameId() == GID_IHNM) ? - IHNM_SCRIPT_FUNCTION_MAX : ITE_SCRIPT_FUNCTION_MAX)) { - error("Script::runThread() Invalid script function number (%d)", functionNumber); - } - - debug(2, "Calling #%d %s argCount=%i", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName, argumentsCount); - scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction; - checkStackTopIndex = thread->_stackTopIndex + argumentsCount; - disContinue = false; - (this->*scriptFunction)(thread, argumentsCount, disContinue); - if (disContinue) { - return true; - } - if (scriptFunction == &Saga::Script::sfScriptGotoScene || - scriptFunction == &Saga::Script::sfVsetTrack) { - return true; // cause abortAllThreads called and _this_ thread destroyed - } - thread->_stackTopIndex = checkStackTopIndex; - - if (operandChar == opCcall) {// CALL function - thread->push(thread->_returnValue); - } - - if (thread->_flags & kTFlagAsleep) - instructionCount = instructionLimit; // break out of loop! - break; - CASEOP(opEnter) - thread->push(thread->_frameIndex); - thread->_frameIndex = thread->_stackTopIndex; - thread->_stackTopIndex -= (scriptS.readSint16LE() / 2); - break; - CASEOP(opReturn) - thread->_returnValue = thread->pop(); - CASEOP(opReturnV) - thread->_stackTopIndex = thread->_frameIndex; - thread->_frameIndex = thread->pop(); - if (thread->pushedSize() == 0) { - thread->_flags |= kTFlagFinished; - return true; - } else { - thread->pop(); //cause it 0 - thread->_instructionOffset = thread->pop(); - - // Pop all the call parameters off the stack - iparam1 = thread->pop(); - while (iparam1--) { - thread->pop(); - } - - if (operandChar == opReturn) { - thread->push(thread->_returnValue); - } - } - break; - -// BRANCH INSTRUCTIONS - CASEOP(opJmp) - jmpOffset1 = scriptS.readUint16LE(); - thread->_instructionOffset = jmpOffset1; - break; - CASEOP(opJmpTrueV) - jmpOffset1 = scriptS.readUint16LE(); - if (thread->pop()) { - thread->_instructionOffset = jmpOffset1; - } - break; - CASEOP(opJmpFalseV) - jmpOffset1 = scriptS.readUint16LE(); - if (!thread->pop()) { - thread->_instructionOffset = jmpOffset1; - } - break; - CASEOP(opJmpTrue) - jmpOffset1 = scriptS.readUint16LE(); - if (thread->stackTop()) { - thread->_instructionOffset = jmpOffset1; - } - break; - CASEOP(opJmpFalse) - jmpOffset1 = scriptS.readUint16LE(); - if (!thread->stackTop()) { - thread->_instructionOffset = jmpOffset1; - } - break; - CASEOP(opJmpSwitch) - iparam1 = scriptS.readSint16LE(); - iparam2 = thread->pop(); - while (iparam1--) { - iparam3 = scriptS.readUint16LE(); - thread->_instructionOffset = scriptS.readUint16LE(); - if (iparam3 == iparam2) { - break; - } - } - if (iparam1 < 0) { - thread->_instructionOffset = scriptS.readUint16LE(); - } - break; - CASEOP(opJmpRandom) - // Supposedly the number of possible branches. - // The original interpreter ignores it. - scriptS.readUint16LE(); - iparam1 = scriptS.readSint16LE(); - iparam1 = _vm->_rnd.getRandomNumber(iparam1 - 1); - while (1) { - iparam2 = scriptS.readSint16LE(); - thread->_instructionOffset = scriptS.readUint16LE(); - - iparam1 -= iparam2; - if (iparam1 < 0) { - break; - } - } - break; - -// UNARY INSTRUCTIONS - CASEOP(opNegate) - thread->push(-thread->pop()); - break; - CASEOP(opNot) - thread->push(!thread->pop()); - break; - CASEOP(opCompl) - thread->push(~thread->pop()); - break; - - CASEOP(opIncV) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - iparam1 = readUint16(addr, mode); - writeUint16(addr, iparam1 + 1, mode); - break; - CASEOP(opDecV) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - iparam1 = readUint16(addr, mode); - writeUint16(addr, iparam1 - 1, mode); - break; - CASEOP(opPostInc) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - iparam1 = readUint16(addr, mode); - thread->push(iparam1); - writeUint16(addr, iparam1 + 1, mode); - break; - CASEOP(opPostDec) - mode = scriptS.readByte(); - addr = thread->baseAddress(mode); - iparam1 = scriptS.readSint16LE(); - addr += iparam1; - iparam1 = readUint16(addr, mode); - thread->push(iparam1); - writeUint16(addr, iparam1 - 1, mode); - break; - -// ARITHMETIC INSTRUCTIONS - CASEOP(opAdd) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 += iparam2; - thread->push(iparam1); - break; - CASEOP(opSub) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 -= iparam2; - thread->push(iparam1); - break; - CASEOP(opMul) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 *= iparam2; - thread->push(iparam1); - break; - CASEOP(opDiv) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 /= iparam2; - thread->push(iparam1); - break; - CASEOP(opMod) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 %= iparam2; - thread->push(iparam1); - break; - -// COMPARISION INSTRUCTIONS - CASEOP(opEq) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 == iparam2) ? 1 : 0); - debug(8, "0x%X 0x%X", iparam1, iparam2); - break; - CASEOP(opNe) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 != iparam2) ? 1 : 0); - break; - CASEOP(opGt) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 > iparam2) ? 1 : 0); - break; - CASEOP(opLt) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 < iparam2) ? 1 : 0); - break; - CASEOP(opGe) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 >= iparam2) ? 1 : 0); - break; - CASEOP(opLe) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 <= iparam2) ? 1 : 0); - break; - -// SHIFT INSTRUCTIONS - CASEOP(opRsh) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 >>= iparam2; - thread->push(iparam1); - break; - CASEOP(opLsh) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 <<= iparam2; - thread->push(iparam1); - break; - -// BITWISE INSTRUCTIONS - CASEOP(opAnd) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 &= iparam2; - thread->push(iparam1); - break; - CASEOP(opOr) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 |= iparam2; - thread->push(iparam1); - break; - CASEOP(opXor) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - iparam1 ^= iparam2; - thread->push(iparam1); - break; - -// LOGICAL INSTRUCTIONS - CASEOP(opLAnd) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 && iparam2) ? 1 : 0); - break; - CASEOP(opLOr) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push((iparam1 || iparam2) ? 1 : 0); - break; - CASEOP(opLXor) - iparam2 = thread->pop(); - iparam1 = thread->pop(); - thread->push(((iparam1 && !iparam2) || (!iparam1 && iparam2)) ? 1 : 0); - break; - -// GAME INSTRUCTIONS - CASEOP(opSpeak) { - int stringsCount; - uint16 actorId; - uint16 speechFlags; - int sampleResourceId = -1; - int16 first; - const char *strings[ACTOR_SPEECH_STRING_MAX]; - - if (_vm->_actor->isSpeaking()) { - thread->wait(kWaitTypeSpeech); - return false; - } - - stringsCount = scriptS.readByte(); - actorId = scriptS.readUint16LE(); - speechFlags = scriptS.readByte(); - scriptS.readUint16LE(); // x,y skip - - if (stringsCount == 0) - error("opSpeak stringsCount == 0"); - - if (stringsCount > ACTOR_SPEECH_STRING_MAX) - error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount); - - iparam1 = first = thread->stackTop(); - for (i = 0; i < stringsCount; i++) { - iparam1 = thread->pop(); - strings[i] = thread->_strings->getString(iparam1); - } - // now data contains last string index - - if (_vm->getFeatures() & GF_OLD_ITE_DOS) { // special ITE dos - if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) && - (iparam1 >= 288) && (iparam1 <= (RID_SCENE1_VOICE_END - RID_SCENE1_VOICE_START + 288))) { - sampleResourceId = RID_SCENE1_VOICE_START + iparam1 - 288; - } - } else { - if (thread->_voiceLUT->voicesCount > first) { - sampleResourceId = thread->_voiceLUT->voices[first]; - } - } - - if (sampleResourceId < 0 || sampleResourceId > 4000) - sampleResourceId = -1; - - if (_vm->getGameId() == GID_ITE && !sampleResourceId) - sampleResourceId = -1; - - _vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags); - - if (!(speechFlags & kSpeakAsync)) { - thread->wait(kWaitTypeSpeech); - } - } - break; - CASEOP(opDialogBegin) - if (_conversingThread) { - thread->wait(kWaitTypeDialogBegin); - return false; - } - _conversingThread = thread; - _vm->_interface->converseClear(); - break; - CASEOP(opDialogEnd) - if (thread == _conversingThread) { - _vm->_interface->activate(); - _vm->_interface->setMode(kPanelConverse); - thread->wait(kWaitTypeDialogEnd); - return false; - } - break; - CASEOP(opReply) { - const char *str; - byte replyNum; - byte flags; - replyNum = scriptS.readByte(); - flags = scriptS.readByte(); - iparam1 = 0; - int strID = thread->pop(); - - if (flags & kReplyOnce) { - iparam1 = scriptS.readSint16LE(); - addr = thread->_staticBase + (iparam1 >> 3); - if (*addr & (1 << (iparam1 & 7))) { - break; - } - } - - str = thread->_strings->getString(strID); - if (_vm->_interface->converseAddText(str, strID, replyNum, flags, iparam1)) - warning("Error adding ConverseText (%s, %d, %d, %d)", str, replyNum, flags, iparam1); - } - break; - CASEOP(opAnimate) - scriptS.readUint16LE(); - scriptS.readUint16LE(); - jmpOffset1 = scriptS.readByte(); - thread->_instructionOffset += jmpOffset1; - break; - - default: - error("Script::runThread() Invalid opcode encountered 0x%X", operandChar); - } + stopParsing = false; + (this->*_scriptOpsList[operandChar].scriptOp)(thread, &scriptS, stopParsing, breakOut); + debug(4, "Calling op %s", this->_scriptOpsList[operandChar].scriptOpName); + if (stopParsing) + return true; if (thread->_flags & (kTFlagFinished | kTFlagAborted)) { error("Wrong flags %d in thread", thread->_flags); @@ -740,6 +232,9 @@ bool Script::runThread(ScriptThread *thread, uint instructionLimit) { scriptS.seek(thread->_instructionOffset); } + + if (breakOut) + break; } return false; } -- cgit v1.2.3