mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-07 11:27:44 +00:00
![Filippos Karapetis](/assets/img/avatar_default.png)
When a script thread is waiting for another thread, the current instruction offset should not be modified. The thread processing function would incorrectly continue in some special cases, and would incorrectly alter the current instruction offset. This becomes apparent in some special cases only, which made it hard to spot - plus, SAGA games have not been tested for 0.13.0, so there weren't any reports for this. This change SHOULD go to the 0.13.0 branch, but we've already tagged... svn-id: r38581
244 lines
6.8 KiB
C++
244 lines
6.8 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
// Scripting module thread management component
|
|
#include "saga/saga.h"
|
|
|
|
#include "saga/gfx.h"
|
|
#include "saga/actor.h"
|
|
#include "saga/console.h"
|
|
#include "saga/interface.h"
|
|
|
|
#include "saga/script.h"
|
|
|
|
#include "saga/scene.h"
|
|
|
|
namespace Saga {
|
|
|
|
ScriptThread *Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
|
|
ScriptThread *newThread;
|
|
|
|
loadModule(scriptModuleNumber);
|
|
if (_modules[scriptModuleNumber].entryPointsCount <= scriptEntryPointNumber) {
|
|
error("Script::createThread wrong scriptEntryPointNumber");
|
|
}
|
|
|
|
newThread = _threadList.pushFront().operator->();
|
|
newThread->_flags = kTFlagNone;
|
|
newThread->_stackSize = DEFAULT_THREAD_STACK_SIZE;
|
|
newThread->_stackBuf = (uint16 *)malloc(newThread->_stackSize * sizeof(*newThread->_stackBuf));
|
|
newThread->_stackTopIndex = newThread->_stackSize - 2;
|
|
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;
|
|
|
|
if (_vm->getGameId() == GID_IHNM)
|
|
newThread->_voiceLUT = &_globalVoiceLUT;
|
|
else
|
|
newThread->_voiceLUT = &_modules[scriptModuleNumber].voiceLUT;
|
|
|
|
return newThread;
|
|
}
|
|
|
|
void Script::wakeUpActorThread(int waitType, void *threadObj) {
|
|
ScriptThread *thread;
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
thread = threadIterator.operator->();
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType) && (thread->_threadObj == threadObj)) {
|
|
thread->_flags &= ~kTFlagWaiting;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Script::wakeUpThreads(int waitType) {
|
|
ScriptThread *thread;
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
thread = threadIterator.operator->();
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
|
|
thread->_flags &= ~kTFlagWaiting;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) {
|
|
ScriptThread *thread;
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
|
|
thread = threadIterator.operator->();
|
|
if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
|
|
thread->_waitType = kWaitTypeDelay;
|
|
thread->_sleepTime = sleepTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Script::executeThreads(uint msec) {
|
|
ScriptThread *thread;
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
if (_vm->_interface->_statusTextInput) {
|
|
return;
|
|
}
|
|
|
|
threadIterator = _threadList.begin();
|
|
|
|
while (threadIterator != _threadList.end()) {
|
|
thread = threadIterator.operator->();
|
|
|
|
if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
|
|
if (thread->_flags & kTFlagFinished)
|
|
setPointerVerb();
|
|
|
|
if (_vm->getGameId() == GID_IHNM) {
|
|
thread->_flags &= ~kTFlagFinished;
|
|
thread->_flags |= kTFlagAborted;
|
|
++threadIterator;
|
|
} else {
|
|
threadIterator = _threadList.erase(threadIterator);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (thread->_flags & kTFlagWaiting) {
|
|
|
|
switch (thread->_waitType) {
|
|
case kWaitTypeDelay:
|
|
if (thread->_sleepTime < msec) {
|
|
thread->_sleepTime = 0;
|
|
} else {
|
|
thread->_sleepTime -= msec;
|
|
}
|
|
|
|
if (thread->_sleepTime == 0)
|
|
thread->_flags &= ~kTFlagWaiting;
|
|
break;
|
|
|
|
case kWaitTypeWalk:
|
|
{
|
|
ActorData *actor;
|
|
actor = (ActorData *)thread->_threadObj;
|
|
if (actor->_currentAction == kActionWait) {
|
|
thread->_flags &= ~kTFlagWaiting;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kWaitTypeWaitFrames: // IHNM
|
|
if (thread->_frameWait < _vm->_frameCount)
|
|
thread->_flags &= ~kTFlagWaiting;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!(thread->_flags & kTFlagWaiting)) {
|
|
if (runThread(thread)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
++threadIterator;
|
|
}
|
|
|
|
}
|
|
|
|
void Script::abortAllThreads(void) {
|
|
ScriptThread *thread;
|
|
ScriptThreadList::iterator threadIterator;
|
|
|
|
threadIterator = _threadList.begin();
|
|
|
|
while (threadIterator != _threadList.end()) {
|
|
thread = threadIterator.operator->();
|
|
thread->_flags |= kTFlagAborted;
|
|
++threadIterator;
|
|
}
|
|
executeThreads(0);
|
|
}
|
|
|
|
void Script::completeThread(void) {
|
|
int limit = (_vm->getGameId() == GID_IHNM) ? 100 : 40;
|
|
|
|
for (int i = 0; i < limit && !_threadList.empty(); i++)
|
|
executeThreads(0);
|
|
}
|
|
|
|
bool Script::runThread(ScriptThread *thread) {
|
|
uint16 savedInstructionOffset;
|
|
bool stopParsing = false;
|
|
bool breakOut = false;
|
|
int operandChar;
|
|
|
|
MemoryReadStream scriptS(thread->_moduleBase, thread->_moduleBaseSize);
|
|
|
|
scriptS.seek(thread->_instructionOffset);
|
|
|
|
for (uint instructionCount = 0; instructionCount < STHREAD_TIMESLICE; instructionCount++) {
|
|
if (thread->_flags & (kTFlagAsleep))
|
|
break;
|
|
|
|
savedInstructionOffset = thread->_instructionOffset;
|
|
operandChar = scriptS.readByte();
|
|
|
|
debug(8, "Executing thread offset: %u (%x) stack: %d", thread->_instructionOffset, operandChar, thread->pushedSize());
|
|
|
|
stopParsing = false;
|
|
debug(4, "Calling op %s", this->_scriptOpsList[operandChar].scriptOpName);
|
|
(this->*_scriptOpsList[operandChar].scriptOp)(thread, &scriptS, stopParsing, breakOut);
|
|
if (stopParsing)
|
|
return breakOut;
|
|
|
|
if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
|
|
error("Wrong flags %d in thread", thread->_flags);
|
|
}
|
|
|
|
// 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()) {
|
|
error("Script::runThread() Out of range script execution");
|
|
}
|
|
|
|
scriptS.seek(thread->_instructionOffset);
|
|
}
|
|
|
|
if (breakOut)
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // End of namespace Saga
|
|
|