Rewrote the SAGA script system to use an opcode table, like in other engines

svn-id: r35670
This commit is contained in:
Filippos Karapetis 2009-01-02 13:59:34 +00:00
parent b8fe877fa8
commit 32f73fd34c
4 changed files with 925 additions and 601 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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");
}

View File

@ -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;
}