mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-21 19:51:49 +00:00
Rewrote the SAGA script system to use an opcode table, like in other engines
svn-id: r35670
This commit is contained in:
parent
b8fe877fa8
commit
32f73fd34c
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user