openmohaa/code/script/scriptvm.cpp

1953 lines
34 KiB
C++
Raw Normal View History

2023-01-29 20:59:31 +01:00
/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// scriptvm.cpp : Script virtual machine, interprets and execute scripts
#include "glb_local.h"
#include "scriptmaster.h"
#include "scriptthread.h"
#include "scriptclass.h"
2023-01-29 20:59:31 +01:00
#include "scriptvm.h"
#include "scriptcompiler.h"
2023-01-29 20:59:31 +01:00
#include "game.h"
#include "level.h"
#include "parm.h"
#include "world.h"
#ifdef CGAME_DLL
#define VM_Printf cgi.Printf
#define VM_DPrintf cgi.DPrintf
#elif defined GAME_DLL
#define VM_Printf gi.Printf
#define VM_DPrintf gi.DPrintf2
#else
#define VM_Printf printf
#define VM_DPrintf printf
#endif
//====================
// ScriptVM
//====================
MEM_BlockAlloc<ScriptVM> ScriptVM_allocator;
2023-01-29 20:59:31 +01:00
/*
====================
new ScriptVM
====================
*/
void* ScriptVM::operator new(size_t size)
{
return ScriptVM_allocator.Alloc();
}
/*
====================
delete ptr
====================
*/
void ScriptVM::operator delete(void* ptr)
{
ScriptVM_allocator.Free(ptr);
}
/*
====================
ScriptVM
====================
*/
ScriptVM::ScriptVM(ScriptClass* scriptClass, unsigned char* pCodePos, ScriptThread* thread)
{
next = NULL;
m_Thread = thread;
m_ScriptClass = scriptClass;
m_Stack = NULL;
m_PrevCodePos = NULL;
m_CodePos = pCodePos;
state = STATE_RUNNING;
m_ThreadState = THREAD_RUNNING;
m_pOldData = NULL;
m_OldDataSize = 0;
m_bMarkStack = false;
m_StackPos = NULL;
m_bAllowContextSwitch = true;
localStackSize = m_ScriptClass->GetScript()->GetRequiredStackSize();
if (localStackSize <= 0) {
localStackSize = 1;
}
localStack = new ScriptVariable[localStackSize];
pTop = localStack;
m_ScriptClass->AddThread(this);
}
/*
====================
~ScriptVM
====================
*/
ScriptVM::~ScriptVM()
{
fastEvent.data = m_pOldData;
fastEvent.dataSize = m_OldDataSize;
// clean-up the call stack
while (callStack.NumObjects())
{
LeaveFunction();
}
delete[] localStack;
}
/*
====================
Archive
====================
*/
void ScriptVM::Archive(Archiver& arc)
{
int stack = 0;
if (arc.Saving())
{
if (m_Stack)
stack = m_Stack->m_Count;
arc.ArchiveInteger(&stack);
}
else
{
arc.ArchiveInteger(&stack);
if (stack)
{
m_Stack = new ScriptStack;
m_Stack->m_Array = new ScriptVariable[stack];
m_Stack->m_Count = stack;
}
}
for (int i = 1; i <= stack; i++)
{
m_Stack->m_Array[i].ArchiveInternal(arc);
}
m_ReturnValue.ArchiveInternal(arc);
m_ScriptClass->ArchiveCodePos(arc, &m_PrevCodePos);
m_ScriptClass->ArchiveCodePos(arc, &m_CodePos);
arc.ArchiveByte(&state);
arc.ArchiveByte(&m_ThreadState);
}
/*
====================
error
Triggers an error
====================
*/
void ScriptVM::error(const char* format, ...)
{
char buffer[4000];
va_list va;
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
glbs.Printf("----------------------------------------------------------\n%s\n", buffer);
m_ReturnValue.setStringValue("$.INTERRUPTED");
}
/*
====================
executeCommand
====================
*/
void ScriptVM::executeCommand(Listener* listener, int iParamCount, int eventnum, bool bReturn)
{
Event ev;
ScriptVariable* var;
ev = Event(eventnum);
if (bReturn)
{
var = pTop;
}
else
{
var = pTop + 1;
}
ev.dataSize = iParamCount;
ev.data = new ScriptVariable[ev.dataSize];
ev.fromScript = true;
for (int i = 0; i < iParamCount; i++) {
ev.data[i] = var[i];
}
listener->ProcessScriptEvent(ev);
if (ev.NumArgs() > iParamCount) {
*pTop = ev.GetValue(ev.NumArgs());
}
else {
pTop->Clear();
}
}
/*
====================
executeGetter
====================
*/
bool ScriptVM::executeGetter(Listener* listener, str& name)
{
Event ev;
int eventnum = Event::FindGetterEventNum(name);
if (eventnum && listener->classinfo()->GetDef(eventnum))
{
ev = Event(eventnum);
ev.fromScript = true;
if (listener->ProcessScriptEvent(ev))
{
if (ev.NumArgs() > 0) {
*pTop = ev.GetValue(ev.NumArgs());
}
else {
pTop->Clear();
}
return true;
}
else
{
return false;
}
}
else
{
eventnum = Event::FindSetterEventNum(name);
assert(!eventnum || !listener->classinfo()->GetDef(eventnum));
if (eventnum && listener->classinfo()->GetDef(eventnum))
{
ScriptError("Cannot get a write-only variable");
}
}
return false;
}
/*
====================
executeSetter
====================
*/
bool ScriptVM::executeSetter(Listener* listener, str& name)
{
Event ev;
int eventnum = Event::FindSetterEventNum(name);
if (eventnum && listener->classinfo()->GetDef(eventnum))
{
ev = Event(eventnum);
ev.fromScript = true;
ev.AddValue(*pTop);
if (listener->ProcessScriptEvent(ev))
{
return true;
}
else
{
return false;
}
}
else
{
eventnum = Event::FindGetterEventNum(name);
if (eventnum && listener->classinfo()->GetDef(eventnum))
{
ScriptError("Cannot set a read-only variable");
}
}
return false;
}
/*
====================
jump
====================
*/
void ScriptVM::jump(int offset)
{
m_CodePos += offset;
}
/*
====================
jumpBool
====================
*/
void ScriptVM::jumpBool(int offset, bool booleanValue)
{
if (booleanValue)
{
jump(offset);
}
else
{
m_CodePos += sizeof(unsigned int);
}
}
/*
====================
loadTop
====================
*/
void ScriptVM::loadTop(Listener* listener, bool noTop)
{
int index;
index = *reinterpret_cast<int*>(m_CodePos);
m_CodePos += sizeof(unsigned int);
if (index != -1)
{
str& variable = Director.GetString(index);
if (!executeSetter(listener, variable))
{
listener->Vars()->SetVariable(variable, *pTop);
}
}
if (!noTop) {
pTop--;
}
}
/*
====================
storeTop
====================
*/
void ScriptVM::storeTop(Listener* listener, bool noTop)
{
str variable;
int index;
index = *reinterpret_cast<int*>(m_CodePos);
m_CodePos += sizeof(unsigned int);
if (index != -1)
{
variable = Director.GetString(index);
}
if (!noTop) {
pTop++;
}
if (index != -1 && !executeGetter(listener, variable))
{
*pTop = *listener->Vars()->GetOrCreateVariable(index);
}
}
/*
====================
ProgBuffer
Returns the current program buffer
====================
*/
unsigned char* ScriptVM::ProgBuffer(void)
{
return m_CodePos;
}
/*
====================
EnterFunction
Sets a new instruction pointer
====================
*/
void ScriptVM::EnterFunction(Event* ev)
{
ScriptCallStack* stack;
str label = ev->GetString(1);
SetFastData(ev->data + 1, ev->dataSize - 1);
unsigned char* codePos = m_ScriptClass->FindLabel(label);
if (!codePos)
{
ScriptError("ScriptVM::EnterFunction: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
}
stack = new ScriptCallStack;
stack->codePos = m_CodePos;
stack->pTop = pTop;
stack->returnValue = m_ReturnValue;
stack->localStack = localStack;
stack->m_Self = m_ScriptClass->GetSelf();
callStack.AddObject(stack);
m_CodePos = codePos;
localStack = new ScriptVariable[localStackSize];
pTop = localStack;
m_ReturnValue.Clear();
}
/*
====================
LeaveFunction
Returns to the previous function
====================
*/
void ScriptVM::LeaveFunction()
{
int num = callStack.NumObjects();
if (num)
{
ScriptCallStack* stack = callStack.ObjectAt(num);
pTop = stack->pTop;
*pTop = m_ReturnValue;
m_CodePos = stack->codePos;
m_ReturnValue = stack->returnValue;
m_ScriptClass->m_Self = stack->m_Self;
delete[] localStack;
localStack = stack->localStack;
delete stack;
callStack.RemoveObjectAt(num);
}
else
{
delete m_Thread;
}
}
/*
====================
End
End with a return value
====================
*/
void ScriptVM::End(const ScriptVariable& returnValue)
{
m_ReturnValue.setPointer(returnValue);
LeaveFunction();
}
/*
====================
End
====================
*/
void ScriptVM::End()
{
m_ReturnValue.ClearPointer();
LeaveFunction();
}
/*
====================
Execute
Executes a program
====================
*/
void ScriptVM::Execute(ScriptVariable* data, int dataSize, str label)
{
unsigned char* opcode;
bool doneProcessing = false;
bool deleteThread = false;
bool eventCalled = false;
ScriptVariable* a;
ScriptVariable* b;
ScriptVariable* c;
int index, iParamCount;
Listener* listener;
Event ev;
Event* ev2;
ScriptVariable* var = NULL;
static str str_null = "";
str& value = str_null;
ConSimple* targetList;
if (label != "")
{
// Throw if label is not found
if (!(m_CodePos = m_ScriptClass->FindLabel(label)))
{
ScriptError("ScriptVM::Execute: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
}
}
if (Director.stackCount >= MAX_STACK_DEPTH)
{
state = STATE_EXECUTION;
ScriptException::next_abort = -1;
ScriptException exc("stack overflow");
throw exc;
}
Director.stackCount++;
if (dataSize)
{
SetFastData(data, dataSize);
}
state = STATE_RUNNING;
Director.cmdTime = glbs.Milliseconds();
Director.cmdCount = 0;
while (!doneProcessing && state == STATE_RUNNING)
{
m_PrevCodePos = m_CodePos;
Director.cmdCount++;
try
{
if (Director.cmdCount > 9999 && glbs.Milliseconds() - Director.cmdTime > Director.maxTime)
{
if (level.m_LoopProtection)
{
Director.cmdTime = glbs.Milliseconds();
deleteThread = true;
state = STATE_EXECUTION;
if (level.m_LoopDrop) {
ScriptException::next_abort = -1;
}
ScriptError("Command overflow. Possible infinite loop in thread.\n");
}
VM_DPrintf("Update of script position - This is not an error.\n");
VM_DPrintf("=================================================\n");
m_ScriptClass->GetScript()->PrintSourcePos(opcode, true);
VM_DPrintf("=================================================\n");
Director.cmdCount = 0;
}
if (!m_bMarkStack)
{
assert(pTop >= localStack && pTop < localStack + localStackSize);
if (pTop < localStack)
{
deleteThread = true;
state = STATE_EXECUTION;
error("VM stack error. Negative stack value %d.\n", pTop - localStack);
break;
}
else if (pTop >= localStack + localStackSize)
{
deleteThread = true;
state = STATE_EXECUTION;
error("VM stack error. Exceeded the maximum stack size %d.\n", localStackSize);
break;
}
}
index = 0;
eventCalled = false;
opcode = m_CodePos++;
switch (*opcode)
{
case OP_BIN_BITWISE_AND:
a = pTop--;
b = pTop;
*b &= *a;
break;
case OP_BIN_BITWISE_OR:
a = pTop--;
b = pTop;
*b |= *a;
break;
case OP_BIN_BITWISE_EXCL_OR:
a = pTop--;
b = pTop;
*b ^= *a;
break;
case OP_BIN_EQUALITY:
a = pTop--;
b = pTop;
b->setIntValue(*b == *a);
break;
case OP_BIN_INEQUALITY:
a = pTop--;
b = pTop;
b->setIntValue(*b != *a);
break;
case OP_BIN_GREATER_THAN:
a = pTop--;
b = pTop;
b->greaterthan(*a);
break;
case OP_BIN_GREATER_THAN_OR_EQUAL:
a = pTop--;
b = pTop;
b->greaterthanorequal(*a);
break;
case OP_BIN_LESS_THAN:
a = pTop--;
b = pTop;
b->lessthan(*a);
break;
case OP_BIN_LESS_THAN_OR_EQUAL:
a = pTop--;
b = pTop;
b->lessthanorequal(*a);
break;
case OP_BIN_PLUS:
a = pTop--;
b = pTop;
*b += *a;
break;
case OP_BIN_MINUS:
a = pTop--;
b = pTop;
*b -= *a;
break;
case OP_BIN_MULTIPLY:
a = pTop--;
b = pTop;
*b *= *a;
break;
case OP_BIN_DIVIDE:
a = pTop--;
b = pTop;
*b /= *a;
break;
case OP_BIN_PERCENTAGE:
a = pTop--;
b = pTop;
*b %= *a;
break;
case OP_BIN_SHIFT_LEFT:
a = pTop--;
b = pTop;
*b <<= *a;
break;
case OP_BIN_SHIFT_RIGHT:
a = pTop--;
b = pTop;
*b >>= *a;
break;
case OP_BOOL_JUMP_FALSE4:
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), !pTop->m_data.intValue);
pTop--;
break;
case OP_BOOL_JUMP_TRUE4:
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), pTop->m_data.intValue ? true : false);
pTop--;
break;
case OP_VAR_JUMP_FALSE4:
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), !pTop->booleanValue());
pTop--;
break;
case OP_VAR_JUMP_TRUE4:
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), pTop->booleanValue());
pTop--;
break;
case OP_BOOL_LOGICAL_AND:
if (pTop->m_data.intValue)
{
pTop--;
m_CodePos += sizeof(unsigned int);
}
else
{
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
}
break;
case OP_BOOL_LOGICAL_OR:
if (!pTop->m_data.intValue)
{
pTop--;
m_CodePos += sizeof(unsigned int);
}
else
{
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
}
break;
case OP_VAR_LOGICAL_AND:
if (pTop->booleanValue())
{
pTop--;
m_CodePos += sizeof(unsigned int);
}
else
{
pTop->SetFalse();
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
}
break;
case OP_VAR_LOGICAL_OR:
if (!pTop->booleanValue())
{
pTop--;
m_CodePos += sizeof(unsigned int);
}
else
{
pTop->SetTrue();
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
}
break;
case OP_BOOL_STORE_FALSE:
pTop++;
pTop->SetFalse();
break;
case OP_BOOL_STORE_TRUE:
pTop++;
pTop->SetTrue();
break;
case OP_BOOL_UN_NOT:
pTop->m_data.intValue = (pTop->m_data.intValue == 0);
break;
case OP_CALC_VECTOR:
c = pTop--;
b = pTop--;
a = pTop;
pTop->setVectorValue(Vector(a->floatValue(), b->floatValue(), c->floatValue()));
break;
case OP_EXEC_CMD0:
iParamCount = 0;
goto __execCmd;
case OP_EXEC_CMD1:
iParamCount = 1;
goto __execCmd;
case OP_EXEC_CMD2:
iParamCount = 2;
goto __execCmd;
case OP_EXEC_CMD3:
iParamCount = 3;
goto __execCmd;
case OP_EXEC_CMD4:
iParamCount = 4;
goto __execCmd;
case OP_EXEC_CMD5:
iParamCount = 5;
goto __execCmd;
case OP_EXEC_CMD_COUNT1:
iParamCount = *m_CodePos++;
__execCmd:
index = *reinterpret_cast<unsigned int*>(m_CodePos);
m_CodePos += sizeof(unsigned int);
pTop -= iParamCount;
try
{
executeCommand(m_Thread, iParamCount, index);
}
catch (ScriptException& exc)
{
throw exc;
}
break;
case OP_EXEC_CMD_METHOD0:
iParamCount = 0;
goto __execCmdMethod;
case OP_EXEC_CMD_METHOD1:
iParamCount = 1;
goto __execCmdMethod;
case OP_EXEC_CMD_METHOD2:
iParamCount = 2;
goto __execCmdMethod;
case OP_EXEC_CMD_METHOD3:
iParamCount = 3;
goto __execCmdMethod;
case OP_EXEC_CMD_METHOD4:
iParamCount = 4;
goto __execCmdMethod;
case OP_EXEC_CMD_METHOD5:
iParamCount = 5;
goto __execCmdMethod;
__execCmdMethod:
m_CodePos--;
goto __execCmdMethodInternal;
case OP_EXEC_CMD_METHOD_COUNT1:
iParamCount = *m_CodePos;
__execCmdMethodInternal:
a = pTop--;
try
{
index = *reinterpret_cast<unsigned int*>(m_CodePos + sizeof(byte));
pTop -= iParamCount;
if (a->arraysize() < 0)
{
ScriptError("command '%s' applied to NIL", Event::GetEventName(index).c_str());
}
ScriptVariable array = *a;
Listener* listener;
array.CastConstArrayValue();
for (int i = array.arraysize(); i > 0; i--)
{
if (!(listener = array[i]->listenerValue()))
{
ScriptError("command '%s' applied to NULL listener", Event::GetEventName(index).c_str());
}
executeCommand(listener, iParamCount, index);
}
}
catch (ScriptException& exc)
{
m_CodePos += sizeof(byte) + sizeof(unsigned int);
throw exc;
}
m_CodePos += sizeof(byte) + sizeof(unsigned int);
break;
case OP_EXEC_METHOD0:
iParamCount = 0;
goto __execMethod;
case OP_EXEC_METHOD1:
iParamCount = 1;
goto __execMethod;
case OP_EXEC_METHOD2:
iParamCount = 2;
goto __execMethod;
case OP_EXEC_METHOD3:
iParamCount = 3;
goto __execMethod;
case OP_EXEC_METHOD4:
iParamCount = 4;
goto __execMethod;
case OP_EXEC_METHOD5:
iParamCount = 5;
__execMethod:
m_CodePos--;
goto __execMethodInternal;
case OP_EXEC_METHOD_COUNT1:
iParamCount = *m_CodePos;
__execMethodInternal:
a = pTop--;
try
{
index = *reinterpret_cast<unsigned int*>(m_CodePos + sizeof(byte));
pTop -= iParamCount;
pTop++; // push the return value
Listener* listener = a->listenerValue();
if (!listener)
{
ScriptError("command '%s' applied to NULL listener", Event::GetEventName(index).c_str());
}
executeCommand(listener, iParamCount, index, true);
}
catch (ScriptException& exc)
{
m_CodePos += sizeof(byte) + sizeof(unsigned int);
throw exc;
}
m_CodePos += sizeof(byte) + sizeof(unsigned int);
break;
case OP_FUNC:
ev.Clear();
if (!*m_CodePos++)
{
str& label = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
m_CodePos += sizeof(unsigned int);
try
{
listener = pTop->listenerValue();
if (!listener)
{
ScriptError("function '%s' applied to NULL listener", label.c_str());
}
}
catch (ScriptException& exc)
{
pTop -= *m_CodePos++;
throw exc;
}
pTop--;
ev.AddString(label);
int params = *m_CodePos++;
var = pTop;
pTop -= params;
for (int i = 0; i < params; var++, i++) {
ev.AddValue(*var);
}
pTop++;
EnterFunction(&ev);
m_ScriptClass->m_Self = listener;
}
else
{
str filename, label;
filename = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
m_CodePos += sizeof(unsigned int);
label = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
m_CodePos += sizeof(unsigned int);
try
{
listener = pTop->listenerValue();
if (!listener)
{
ScriptError("function '%s' in '%s' applied to NULL listener", label.c_str(), filename.c_str());
}
}
catch (ScriptException& exc)
{
pTop -= *m_CodePos++;
throw exc;
}
pTop--;
ScriptVariable constarray;
ScriptVariable* pVar = new ScriptVariable[2];
pVar[0].setStringValue(filename);
pVar[1].setStringValue(label);
constarray.setConstArrayValue(pVar, 2);
delete[] pVar;
ev2 = new Event(EV_Listener_WaitCreateReturnThread);
ev2->AddValue(constarray);
int params = *m_CodePos++;
var = pTop;
pTop -= params;
for (int i = 0; i < params; var++, i++) {
ev2->AddValue(*var);
}
pTop++;
*pTop = listener->ProcessEventReturn(ev2);
}
break;
case OP_JUMP4:
m_CodePos += *reinterpret_cast<int*>(m_CodePos) + sizeof(unsigned int);
break;
case OP_JUMP_BACK4:
m_CodePos -= *reinterpret_cast<int*>(m_CodePos);
break;
case OP_LOAD_ARRAY_VAR:
a = pTop--;
b = pTop--;
c = pTop--;
b->setArrayAt(*a, *c);
break;
case OP_LOAD_FIELD_VAR:
a = pTop--;
try
{
listener = a->listenerValue();
if (listener == NULL)
{
value = Director.GetString(*reinterpret_cast<int*>(m_CodePos));
ScriptError("Field '%s' applied to NULL listener", value.c_str());
}
else
{
eventCalled = true;
loadTop(listener);
}
}
catch (ScriptException& exc)
{
pTop--;
if (!eventCalled) {
m_CodePos += sizeof(unsigned int);
}
throw exc;
}
break;
case OP_LOAD_CONST_ARRAY1:
index = *reinterpret_cast<short*>(m_CodePos);
m_CodePos += sizeof(short);
pTop -= index - 1;
pTop->setConstArrayValue(pTop, index);
break;
case OP_LOAD_GAME_VAR:
loadTop(&game);
break;
case OP_LOAD_GROUP_VAR:
loadTop(m_ScriptClass);
break;
case OP_LOAD_LEVEL_VAR:
loadTop(&level);
break;
case OP_LOAD_LOCAL_VAR:
loadTop(m_Thread);
break;
case OP_LOAD_OWNER_VAR:
if (!m_ScriptClass->m_Self)
{
pTop--;
m_CodePos += sizeof(unsigned int);
ScriptError("self is NULL");
}
if (!m_ScriptClass->m_Self->GetScriptOwner())
{
pTop--;
m_CodePos += sizeof(unsigned int);
ScriptError("self.owner is NULL");
}
loadTop(m_ScriptClass->m_Self->GetScriptOwner());
break;
case OP_LOAD_PARM_VAR:
loadTop(&parm);
break;
case OP_LOAD_SELF_VAR:
if (!m_ScriptClass->m_Self)
{
pTop--;
m_CodePos += sizeof(unsigned int);
ScriptError("self is NULL");
}
loadTop(m_ScriptClass->m_Self);
break;
case OP_LOAD_STORE_GAME_VAR:
loadTop(&game, true);
break;
case OP_LOAD_STORE_GROUP_VAR:
loadTop(m_ScriptClass, true);
break;
case OP_LOAD_STORE_LEVEL_VAR:
loadTop(&level, true);
break;
case OP_LOAD_STORE_LOCAL_VAR:
loadTop(m_Thread, true);
break;
case OP_LOAD_STORE_OWNER_VAR:
if (!m_ScriptClass->m_Self)
{
m_CodePos += sizeof(unsigned int);
ScriptError("self is NULL");
}
if (!m_ScriptClass->m_Self->GetScriptOwner())
{
m_CodePos += sizeof(unsigned int);
ScriptError("self.owner is NULL");
}
loadTop(m_ScriptClass->m_Self->GetScriptOwner(), true);
break;
case OP_LOAD_STORE_PARM_VAR:
loadTop(&parm, true);
break;
case OP_LOAD_STORE_SELF_VAR:
if (!m_ScriptClass->m_Self)
{
ScriptError("self is NULL");
}
loadTop(m_ScriptClass->m_Self, true);
break;
case OP_MARK_STACK_POS:
m_StackPos = pTop;
m_bMarkStack = true;
break;
case OP_STORE_PARAM:
if (fastEvent.dataSize)
{
pTop = fastEvent.data++;
fastEvent.dataSize--;
}
else
{
pTop = m_StackPos + 1;
pTop->Clear();
}
break;
case OP_RESTORE_STACK_POS:
pTop = m_StackPos;
m_bMarkStack = false;
break;
case OP_STORE_ARRAY:
pTop--;
pTop->evalArrayAt(*(pTop + 1));
break;
case OP_STORE_ARRAY_REF:
pTop--;
pTop->setArrayRefValue(*(pTop + 1));
break;
case OP_STORE_FIELD_REF:
case OP_STORE_FIELD:
try
{
value = Director.GetString(*reinterpret_cast<int*>(m_CodePos));
listener = pTop->listenerValue();
if (listener == NULL)
{
ScriptError("Field '%s' applied to NULL listener", value.c_str());
}
else
{
eventCalled = true;
storeTop(listener, true);
}
if (*opcode == OP_STORE_FIELD_REF)
{
pTop->setRefValue(listener->vars->GetOrCreateVariable(value));
}
}
catch (ScriptException& exc)
{
if (*opcode == OP_STORE_FIELD_REF)
{
pTop->setRefValue(pTop);
}
if (!eventCalled) {
m_CodePos += sizeof(unsigned int);
}
throw exc;
}
break;
case OP_STORE_FLOAT:
pTop++;
pTop->setFloatValue(*reinterpret_cast<float*>(m_CodePos));
m_CodePos += sizeof(float);
break;
case OP_STORE_INT0:
pTop++;
pTop->setIntValue(0);
break;
case OP_STORE_INT1:
pTop++;
pTop->setIntValue(*m_CodePos++);
break;
case OP_STORE_INT2:
pTop++;
pTop->setIntValue(*reinterpret_cast<short*>(m_CodePos));
m_CodePos += sizeof(short);
break;
case OP_STORE_INT3:
pTop++;
pTop->setIntValue(*reinterpret_cast<short3*>(m_CodePos));
m_CodePos += sizeof(short3);
break;
case OP_STORE_INT4:
pTop++;
pTop->setIntValue(*reinterpret_cast<int*>(m_CodePos));
m_CodePos += sizeof(int);
break;
case OP_STORE_GAME_VAR:
storeTop(&game);
break;
case OP_STORE_GROUP_VAR:
storeTop(m_ScriptClass);
break;
case OP_STORE_LEVEL_VAR:
storeTop(&level);
break;
case OP_STORE_LOCAL_VAR:
storeTop(m_Thread);
break;
case OP_STORE_OWNER_VAR:
if (!m_ScriptClass->m_Self)
{
pTop++;
m_CodePos += sizeof(unsigned int);
ScriptError("self is NULL");
}
if (!m_ScriptClass->m_Self->GetScriptOwner())
{
pTop++;
m_CodePos += sizeof(unsigned int);
ScriptError("self.owner is NULL");
}
storeTop(m_ScriptClass->m_Self->GetScriptOwner());
break;
case OP_STORE_PARM_VAR:
storeTop(&parm);
break;
case OP_STORE_SELF_VAR:
if (!m_ScriptClass->m_Self)
{
pTop++;
m_CodePos += sizeof(unsigned int);
ScriptError("self is NULL");
}
storeTop(m_ScriptClass->m_Self);
break;
case OP_STORE_GAME:
pTop++;
pTop->setListenerValue(&game);
break;
case OP_STORE_GROUP:
pTop++;
pTop->setListenerValue(m_ScriptClass);
break;
case OP_STORE_LEVEL:
pTop++;
pTop->setListenerValue(&level);
break;
case OP_STORE_LOCAL:
pTop++;
pTop->setListenerValue(m_Thread);
break;
case OP_STORE_OWNER:
pTop++;
if (!m_ScriptClass->m_Self)
{
pTop++;
ScriptError("self is NULL");
}
pTop->setListenerValue(m_ScriptClass->m_Self->GetScriptOwner());
break;
case OP_STORE_PARM:
pTop++;
pTop->setListenerValue(&parm);
break;
case OP_STORE_SELF:
pTop++;
pTop->setListenerValue(m_ScriptClass->m_Self);
break;
case OP_STORE_NIL:
pTop++;
pTop->Clear();
break;
case OP_STORE_NULL:
pTop++;
pTop->setListenerValue(NULL);
break;
case OP_STORE_STRING:
pTop++;
pTop->setConstStringValue(*reinterpret_cast<unsigned int*>(m_CodePos));
m_CodePos += sizeof(unsigned int);
break;
case OP_STORE_VECTOR:
pTop++;
pTop->setVectorValue(*reinterpret_cast<Vector*>(m_CodePos));
m_CodePos += sizeof(Vector);
break;
case OP_SWITCH:
if (!Switch(*reinterpret_cast<StateScript**>(m_CodePos), *pTop))
{
m_CodePos += sizeof(StateScript*);
}
pTop--;
break;
case OP_UN_CAST_BOOLEAN:
pTop->CastBoolean();
break;
case OP_UN_COMPLEMENT:
pTop->complement();
break;
case OP_UN_MINUS:
pTop->minus();
break;
case OP_UN_DEC:
(*pTop)--;
break;
case OP_UN_INC:
(*pTop)++;
break;
case OP_UN_SIZE:
pTop->setIntValue((int)pTop->size());
break;
case OP_UN_TARGETNAME:
targetList = world->GetExistingTargetList(pTop->stringValue());
if (!targetList)
{
value = pTop->stringValue();
pTop->setListenerValue(NULL);
if (*m_CodePos >= OP_BIN_EQUALITY && *m_CodePos <= OP_BIN_GREATER_THAN_OR_EQUAL || *m_CodePos >= OP_BOOL_UN_NOT && *m_CodePos <= OP_UN_CAST_BOOLEAN) {
ScriptError("Targetname '%s' does not exist.", value.c_str());
}
break;
}
if (targetList->NumObjects() == 1)
{
pTop->setListenerValue(targetList->ObjectAt(1));
}
else if (targetList->NumObjects() > 1)
{
pTop->setContainerValue((Container< SafePtr< Listener > > *)targetList);
}
else
{
value = pTop->stringValue();
pTop->setListenerValue(NULL);
if (*m_CodePos >= OP_BIN_EQUALITY && *m_CodePos <= OP_BIN_GREATER_THAN_OR_EQUAL || *m_CodePos >= OP_BOOL_UN_NOT && *m_CodePos <= OP_UN_CAST_BOOLEAN) {
ScriptError("Targetname '%s' does not exist.", value.c_str());
}
break;
}
break;
case OP_VAR_UN_NOT:
pTop->setIntValue(pTop->booleanValue());
break;
case OP_DONE:
End();
break;
case OP_NOP:
break;
default:
if (*opcode < OP_MAX)
{
glbs.DPrintf("unknown opcode %d ('%s')\n", *opcode, OpcodeName(*opcode));
}
else
{
glbs.DPrintf("unknown opcode %d\n", *opcode);
}
break;
}
}
catch (ScriptException& exc)
{
HandleScriptException(exc);
}
}
Director.stackCount--;
if (deleteThread || state == STATE_WAITING)
{
delete m_Thread;
}
else if (state == STATE_SUSPENDED)
{
state = STATE_EXECUTION;
}
if (state == STATE_DESTROYED)
{
delete this;
}
}
/*
====================
HandleScriptException
====================
*/
void ScriptVM::HandleScriptException(ScriptException& exc)
{
if (m_ScriptClass)
{
m_ScriptClass->GetScript()->PrintSourcePos(m_PrevCodePos, true);
}
else
{
glbs.DPrintf("unknown source pos");
}
if (exc.bAbort)
{
ScriptException e(exc.string);
e.bAbort = exc.bAbort;
e.bIsForAnim = exc.bIsForAnim;
state = STATE_EXECUTION;
throw e;
}
glbs.Printf("^~^~^ Script Error : %s\n\n", exc.string.c_str());
if (m_ScriptClass->GetScript()->ScriptCheck())
{
if (g_scriptcheck->integer != 2 || !exc.bIsForAnim)
{
ScriptException e("Script check failed");
e.bAbort = exc.bAbort;
e.bIsForAnim = exc.bIsForAnim;
state = STATE_EXECUTION;
throw e;
}
}
Director.cmdCount += 100;
}
/*
====================
SetFastData
Sets the starting virtual machine parameters
====================
*/
void ScriptVM::SetFastData(ScriptVariable* data, int dataSize)
{
if (fastEvent.data)
{
fastEvent.data = m_pOldData;
fastEvent.dataSize = m_OldDataSize;
fastEvent.Clear();
m_pOldData = NULL;
m_OldDataSize = 0;
}
if (dataSize)
{
fastEvent.data = new ScriptVariable[dataSize];
fastEvent.dataSize = dataSize;
for (int i = 0; i < dataSize; i++)
{
fastEvent.data[i] = data[i];
}
m_pOldData = fastEvent.data;
m_OldDataSize = fastEvent.dataSize;
}
}
/*
====================
NotifyDelete
====================
*/
void ScriptVM::NotifyDelete(void)
{
switch (state)
{
case STATE_DESTROYED:
ScriptError("Attempting to delete a dead thread.");
break;
case STATE_RUNNING:
case STATE_SUSPENDED:
case STATE_WAITING:
state = STATE_DESTROYED;
if (m_ScriptClass) {
m_ScriptClass->RemoveThread(this);
}
break;
case STATE_EXECUTION:
state = STATE_DESTROYED;
if (m_ScriptClass) {
m_ScriptClass->RemoveThread(this);
}
delete this;
break;
}
}
/*
====================
Resume
====================
*/
void ScriptVM::Resume(qboolean bForce)
{
if (state == STATE_SUSPENDED || (bForce && state != STATE_DESTROYED)) {
state = STATE_RUNNING;
}
}
/*
====================
Suspend
====================
*/
void ScriptVM::Suspend()
{
if (state == STATE_DESTROYED) {
ScriptError("Cannot suspend a dead thread.");
}
else if (!state) {
state = STATE_SUSPENDED;
}
}
/*
====================
Switch
Switch statement
====================
*/
bool ScriptVM::Switch(StateScript* stateScript, ScriptVariable& var)
{
unsigned char* pos;
fastEvent.dataSize = 0;
pos = stateScript->FindLabel(var.stringValue());
if (!pos)
{
pos = stateScript->FindLabel("");
if (!pos) {
return false;
}
}
m_CodePos = pos;
return true;
}
/*
====================
Filename
====================
*/
str ScriptVM::Filename(void)
{
return m_ScriptClass->Filename();
}
/*
====================
Label
====================
*/
str ScriptVM::Label(void)
{
const_str label = m_ScriptClass->NearestLabel(m_CodePos);
if (!label)
{
return "";
}
return Director.GetString(label);
}
/*
====================
GetScriptClass
====================
*/
ScriptClass* ScriptVM::GetScriptClass(void)
{
return m_ScriptClass;
}
/*
====================
IsSuspended
====================
*/
bool ScriptVM::IsSuspended(void)
{
return state == STATE_SUSPENDED;
}
/*
====================
State
====================
*/
int ScriptVM::State(void)
{
return state;
}
/*
====================
ThreadState
====================
*/
int ScriptVM::ThreadState(void)
{
return m_ThreadState;
}
/*
====================
EventGoto
====================
*/
void ScriptVM::EventGoto(Event* ev)
{
str label = ev->GetString(1);
SetFastData(ev->data + 1, ev->dataSize - 1);
unsigned char* codePos = m_ScriptClass->FindLabel(label);
if (!codePos)
{
ScriptError("ScriptVM::EventGoto: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
}
m_CodePos = codePos;
}
/*
====================
EventThrow
Called when throwing an exception
====================
*/
bool ScriptVM::EventThrow(Event* ev)
{
str label = ev->GetString(1);
SetFastData(ev->data, ev->dataSize);
fastEvent.eventnum = ev->eventnum;
while (1)
{
StateScript* stateScript = m_ScriptClass->GetCatchStateScript(m_PrevCodePos, m_PrevCodePos);
if (!stateScript) {
break;
}
m_CodePos = stateScript->FindLabel(label);
if (m_CodePos)
{
fastEvent.data++;
fastEvent.dataSize--;
return true;
}
}
return false;
}
bool ScriptVM::CanScriptTracePrint
(
void
)
{
if (g_scripttrace->integer < 1 || g_scripttrace->integer > 4)
{
return false;
}
if (g_scripttrace->integer <= 2)
{
return true;
}
if (!m_ScriptClass)
{
return false;
}
if (!*g_monitor->string || !m_ScriptClass->m_Self || !m_ScriptClass->m_Self->isInheritedBy(&SimpleEntity::ClassInfo) || ((SimpleEntity*)m_ScriptClass->m_Self.Pointer())->targetname != g_monitor->string)
{
if (g_monitorNum->integer >= 0)
{
if (m_ScriptClass->m_Self && m_ScriptClass->m_Self->isInheritedBy(&Entity::ClassInfo) && ((Entity*)m_ScriptClass->m_Self.Pointer())->entnum == g_monitorNum->integer)
{
return true;
}
}
return false;
}
return true;
}