aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/saga/script.cpp821
-rw-r--r--engines/saga/script.h156
-rw-r--r--engines/saga/sfuncs.cpp18
-rw-r--r--engines/saga/sthread.cpp531
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<ScriptThread> 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;
}