2004-04-12 21:40:49 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
2005-01-01 16:20:17 +00:00
|
|
|
* Copyright (C) 2004-2005 The ScummVM project
|
2004-04-12 21:40:49 +00:00
|
|
|
*
|
|
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* $Header$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// Scripting module thread management component
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/saga.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/gfx.h"
|
|
|
|
#include "saga/actor.h"
|
2004-08-10 18:31:33 +00:00
|
|
|
#include "saga/console.h"
|
2005-01-06 14:02:53 +00:00
|
|
|
#include "saga/interface.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-12 21:39:11 +00:00
|
|
|
#include "saga/script.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-15 00:24:12 +00:00
|
|
|
#include "saga/stream.h"
|
2004-12-21 06:49:07 +00:00
|
|
|
#include "saga/scene.h"
|
|
|
|
#include "saga/resnames.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
namespace Saga {
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
ScriptThread *Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
|
2005-01-15 20:12:49 +00:00
|
|
|
ScriptThread *newThread;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
loadModule(scriptModuleNumber);
|
|
|
|
if (_modules[scriptModuleNumber].entryPointsCount <= scriptEntryPointNumber) {
|
|
|
|
error("Script::createThread wrong scriptEntryPointNumber");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2005-01-21 21:55:54 +00:00
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
newThread = _threadList.pushFront().operator->();
|
2005-01-21 21:55:54 +00:00
|
|
|
newThread->_flags = kTFlagNone;
|
|
|
|
newThread->_stackSize = DEFAULT_THREAD_STACK_SIZE;
|
|
|
|
newThread->_stackBuf = (uint16 *)malloc(newThread->_stackSize * sizeof(*newThread->_stackBuf));
|
|
|
|
newThread->_stackTopIndex = newThread->_stackSize - 1; // or 2 - as in original
|
|
|
|
newThread->_instructionOffset = _modules[scriptModuleNumber].entryPoints[scriptEntryPointNumber].offset;
|
|
|
|
newThread->_commonBase = _commonBuffer;
|
|
|
|
newThread->_staticBase = _commonBuffer + _modules[scriptModuleNumber].staticOffset;
|
|
|
|
newThread->_moduleBase = _modules[scriptModuleNumber].moduleBase;
|
|
|
|
newThread->_moduleBaseSize = _modules[scriptModuleNumber].moduleBaseSize;
|
|
|
|
|
|
|
|
newThread->_strings = &_modules[scriptModuleNumber].strings;
|
|
|
|
newThread->_voiceLUT = &_modules[scriptModuleNumber].voiceLUT;
|
2004-10-22 07:18:01 +00:00
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
return newThread;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-12-24 10:22:01 +00:00
|
|
|
void Script::wakeUpActorThread(int waitType, void *threadObj) {
|
2005-01-15 20:12:49 +00:00
|
|
|
ScriptThread *thread;
|
2004-12-24 10:22:01 +00:00
|
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
|
|
thread = threadIterator.operator->();
|
2005-01-21 21:55:54 +00:00
|
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType) && (thread->_threadObj == threadObj)) {
|
|
|
|
thread->_flags &= ~kTFlagWaiting;
|
2004-12-24 10:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
void Script::wakeUpThreads(int waitType) {
|
2005-01-15 20:12:49 +00:00
|
|
|
ScriptThread *thread;
|
2004-12-21 06:49:07 +00:00
|
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
|
|
thread = threadIterator.operator->();
|
2005-01-21 21:55:54 +00:00
|
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
|
|
|
|
thread->_flags &= ~kTFlagWaiting;
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) {
|
2005-01-15 20:12:49 +00:00
|
|
|
ScriptThread *thread;
|
2004-12-21 06:49:07 +00:00
|
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
|
|
thread = threadIterator.operator->();
|
2005-01-21 21:55:54 +00:00
|
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
|
|
|
|
thread->_waitType = kWaitTypeDelay;
|
|
|
|
thread->_sleepTime = sleepTime;
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Script::executeThreads(uint msec) {
|
2005-01-15 20:12:49 +00:00
|
|
|
ScriptThread *thread;
|
2004-12-21 06:49:07 +00:00
|
|
|
ScriptThreadList::iterator threadIterator;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-12 23:57:45 +00:00
|
|
|
if (!isInitialized()) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
threadIterator = _threadList.begin();
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
while (threadIterator != _threadList.end()) {
|
|
|
|
thread = threadIterator.operator->();
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
|
|
|
|
if (thread->_flags & kTFlagFinished)
|
2005-01-15 20:12:49 +00:00
|
|
|
setPointerVerb();
|
2004-12-15 00:24:12 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
threadIterator = _threadList.erase(threadIterator);
|
2004-10-27 02:27:54 +00:00
|
|
|
continue;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_flags & kTFlagWaiting) {
|
2004-12-24 10:22:01 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_waitType == kWaitTypeDelay) {
|
|
|
|
if (thread->_sleepTime < msec) {
|
|
|
|
thread->_sleepTime = 0;
|
2004-12-24 10:22:01 +00:00
|
|
|
} else {
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_sleepTime -= msec;
|
2004-12-24 10:22:01 +00:00
|
|
|
}
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_sleepTime == 0)
|
|
|
|
thread->_flags &= ~kTFlagWaiting;
|
2004-12-21 06:49:07 +00:00
|
|
|
} else {
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_waitType == kWaitTypeWalk) {
|
2004-12-24 10:22:01 +00:00
|
|
|
ActorData *actor;
|
2005-01-21 21:55:54 +00:00
|
|
|
actor = (ActorData *)thread->_threadObj;
|
2004-12-24 10:22:01 +00:00
|
|
|
if (actor->currentAction == kActionWait) {
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_flags &= ~kTFlagWaiting;
|
2004-12-24 10:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
2004-10-27 02:27:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
if (!(thread->_flags & kTFlagWaiting))
|
2005-01-03 21:17:32 +00:00
|
|
|
runThread(thread, STHREAD_TIMESLICE);
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
++threadIterator;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
void Script::completeThread(void) {
|
2004-12-15 00:24:12 +00:00
|
|
|
for (int i = 0; i < 40 && !_threadList.isEmpty() ; i++)
|
2004-12-21 06:49:07 +00:00
|
|
|
executeThreads(0);
|
2004-08-12 01:11:11 +00:00
|
|
|
}
|
|
|
|
|
2004-08-12 23:57:45 +00:00
|
|
|
int Script::SThreadDebugStep() {
|
|
|
|
if (_dbg_singlestep) {
|
|
|
|
_dbg_dostep = 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
void Script::runThread(ScriptThread *thread, uint instructionLimit) {
|
2005-01-22 14:43:49 +00:00
|
|
|
const char*operandName;
|
2005-01-21 21:55:54 +00:00
|
|
|
uint instructionCount;
|
2005-01-21 23:25:54 +00:00
|
|
|
uint16 savedInstructionOffset;
|
|
|
|
|
|
|
|
byte *addr;
|
2005-01-21 21:55:54 +00:00
|
|
|
uint16 param1;
|
|
|
|
uint16 param2;
|
2005-01-21 23:25:54 +00:00
|
|
|
int16 iparam1;
|
|
|
|
int16 iparam2;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-21 23:25:54 +00:00
|
|
|
byte argumentsCount;
|
|
|
|
uint16 functionNumber;
|
|
|
|
ScriptFunctionType scriptFunction;
|
|
|
|
|
2004-04-12 21:40:49 +00:00
|
|
|
int debug_print = 0;
|
2004-12-21 06:49:07 +00:00
|
|
|
int operandChar;
|
2004-04-12 21:40:49 +00:00
|
|
|
int i;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// Handle debug single-stepping
|
2004-08-12 23:57:45 +00:00
|
|
|
if ((thread == _dbg_thread) && _dbg_singlestep) {
|
|
|
|
if (_dbg_dostep) {
|
2004-04-12 21:40:49 +00:00
|
|
|
debug_print = 1;
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_sleepTime = 0;
|
|
|
|
instructionLimit = 1;
|
2004-08-12 23:57:45 +00:00
|
|
|
_dbg_dostep = 0;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2005-01-03 21:17:32 +00:00
|
|
|
return;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
MemoryReadStream scriptS(thread->_moduleBase, thread->_moduleBaseSize);
|
2004-09-19 14:49:00 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
scriptS.seek(thread->_instructionOffset);
|
2004-10-09 07:39:46 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
for (instructionCount = 0; instructionCount < instructionLimit; instructionCount++) {
|
|
|
|
if (thread->_flags & (kTFlagAsleep))
|
2004-12-21 06:49:07 +00:00
|
|
|
break;
|
|
|
|
|
2005-01-21 23:25:54 +00:00
|
|
|
savedInstructionOffset = thread->_instructionOffset;
|
2004-12-21 06:49:07 +00:00
|
|
|
operandChar = scriptS.readByte();
|
2005-01-21 23:25:54 +00:00
|
|
|
|
|
|
|
#define CASEOP(opName) case opName: \
|
|
|
|
if (operandChar == opName) { \
|
2005-01-22 14:43:49 +00:00
|
|
|
operandName = #opName; \
|
2005-01-21 23:25:54 +00:00
|
|
|
}
|
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
debug(8, "Executing thread offset: %lu (%x) stack: %d", thread->_instructionOffset, operandChar, thread->pushedSize());
|
2005-01-22 14:43:49 +00:00
|
|
|
operandName="";
|
2004-12-21 06:49:07 +00:00
|
|
|
switch (operandChar) {
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opNextBlock)
|
2004-09-21 06:35:00 +00:00
|
|
|
// Some sort of "jump to the start of the next memory
|
|
|
|
// page" instruction, I think.
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_instructionOffset = 1024 * ((thread->_instructionOffset / 1024) + 1);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// STACK INSTRUCTIONS
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opDup)
|
|
|
|
thread->push(thread->stackTop());
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opDrop)
|
2004-09-23 06:46:44 +00:00
|
|
|
thread->pop();
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opZero)
|
2004-09-23 06:46:44 +00:00
|
|
|
thread->push(0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opOne)
|
2004-09-23 06:46:44 +00:00
|
|
|
thread->push(1);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opConstint)
|
|
|
|
CASEOP(opStrlit)
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
thread->push(iparam1);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// DATA INSTRUCTIONS
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opGetFlag)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-21 21:55:54 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-21 23:25:54 +00:00
|
|
|
addr += (param1 >> 3);
|
|
|
|
param1 = (1 << (param1 & 7));
|
|
|
|
thread->push((*addr) & param1 ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opGetInt)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
2005-01-21 23:25:54 +00:00
|
|
|
thread->push(*((uint16*)addr));
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opPutFlag)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-21 21:55:54 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-21 23:25:54 +00:00
|
|
|
addr += (param1 >> 3);
|
|
|
|
param1 = (1 << (param1 & 7));
|
|
|
|
if (thread->stackTop()) {
|
|
|
|
*addr |= param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2005-01-21 23:25:54 +00:00
|
|
|
*addr &= ~param1;
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opPutInt)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
2005-01-21 23:25:54 +00:00
|
|
|
*(uint16*)addr = thread->stackTop();
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opPutFlagV)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-21 21:55:54 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-21 23:25:54 +00:00
|
|
|
addr += (param1 >> 3);
|
|
|
|
param1 = (1 << (param1 & 7));
|
|
|
|
if (thread->pop()) {
|
|
|
|
*addr |= param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2005-01-21 23:25:54 +00:00
|
|
|
*addr &= ~param1;
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opPutIntV)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
2005-01-21 23:25:54 +00:00
|
|
|
*(uint16*)addr = thread->pop();
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2005-01-22 15:28:24 +00:00
|
|
|
// FUNCTION CALL INSTRUCTIONS
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opCall)
|
|
|
|
argumentsCount = scriptS.readByte();
|
|
|
|
param1 = scriptS.readByte();
|
|
|
|
if (param1 != kAddressModule) {
|
|
|
|
error("Script::runThread param1 != kAddressModule");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2005-01-21 23:25:54 +00:00
|
|
|
addr = thread->baseAddress(param1);
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
2005-01-21 23:25:54 +00:00
|
|
|
thread->push(argumentsCount);
|
|
|
|
|
|
|
|
param2 = 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(param2);
|
2005-01-22 14:43:49 +00:00
|
|
|
thread->_instructionOffset = iparam1;
|
2005-01-21 23:25:54 +00:00
|
|
|
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:25:54 +00:00
|
|
|
CASEOP(opCcall)
|
|
|
|
CASEOP(opCcallV)
|
|
|
|
argumentsCount = scriptS.readByte();
|
|
|
|
functionNumber = scriptS.readUint16LE();
|
|
|
|
if (functionNumber >= SCRIPT_FUNCTION_MAX) {
|
|
|
|
error("Script::runThread() Invalid script function number");
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-21 23:25:54 +00:00
|
|
|
debug(8, "Calling 0x%X %s", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName);
|
|
|
|
scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction;
|
2005-01-22 19:21:38 +00:00
|
|
|
(this->*scriptFunction)(thread, argumentsCount);
|
2004-10-03 17:11:23 +00:00
|
|
|
|
2005-01-21 23:35:33 +00:00
|
|
|
if (functionNumber == 16) { // SF_gotoScene
|
2005-01-21 23:25:54 +00:00
|
|
|
instructionCount = instructionLimit; // break the loop
|
|
|
|
break;
|
|
|
|
}
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2005-01-21 23:44:34 +00:00
|
|
|
if (operandChar == opCcall) {// CALL function
|
2005-01-21 23:25:54 +00:00
|
|
|
thread->push(thread->_returnValue);
|
2005-01-21 23:44:34 +00:00
|
|
|
}
|
2004-10-27 02:27:54 +00:00
|
|
|
|
2005-01-21 23:25:54 +00:00
|
|
|
if (thread->_flags & kTFlagAsleep)
|
|
|
|
instructionCount = instructionLimit; // break out of loop!
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:44:34 +00:00
|
|
|
CASEOP(opEnter)
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->push(thread->_frameIndex);
|
|
|
|
thread->_frameIndex = thread->_stackTopIndex;
|
2005-01-22 14:43:49 +00:00
|
|
|
thread->_stackTopIndex -= (scriptS.readSint16LE() / 2);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-21 23:44:34 +00:00
|
|
|
CASEOP(opReturn)
|
|
|
|
thread->_returnValue = thread->pop();
|
|
|
|
CASEOP(opReturnV)
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_stackTopIndex = thread->_frameIndex;
|
|
|
|
thread->_frameIndex = thread->pop();
|
|
|
|
if (thread->pushedSize() == 0) {
|
|
|
|
thread->_flags |= kTFlagFinished;
|
2005-01-22 14:43:49 +00:00
|
|
|
break;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2005-01-21 21:55:54 +00:00
|
|
|
thread->_instructionOffset = thread->pop();
|
2005-01-22 14:43:49 +00:00
|
|
|
iparam1 = thread->pop();
|
|
|
|
iparam1 += iparam1;
|
|
|
|
while (iparam1--) {
|
2005-01-21 23:44:34 +00:00
|
|
|
thread->pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (operandChar == opReturn) {
|
|
|
|
thread->push(thread->_returnValue);
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// BRANCH INSTRUCTIONS
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmp)
|
2004-08-22 18:28:42 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-22 14:43:49 +00:00
|
|
|
thread->_instructionOffset = param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmpTrueV)
|
2004-08-22 18:28:42 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-22 14:43:49 +00:00
|
|
|
if (thread->pop()) {
|
|
|
|
thread->_instructionOffset = param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmpFalseV)
|
2004-08-22 18:28:42 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-22 14:43:49 +00:00
|
|
|
if (!thread->pop()) {
|
|
|
|
thread->_instructionOffset = param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmpTrue)
|
2004-08-22 18:28:42 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-22 14:43:49 +00:00
|
|
|
if (thread->stackTop()) {
|
|
|
|
thread->_instructionOffset = param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmpFalse)
|
2004-08-22 18:28:42 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
2005-01-22 14:43:49 +00:00
|
|
|
if (!thread->stackTop()) {
|
|
|
|
thread->_instructionOffset = param1;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opJmpSwitch)
|
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
param1 = thread->pop();
|
|
|
|
while (iparam1--) {
|
|
|
|
param2 = scriptS.readUint16LE();
|
|
|
|
thread->_instructionOffset = scriptS.readUint16LE();
|
|
|
|
if (param2 == param1) {
|
|
|
|
break;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
2005-01-22 14:43:49 +00:00
|
|
|
if (iparam1 < 0) {
|
|
|
|
thread->_instructionOffset = scriptS.readUint16LE();
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
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;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-08-26 23:28:10 +00:00
|
|
|
// UNARY INSTRUCTIONS
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opNegate)
|
|
|
|
thread->push(-thread->pop());
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opNot)
|
|
|
|
thread->push(!thread->pop());
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opCompl)
|
|
|
|
thread->push(~thread->pop());
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
|
|
|
|
CASEOP(opIncV)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
|
|
|
*(uint16*)addr += 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opDecV)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
|
|
|
*(uint16*)addr -= 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opPostInc)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
|
|
|
thread->push(*(int16*)addr);
|
|
|
|
*(uint16*)addr += 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 14:43:49 +00:00
|
|
|
CASEOP(opPostDec)
|
|
|
|
addr = thread->baseAddress(scriptS.readByte());
|
|
|
|
iparam1 = scriptS.readSint16LE();
|
|
|
|
addr += iparam1;
|
|
|
|
thread->push(*(int16*)addr);
|
|
|
|
*(uint16*)addr -= 1;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// ARITHMETIC INSTRUCTIONS
|
2005-01-22 15:28:24 +00:00
|
|
|
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);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2004-05-13 01:41:23 +00:00
|
|
|
|
2005-01-22 15:28:24 +00:00
|
|
|
// COMPARISION INSTRUCTIONS
|
|
|
|
CASEOP(opEq)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 == iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
CASEOP(opNe)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 != iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
CASEOP(opGt)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 > iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
CASEOP(opLt)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 < iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
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);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2005-01-22 15:28:24 +00:00
|
|
|
// 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;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-22 15:28:24 +00:00
|
|
|
// LOGICAL INSTRUCTIONS
|
|
|
|
CASEOP(opLAnd)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 && iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
CASEOP(opLOr)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push((iparam1 || iparam2) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 15:28:24 +00:00
|
|
|
CASEOP(opLXor)
|
|
|
|
iparam2 = thread->pop();
|
|
|
|
iparam1 = thread->pop();
|
|
|
|
thread->push(((iparam1 && !iparam2) || (!iparam1 && iparam2)) ? 1 : 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-01 16:15:55 +00:00
|
|
|
// GAME INSTRUCTIONS
|
2005-01-22 16:32:30 +00:00
|
|
|
CASEOP(opSpeak) {
|
2004-12-21 06:49:07 +00:00
|
|
|
int stringsCount;
|
2004-12-17 11:18:56 +00:00
|
|
|
uint16 actorId;
|
2005-01-22 16:32:30 +00:00
|
|
|
uint16 speechFlags;
|
2004-12-21 06:49:07 +00:00
|
|
|
int sampleResourceId = -1;
|
2005-01-22 16:32:30 +00:00
|
|
|
int16 first;
|
2004-12-21 06:49:07 +00:00
|
|
|
const char *strings[ACTOR_SPEECH_STRING_MAX];
|
|
|
|
|
|
|
|
if (_vm->_actor->isSpeaking()) {
|
|
|
|
thread->wait(kWaitTypeSpeech);
|
2005-01-03 21:17:32 +00:00
|
|
|
return;
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
stringsCount = scriptS.readByte();
|
|
|
|
actorId = scriptS.readUint16LE();
|
|
|
|
speechFlags = scriptS.readByte();
|
|
|
|
scriptS.readUint16LE(); // x,y skip
|
|
|
|
|
|
|
|
if (stringsCount == 0)
|
|
|
|
error("opSpeak stringsCount == 0");
|
|
|
|
|
2004-12-22 21:04:50 +00:00
|
|
|
if (stringsCount > ACTOR_SPEECH_STRING_MAX)
|
2004-12-21 06:49:07 +00:00
|
|
|
error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount);
|
|
|
|
|
2005-01-22 16:32:30 +00:00
|
|
|
iparam1 = first = thread->stackTop();
|
2004-12-21 06:49:07 +00:00
|
|
|
for (i = 0; i < stringsCount; i++) {
|
2005-01-22 16:32:30 +00:00
|
|
|
iparam1 = thread->pop();
|
|
|
|
strings[i] = thread->_strings->getString(iparam1);
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
|
|
|
// now data contains last string index
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-11 21:10:36 +00:00
|
|
|
if (_vm->getGameId() == GID_ITE_DISK_G) { // special ITE dos
|
2005-01-22 16:32:30 +00:00
|
|
|
if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) &&
|
|
|
|
(iparam1 >= 288) && (iparam1 <= (RID_SCENE1_VOICE_138 - RID_SCENE1_VOICE_009 + 288))) {
|
|
|
|
sampleResourceId = RID_SCENE1_VOICE_009 + iparam1 - 288;
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
|
|
|
} else {
|
2005-01-21 21:55:54 +00:00
|
|
|
if (thread->_voiceLUT->voicesCount > first) {
|
|
|
|
sampleResourceId = thread->_voiceLUT->voices[first];
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2004-12-21 06:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags);
|
|
|
|
|
|
|
|
if (!(speechFlags & kSpeakAsync)) {
|
|
|
|
thread->wait(kWaitTypeSpeech);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2005-01-22 16:32:30 +00:00
|
|
|
CASEOP(opDialogBegin)
|
2005-01-17 20:17:06 +00:00
|
|
|
if (_conversingThread) {
|
|
|
|
thread->wait(kWaitTypeDialogBegin);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_conversingThread = thread;
|
|
|
|
_vm->_interface->converseClear();
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 16:32:30 +00:00
|
|
|
CASEOP(opDialogEnd)
|
2005-01-17 20:17:06 +00:00
|
|
|
if (thread == _conversingThread) {
|
2005-01-06 14:02:53 +00:00
|
|
|
_vm->_interface->activate();
|
|
|
|
_vm->_interface->setMode(kPanelConverse);
|
2005-01-17 20:17:06 +00:00
|
|
|
thread->wait(kWaitTypeDialogEnd);
|
2005-01-06 14:02:53 +00:00
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
2005-01-22 16:32:30 +00:00
|
|
|
CASEOP(opReply) {
|
2005-01-17 20:17:06 +00:00
|
|
|
const char *str;
|
2005-01-22 16:32:30 +00:00
|
|
|
byte replyNum;
|
|
|
|
byte flags;
|
|
|
|
replyNum = scriptS.readByte();
|
|
|
|
flags = scriptS.readByte();
|
|
|
|
param1 = 0;
|
2005-01-17 20:17:06 +00:00
|
|
|
|
|
|
|
if (flags & kReplyOnce) {
|
2005-01-22 16:32:30 +00:00
|
|
|
param1 = scriptS.readUint16LE();
|
|
|
|
addr = thread->_staticBase + (param1 >> 3);
|
|
|
|
if (*addr & (1 << (param1 & 7))) {
|
|
|
|
break;
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2005-01-17 20:17:06 +00:00
|
|
|
|
2005-01-21 21:55:54 +00:00
|
|
|
str = thread->_strings->getString(thread->pop());
|
2005-01-22 16:32:30 +00:00
|
|
|
if (_vm->_interface->converseAddText(str, replyNum, flags, param1))
|
|
|
|
warning("Error adding ConverseText (%s, %d, %d, %d)", str, replyNum, flags, param1);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-01-22 16:32:30 +00:00
|
|
|
CASEOP(opAnimate)
|
2004-08-26 23:28:10 +00:00
|
|
|
scriptS.readUint16LE();
|
|
|
|
scriptS.readUint16LE();
|
2005-01-22 16:32:30 +00:00
|
|
|
param1 = scriptS.readByte();
|
|
|
|
thread->_instructionOffset += param1;
|
2004-08-26 23:28:10 +00:00
|
|
|
break;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
default:
|
2005-01-22 16:32:30 +00:00
|
|
|
error("Script::runThread() Invalid opcode encountered 0x%X", operandChar);
|
2004-05-01 16:15:55 +00:00
|
|
|
}
|
2005-01-22 16:32:30 +00:00
|
|
|
|
2005-01-22 14:43:49 +00:00
|
|
|
debug(8, operandName);
|
|
|
|
_vm->_console->DebugPrintf("%s\n", operandName);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-22 14:43:49 +00:00
|
|
|
|
|
|
|
if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
|
|
|
|
_vm->_console->DebugPrintf("Script finished\n");
|
|
|
|
break;
|
2004-09-19 14:49:00 +00:00
|
|
|
} else {
|
2005-01-21 23:25:54 +00:00
|
|
|
|
2005-01-22 14:43:49 +00:00
|
|
|
// Set instruction offset only if a previous instruction didn't branch
|
|
|
|
if (savedInstructionOffset == thread->_instructionOffset) {
|
|
|
|
thread->_instructionOffset = scriptS.pos();
|
|
|
|
} else {
|
|
|
|
if (thread->_instructionOffset >= scriptS.size()) {
|
2005-01-22 16:32:30 +00:00
|
|
|
error("Script::runThread() Out of range script execution");
|
2005-01-22 14:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
scriptS.seek(thread->_instructionOffset);
|
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2005-01-03 21:17:32 +00:00
|
|
|
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Saga
|
|
|
|
|