/* =========================================================================== 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" #include "scriptvm.h" #include "scriptcompiler.h" #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_allocator; /* ==================== 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(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(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(m_CodePos) + sizeof(unsigned int), !pTop->m_data.intValue); pTop--; break; case OP_BOOL_JUMP_TRUE4: jumpBool(*reinterpret_cast(m_CodePos) + sizeof(unsigned int), pTop->m_data.intValue ? true : false); pTop--; break; case OP_VAR_JUMP_FALSE4: jumpBool(*reinterpret_cast(m_CodePos) + sizeof(unsigned int), !pTop->booleanValue()); pTop--; break; case OP_VAR_JUMP_TRUE4: jumpBool(*reinterpret_cast(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(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(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(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(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(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(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(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(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(m_CodePos)); m_CodePos += sizeof(unsigned int); label = Director.GetString(*reinterpret_cast(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(m_CodePos) + sizeof(unsigned int); break; case OP_JUMP_BACK4: m_CodePos -= *reinterpret_cast(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(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(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(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(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(m_CodePos)); m_CodePos += sizeof(short); break; case OP_STORE_INT3: pTop++; pTop->setIntValue(*reinterpret_cast(m_CodePos)); m_CodePos += sizeof(short3); break; case OP_STORE_INT4: pTop++; pTop->setIntValue(*reinterpret_cast(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(m_CodePos)); m_CodePos += sizeof(unsigned int); break; case OP_STORE_VECTOR: pTop++; pTop->setVectorValue(*reinterpret_cast(m_CodePos)); m_CodePos += sizeof(Vector); break; case OP_SWITCH: if (!Switch(*reinterpret_cast(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; }