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
|
|
|
|
|
2023-06-17 01:24:20 +02:00
|
|
|
#include "../fgame/g_local.h"
|
|
|
|
#include "../fgame/scriptmaster.h"
|
|
|
|
#include "../fgame/scriptthread.h"
|
2023-01-30 18:20:50 +01:00
|
|
|
#include "scriptclass.h"
|
2023-01-29 20:59:31 +01:00
|
|
|
#include "scriptvm.h"
|
2023-01-30 18:20:50 +01:00
|
|
|
#include "scriptcompiler.h"
|
2023-04-29 21:56:38 +02:00
|
|
|
#include "scriptexception.h"
|
2023-06-17 01:24:20 +02:00
|
|
|
#include "../fgame/game.h"
|
|
|
|
#include "../fgame/level.h"
|
|
|
|
#include "../fgame/parm.h"
|
|
|
|
#include "../fgame/world.h"
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-02-07 20:50:09 +01:00
|
|
|
#include <utility>
|
|
|
|
|
2023-01-29 20:59:31 +01:00
|
|
|
#ifdef CGAME_DLL
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
# define VM_Printf cgi.Printf
|
|
|
|
# define VM_DPrintf cgi.DPrintf
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
#elif defined GAME_DLL
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
# define VM_Printf gi.Printf
|
|
|
|
# define VM_DPrintf gi.DPrintf2
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
# define VM_Printf printf
|
|
|
|
# define VM_DPrintf printf
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
class ScriptCommandEvent : public Event
|
|
|
|
{
|
|
|
|
public:
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptCommandEvent(unsigned int eventNum);
|
|
|
|
ScriptCommandEvent(unsigned int eventNum, size_t numArgs);
|
2023-02-02 23:48:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
ScriptCommandEvent::ScriptCommandEvent(unsigned int eventNum)
|
2023-07-05 21:23:39 +02:00
|
|
|
: Event(eventNum)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
fromScript = true;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptCommandEvent::ScriptCommandEvent(unsigned int eventNum, size_t numArgs)
|
2023-08-13 03:34:19 +02:00
|
|
|
: Event(eventNum)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-08-13 03:34:19 +02:00
|
|
|
fromScript = true;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVMStack::ScriptVMStack()
|
2023-07-05 21:23:39 +02:00
|
|
|
: localStack(nullptr)
|
|
|
|
, stackBottom(nullptr)
|
|
|
|
, pTop(nullptr)
|
|
|
|
{}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
|
|
|
ScriptVMStack::ScriptVMStack(size_t stackSize)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!stackSize) {
|
|
|
|
stackSize = 1;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
// allocate at once
|
|
|
|
uint8_t *data = (uint8_t *)gi.Malloc((sizeof(ScriptVariable) + sizeof(ScriptVariable *)) * stackSize);
|
|
|
|
localStack = new (data) ScriptVariable[stackSize];
|
|
|
|
data += sizeof(ScriptVariable) * stackSize;
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
listenerVarPtr = new (data) ScriptVariable *[stackSize]();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
pTop = localStack;
|
|
|
|
stackBottom = localStack + stackSize;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVMStack::ScriptVMStack(ScriptVMStack&& other)
|
2023-07-05 21:23:39 +02:00
|
|
|
: localStack(other.localStack)
|
|
|
|
, stackBottom(other.stackBottom)
|
|
|
|
, pTop(other.pTop)
|
|
|
|
, listenerVarPtr(other.listenerVarPtr)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
other.localStack = other.stackBottom = nullptr;
|
|
|
|
other.pTop = nullptr;
|
|
|
|
other.listenerVarPtr = nullptr;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVMStack& ScriptVMStack::operator=(ScriptVMStack&& other)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
localStack = other.localStack;
|
|
|
|
stackBottom = other.stackBottom;
|
|
|
|
pTop = other.pTop;
|
|
|
|
listenerVarPtr = other.listenerVarPtr;
|
|
|
|
other.localStack = other.stackBottom = nullptr;
|
|
|
|
other.pTop = nullptr;
|
|
|
|
other.listenerVarPtr = nullptr;
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return *this;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVMStack::~ScriptVMStack()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const size_t localStackSize = GetStackSize();
|
|
|
|
for (uintptr_t i = 0; i < localStackSize; ++i) {
|
|
|
|
localStack[i].~ScriptVariable();
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
uint8_t *const data = (uint8_t *)localStack;
|
|
|
|
if (data) {
|
|
|
|
gi.Free(data);
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t ScriptVMStack::GetStackSize() const
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return stackBottom - localStack;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::SetTop(ScriptVariable& newTop)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *(pTop = &newTop);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::GetTop() const
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::GetTop(size_t offset) const
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *(pTop + offset);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVMStack::GetTopPtr() const
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVMStack::GetTopPtr(size_t offset) const
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return pTop + offset;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVMStack::GetTopArray(size_t offset) const
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return pTop + offset;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t ScriptVMStack::GetIndex() const
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return pTop - localStack;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::Pop()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *(pTop--);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::Pop(size_t offset)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable& old = *pTop;
|
|
|
|
pTop -= offset;
|
|
|
|
return old;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::PopAndGet()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *--pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::PopAndGet(size_t offset)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
pTop -= offset;
|
|
|
|
return *pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::Push()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *(pTop++);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::Push(size_t offset)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable& old = *pTop;
|
|
|
|
pTop += offset;
|
|
|
|
return old;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::PushAndGet()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return *++pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable& ScriptVMStack::PushAndGet(size_t offset)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
pTop += offset;
|
|
|
|
return *pTop;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptVMStack::MoveTop(ScriptVariable&& other)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
*pTop = std::move(other);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVMStack::GetListenerVar(uintptr_t index)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return listenerVarPtr[index];
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVMStack::SetListenerVar(uintptr_t index, ScriptVariable *newVar)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
listenerVarPtr[index] = newVar;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-01-29 20:59:31 +01:00
|
|
|
//====================
|
|
|
|
// ScriptVM
|
|
|
|
//====================
|
|
|
|
|
2023-01-30 17:11:44 +01:00
|
|
|
MEM_BlockAlloc<ScriptVM> ScriptVM_allocator;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
new ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void *ScriptVM::operator new(size_t size)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return ScriptVM_allocator.Alloc();
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
delete ptr
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::operator delete(void *ptr)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVM_allocator.Free(ptr);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVM::ScriptVM(ScriptClass *scriptClass, unsigned char *pCodePos, ScriptThread *thread)
|
|
|
|
: m_VMStack(scriptClass->GetScript()->GetRequiredStackSize())
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
next = NULL;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_Thread = thread;
|
|
|
|
m_ScriptClass = scriptClass;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_Stack = NULL;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_PrevCodePos = NULL;
|
|
|
|
m_CodePos = pCodePos;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
state = STATE_RUNNING;
|
|
|
|
m_ThreadState = THREAD_RUNNING;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_pOldData = NULL;
|
|
|
|
m_OldDataSize = 0;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_bMarkStack = false;
|
|
|
|
m_StackPos = NULL;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_ScriptClass->AddThread(this);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
~ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptVM::~ScriptVM()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
fastEvent.data = m_pOldData;
|
|
|
|
fastEvent.dataSize = m_OldDataSize;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
// clean-up the call stack
|
|
|
|
while (callStack.NumObjects()) {
|
|
|
|
LeaveFunction();
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Archive
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Archive(Archiver& arc)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
int stack = 0;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (arc.Saving()) {
|
|
|
|
if (m_Stack) {
|
|
|
|
stack = m_Stack->m_Count;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
arc.ArchiveInteger(&stack);
|
|
|
|
} else {
|
|
|
|
arc.ArchiveInteger(&stack);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (stack) {
|
|
|
|
m_Stack = new ScriptStack;
|
|
|
|
m_Stack->m_Array = new ScriptVariable[stack];
|
|
|
|
m_Stack->m_Count = stack;
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
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);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
error
|
|
|
|
|
|
|
|
Triggers an error
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::error(const char *format, ...)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
char buffer[4000];
|
|
|
|
va_list va;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
va_start(va, format);
|
|
|
|
vsprintf(buffer, format, va);
|
|
|
|
va_end(va);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
glbs.Printf("----------------------------------------------------------\n%s\n", buffer);
|
|
|
|
m_ReturnValue.setStringValue("$.INTERRUPTED");
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
void ScriptVM::jump(unsigned int offset)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-08-13 17:59:49 +02:00
|
|
|
m_CodePos += offset;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
void ScriptVM::jumpBack(unsigned int offset)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-08-13 17:59:49 +02:00
|
|
|
m_CodePos -= offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptVM::jumpBool(unsigned int offset, bool booleanValue)
|
|
|
|
{
|
|
|
|
if (booleanValue)
|
|
|
|
{
|
|
|
|
jump(offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScriptVM::jumpVar(unsigned int offset, bool booleanValue)
|
|
|
|
{
|
|
|
|
if (booleanValue)
|
|
|
|
{
|
|
|
|
jump(offset);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_VMStack.Pop();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptVM::doJumpIf(bool booleanValue)
|
|
|
|
{
|
|
|
|
const unsigned int offset = fetchOpcodeValue<unsigned int>();
|
|
|
|
jumpBool(offset, booleanValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScriptVM::doJumpVarIf(bool booleanValue)
|
|
|
|
{
|
|
|
|
const unsigned int offset = fetchOpcodeValue<unsigned int>();
|
|
|
|
return jumpVar(offset, booleanValue);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::loadTopInternal(Listener *listener)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const const_str variable = fetchOpcodeValue<op_name_t>();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!executeSetter(listener, variable)) {
|
|
|
|
// just set the variable
|
|
|
|
const uintptr_t varIndex = m_VMStack.GetIndex();
|
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
if (varIndex < m_VMStack.GetStackSize()) {
|
|
|
|
ScriptVariable *const listenerVar = m_VMStack.GetListenerVar(varIndex);
|
|
|
|
if (!listenerVar || listenerVar->GetKey() != short3(variable)) {
|
|
|
|
listener->Vars()->SetVariable(variable, std::move(pTop));
|
|
|
|
} else {
|
|
|
|
*listenerVar = std::move(pTop);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
listener->Vars()->SetVariable(variable, std::move(pTop));
|
|
|
|
}
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVM::storeTopInternal(Listener *listener)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const const_str variable = fetchOpcodeValue<op_name_t>();
|
|
|
|
ScriptVariable *listenerVar;
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!executeGetter(listener, variable)) {
|
|
|
|
const uintptr_t varIndex = m_VMStack.GetIndex();
|
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
listenerVar = m_VMStack.GetListenerVar(varIndex);
|
|
|
|
if (!listenerVar || listenerVar->GetKey() != short3(variable)) {
|
|
|
|
listenerVar = listener->Vars()->GetOrCreateVariable(variable);
|
|
|
|
m_VMStack.SetListenerVar(varIndex, listenerVar);
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop = *listenerVar;
|
|
|
|
} else {
|
|
|
|
listenerVar = nullptr;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return listenerVar;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-08-13 20:17:59 +02:00
|
|
|
void ScriptVM::loadStoreTop(Listener* listener)
|
|
|
|
{
|
|
|
|
const const_str variable = fetchOpcodeValue<op_name_t>();
|
|
|
|
|
|
|
|
if (!executeSetter(listener, variable)) {
|
|
|
|
// just set the variable
|
|
|
|
const uintptr_t varIndex = m_VMStack.GetIndex();
|
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
if (varIndex < m_VMStack.GetStackSize()) {
|
|
|
|
ScriptVariable* const listenerVar = m_VMStack.GetListenerVar(varIndex);
|
|
|
|
if (!listenerVar || listenerVar->GetKey() != short3(variable)) {
|
|
|
|
listener->Vars()->SetVariable(variable, pTop);
|
|
|
|
} else {
|
|
|
|
*listenerVar = pTop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
listener->Vars()->SetVariable(variable, pTop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 16:07:38 +02:00
|
|
|
void ScriptVM::skipField()
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:50:09 +01:00
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::loadTop<false>(Listener *listener)
|
2023-02-07 20:50:09 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
loadTopInternal(listener);
|
|
|
|
m_VMStack.Pop();
|
2023-02-07 20:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::loadTop<true>(Listener *listener)
|
2023-02-07 20:50:09 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
loadTopInternal(listener);
|
2023-02-07 20:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVM::storeTop<false>(Listener *listener)
|
2023-02-07 20:50:09 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Push();
|
2023-02-07 20:50:09 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return storeTopInternal(listener);
|
2023-02-07 20:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *ScriptVM::storeTop<true>(Listener *listener)
|
2023-02-07 20:50:09 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return storeTopInternal(listener);
|
2023-02-07 20:50:09 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommandInternal<false>(
|
|
|
|
Event& ev, Listener *listener, ScriptVariable *fromVar, op_parmNum_t iParamCount
|
|
|
|
)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
transferVarsToEvent(ev, fromVar, iParamCount);
|
|
|
|
listener->ProcessScriptEvent(ev);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommandInternal<true>(
|
|
|
|
Event& ev, Listener *listener, ScriptVariable *fromVar, op_parmNum_t iParamCount
|
|
|
|
)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
transferVarsToEvent(ev, fromVar, iParamCount);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
try {
|
|
|
|
listener->ProcessScriptEvent(ev);
|
|
|
|
} catch (...) {
|
|
|
|
m_VMStack.GetTop().Clear();
|
|
|
|
throw;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
if (ev.NumArgs() > iParamCount) {
|
|
|
|
pTop = std::move(ev.GetLastValue());
|
|
|
|
} else {
|
|
|
|
pTop.Clear();
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommand<false, false>(Listener *listener, op_parmNum_t iParamCount, op_evName_t eventnum)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptCommandEvent ev = iParamCount ? ScriptCommandEvent(eventnum, iParamCount) : ScriptCommandEvent(eventnum);
|
|
|
|
return executeCommandInternal<false>(ev, listener, m_VMStack.GetTopArray(1), iParamCount);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommand<true, false>(Listener *listener, op_parmNum_t iParamCount, op_evName_t eventnum)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptCommandEvent ev = iParamCount ? ScriptCommandEvent(eventnum, iParamCount) : ScriptCommandEvent(eventnum);
|
|
|
|
return executeCommandInternal<false>(ev, listener, m_VMStack.GetTopArray(1), iParamCount);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommand<false, true>(Listener *listener, op_parmNum_t iParamCount, op_evName_t eventnum)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptCommandEvent ev =
|
|
|
|
iParamCount ? ScriptCommandEvent(eventnum, iParamCount + 1) : ScriptCommandEvent(eventnum, 1);
|
|
|
|
return executeCommandInternal<true>(ev, listener, m_VMStack.GetTopArray(), iParamCount);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::executeCommand<true, true>(Listener *listener, op_parmNum_t iParamCount, op_evName_t eventnum)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptCommandEvent ev =
|
|
|
|
iParamCount ? ScriptCommandEvent(eventnum, iParamCount + 1) : ScriptCommandEvent(eventnum, 1);
|
|
|
|
return executeCommandInternal<true>(ev, listener, m_VMStack.GetTopArray(), iParamCount);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::transferVarsToEvent(Event& ev, ScriptVariable *fromVar, op_parmNum_t count)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
for (uint16_t i = 0; i < count; i++) {
|
|
|
|
ev.AddValue(fromVar[i]);
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
bool ScriptVM::executeGetter(Listener *listener, op_evName_t eventName)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
int eventNum = Event::FindGetterEventNum(eventName);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (eventNum && listener->classinfo()->GetDef(eventNum)) {
|
|
|
|
ScriptCommandEvent ev(eventNum);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
listener->ProcessScriptEvent(ev);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
if (ev.NumArgs() > 0) {
|
|
|
|
pTop = std::move(ev.GetLastValue());
|
|
|
|
} else {
|
|
|
|
pTop.Clear();
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
eventNum = Event::FindSetterEventNum(eventName);
|
|
|
|
assert(!eventNum || !listener->classinfo()->GetDef(eventNum));
|
|
|
|
if (eventNum && listener->classinfo()->GetDef(eventNum)) {
|
|
|
|
ScriptError("Cannot set a read-only variable");
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return false;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
bool ScriptVM::executeSetter(Listener *listener, op_evName_t eventName)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
int eventNum = Event::FindSetterEventNum(eventName);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (eventNum && listener->classinfo()->GetDef(eventNum)) {
|
|
|
|
ScriptCommandEvent ev(eventNum, 1);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable& pTop = m_VMStack.GetTop();
|
|
|
|
ev.AddValue(pTop);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
listener->ProcessScriptEvent(ev);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
eventNum = Event::FindSetterEventNum(eventName);
|
|
|
|
assert(!eventNum || !listener->classinfo()->GetDef(eventNum));
|
|
|
|
if (eventNum && listener->classinfo()->GetDef(eventNum)) {
|
|
|
|
ScriptError("Cannot get a write-only variable");
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return false;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
void ScriptVM::execCmdCommon(op_parmNum_t param)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const op_ev_t eventNum = fetchOpcodeValue<op_ev_t>();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Pop(param);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
executeCommand(m_Thread, param, eventNum);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
void ScriptVM::execCmdMethodCommon(op_parmNum_t param)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-08-17 03:04:01 +02:00
|
|
|
const ScriptVariable& a = m_VMStack.Pop();
|
|
|
|
const op_ev_t eventNum = fetchOpcodeValue<op_ev_t>();
|
2023-07-05 21:23:39 +02:00
|
|
|
|
|
|
|
m_VMStack.Pop(param);
|
|
|
|
|
|
|
|
const size_t arraysize = a.arraysize();
|
|
|
|
if (arraysize == (size_t)-1) {
|
|
|
|
throw ScriptException("command '%s' applied to NIL", Event::GetEventName(eventNum).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arraysize > 1) {
|
2023-08-17 03:04:01 +02:00
|
|
|
if (a.IsConstArray()) {
|
|
|
|
// copy the variable
|
|
|
|
// because if it's a targetlist, the container object can be modified
|
|
|
|
// while iterating
|
|
|
|
ScriptVariable array = a;
|
2023-08-17 03:18:41 +02:00
|
|
|
array.CastConstArrayValue();
|
2023-08-17 03:04:01 +02:00
|
|
|
|
|
|
|
for (uintptr_t i = arraysize; i > 0; i--) {
|
|
|
|
Listener *const listener = array.listenerAt(i);
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
// if the listener is NULL, don't throw an exception
|
|
|
|
// it would be unfair if the other listeners executed the command
|
|
|
|
if (listener) {
|
|
|
|
executeCommand<true>(listener, param, eventNum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ScriptVariable array = a;
|
|
|
|
// must cast into a const array value
|
|
|
|
array.CastConstArrayValue();
|
|
|
|
|
|
|
|
for (uintptr_t i = array.arraysize(); i > 0; i--) {
|
|
|
|
Listener *const listener = array[i]->listenerAt(i);
|
|
|
|
if (listener) {
|
|
|
|
executeCommand<true>(listener, param, eventNum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// avoid useless allocations of const array
|
|
|
|
Listener *const listener = a.listenerValue();
|
|
|
|
if (!listener) {
|
|
|
|
throw ScriptException("command '%s' applied to NULL listener", Event::GetEventName(eventNum).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
executeCommand<true>(listener, param, eventNum);
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
void ScriptVM::execMethodCommon(op_parmNum_t param)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const ScriptVariable& a = m_VMStack.Pop();
|
|
|
|
const op_ev_t eventNum = fetchOpcodeValue<op_ev_t>();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Pop(param);
|
|
|
|
// push the return value
|
|
|
|
m_VMStack.Push();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Listener *const listener = a.listenerValue();
|
|
|
|
if (!listener) {
|
|
|
|
throw ScriptException("command '%s' applied to NULL listener", Event::GetEventName(eventNum).c_str());
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
executeCommand<true, true>(listener, param, eventNum);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
void ScriptVM::execFunction(ScriptMaster& Director)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!fetchOpcodeValue<bool>()) {
|
|
|
|
const op_name_t label = fetchOpcodeValue<op_name_t>();
|
|
|
|
const op_parmNum_t params = fetchOpcodeValue<op_parmNum_t>();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Listener *listener;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
try {
|
|
|
|
listener = m_VMStack.GetTop().listenerValue();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!listener) {
|
|
|
|
const str& labelName = Director.GetString(label);
|
|
|
|
throw ScriptException("function '" + labelName + "' applied to NULL listener");
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
m_VMStack.Pop(params);
|
|
|
|
throw;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Pop();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Container<ScriptVariable> data;
|
|
|
|
data.Resize(params + 1);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *labelVar = &data.ObjectAt(data.AddObject(ScriptVariable()));
|
|
|
|
labelVar->setConstStringValue(label);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
const ScriptVariable *var = &m_VMStack.Pop(params);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
for (int i = 0; i < params; var++, i++) {
|
|
|
|
data.AddObject(*var);
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Push();
|
|
|
|
EnterFunction(std::move(data));
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
GetScriptClass()->m_Self = listener;
|
|
|
|
} else {
|
|
|
|
const op_name_t filename = fetchOpcodeValue<op_name_t>();
|
|
|
|
const op_name_t label = fetchOpcodeValue<op_name_t>();
|
|
|
|
const op_parmNum_t params = fetchOpcodeValue<op_parmNum_t>();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Listener *listener;
|
|
|
|
try {
|
|
|
|
listener = m_VMStack.GetTop().listenerValue();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!listener) {
|
|
|
|
const str& labelStr = Director.GetString(label);
|
|
|
|
const str& fileStr = Director.GetString(filename);
|
|
|
|
throw ScriptException("function '" + labelStr + "' in '" + fileStr + "' applied to NULL listener");
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
m_VMStack.Pop(params);
|
|
|
|
throw;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Pop();
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable constarray;
|
|
|
|
ScriptVariable *pVar = new ScriptVariable[2];
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
pVar[0].setConstStringValue(filename);
|
|
|
|
pVar[1].setConstStringValue(label);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
constarray.setConstArrayValue(pVar, 2);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
delete[] pVar;
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Event ev(EV_Listener_WaitCreateReturnThread);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
const ScriptVariable *var = &m_VMStack.Pop(params);
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
for (int i = 0; i < params; var++, i++) {
|
|
|
|
ev.AddValue(*var);
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop() = listener->ProcessEventReturn(&ev);
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ProgBuffer
|
|
|
|
|
|
|
|
Returns the current program buffer
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
unsigned char *ScriptVM::ProgBuffer(void)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return m_CodePos;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EnterFunction
|
|
|
|
|
|
|
|
Sets a new instruction pointer
|
|
|
|
====================
|
|
|
|
*/
|
2023-02-02 23:48:51 +01:00
|
|
|
void ScriptVM::EnterFunction(Container<ScriptVariable>&&)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-02-02 23:48:51 +01:00
|
|
|
#if 0
|
2023-01-29 20:59:31 +01:00
|
|
|
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;
|
|
|
|
|
2023-02-02 23:48:51 +01:00
|
|
|
stack->pTop = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
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();
|
2023-02-02 23:48:51 +01:00
|
|
|
#endif
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
LeaveFunction
|
|
|
|
|
|
|
|
Returns to the previous function
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::LeaveFunction()
|
|
|
|
{
|
2023-02-02 23:48:51 +01:00
|
|
|
#if 0
|
2023-01-29 20:59:31 +01:00
|
|
|
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;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
#else
|
2023-07-05 21:23:39 +02:00
|
|
|
delete m_Thread;
|
2023-02-02 23:48:51 +01:00
|
|
|
#endif
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
End
|
|
|
|
|
|
|
|
End with a return value
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::End(const ScriptVariable& returnValue)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
m_ReturnValue.setPointer(returnValue);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
LeaveFunction();
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
End
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::End()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
m_ReturnValue.ClearPointer();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
LeaveFunction();
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Execute
|
|
|
|
|
|
|
|
Executes a program
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::Execute(ScriptVariable *data, int dataSize, str label)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
unsigned char *opcode;
|
|
|
|
bool doneProcessing = false;
|
|
|
|
bool deleteThread = false;
|
|
|
|
bool eventCalled = false;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptVariable *a;
|
|
|
|
ScriptVariable *b;
|
|
|
|
ScriptVariable *c;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
int index;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Listener *listener;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Event ev;
|
|
|
|
ScriptVariable *var = NULL;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
static str str_null = "";
|
|
|
|
str & value = str_null;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ConSimple *targetList;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (Director.stackCount >= MAX_STACK_DEPTH) {
|
|
|
|
state = STATE_EXECUTION;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptException::next_abort = -1;
|
|
|
|
ScriptException exc("stack overflow");
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
throw exc;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Director.stackCount++;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (dataSize) {
|
|
|
|
SetFastData(data, dataSize);
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
state = STATE_RUNNING;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Director.cmdTime = glbs.Milliseconds();
|
|
|
|
Director.cmdCount = 0;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
while (!doneProcessing && state == STATE_RUNNING) {
|
|
|
|
m_PrevCodePos = m_CodePos;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Director.cmdCount++;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
try {
|
|
|
|
if (Director.cmdCount > 9999 && glbs.Milliseconds() - Director.cmdTime > Director.maxTime) {
|
|
|
|
if (level.m_LoopProtection) {
|
|
|
|
Director.cmdTime = glbs.Milliseconds();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
deleteThread = true;
|
|
|
|
state = STATE_EXECUTION;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (level.m_LoopDrop) {
|
|
|
|
ScriptException::next_abort = -1;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptError("Command overflow. Possible infinite loop in thread.\n");
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
VM_DPrintf("Update of script position - This is not an error.\n");
|
|
|
|
VM_DPrintf("=================================================\n");
|
|
|
|
m_ScriptClass->GetScript()->PrintSourcePos(opcode, true);
|
|
|
|
VM_DPrintf("=================================================\n");
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Director.cmdCount = 0;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!m_bMarkStack) {
|
|
|
|
/*
|
2023-01-29 20:59:31 +01:00
|
|
|
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;
|
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
}
|
2023-08-13 17:59:49 +02:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
index = 0;
|
|
|
|
eventCalled = false;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
opcode = m_CodePos++;
|
|
|
|
switch (*opcode) {
|
|
|
|
case OP_BIN_BITWISE_AND:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
*b &= *a;
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_BITWISE_OR:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
*b |= *a;
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_BITWISE_EXCL_OR:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
*b ^= *a;
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_EQUALITY:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->setIntValue(*b == *a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_INEQUALITY:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->setIntValue(*b != *a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_GREATER_THAN:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->greaterthan(*a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_GREATER_THAN_OR_EQUAL:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->greaterthanorequal(*a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_LESS_THAN:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->lessthan(*a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_LESS_THAN_OR_EQUAL:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
b->lessthanorequal(*a);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_PLUS:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
*b += *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_MINUS:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
*b -= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_MULTIPLY:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
*b *= *a;
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_DIVIDE:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
*b /= *a;
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_BIN_PERCENTAGE:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
*b %= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_SHIFT_LEFT:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
*b <<= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_SHIFT_RIGHT:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
*b >>= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_JUMP_FALSE4:
|
2023-08-13 17:59:49 +02:00
|
|
|
doJumpIf(!m_VMStack.Pop().m_data.intValue);
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
case OP_BOOL_JUMP_TRUE4:
|
|
|
|
doJumpIf(m_VMStack.Pop().m_data.intValue);
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
case OP_VAR_JUMP_FALSE4:
|
|
|
|
doJumpIf(!m_VMStack.Pop().booleanValue());
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
case OP_VAR_JUMP_TRUE4:
|
|
|
|
doJumpIf(m_VMStack.Pop().booleanValue());
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_LOGICAL_AND:
|
2023-08-13 17:59:49 +02:00
|
|
|
doJumpVarIf(!m_VMStack.GetTop().m_data.intValue);
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
|
|
|
case OP_BOOL_LOGICAL_OR:
|
2023-08-13 17:59:49 +02:00
|
|
|
doJumpVarIf(m_VMStack.GetTop().m_data.intValue);
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
|
|
|
case OP_VAR_LOGICAL_AND:
|
2023-08-13 17:59:49 +02:00
|
|
|
if (!doJumpVarIf(m_VMStack.GetTop().booleanValue()))
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.GetTop().SetFalse();
|
2023-08-13 17:59:49 +02:00
|
|
|
}
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
|
|
|
case OP_VAR_LOGICAL_OR:
|
2023-08-13 17:59:49 +02:00
|
|
|
if (!doJumpVarIf(!m_VMStack.GetTop().booleanValue()))
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.GetTop().SetTrue();
|
2023-08-13 17:59:49 +02:00
|
|
|
}
|
|
|
|
break;
|
2023-07-05 21:23:39 +02:00
|
|
|
|
|
|
|
case OP_BOOL_STORE_FALSE:
|
|
|
|
m_VMStack.PushAndGet().SetFalse();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_STORE_TRUE:
|
|
|
|
m_VMStack.PushAndGet().SetTrue();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_UN_NOT:
|
|
|
|
m_VMStack.GetTop().m_data.intValue = (m_VMStack.GetTop().m_data.intValue == 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CALC_VECTOR:
|
|
|
|
c = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.Pop();
|
|
|
|
a = &m_VMStack.GetTop();
|
|
|
|
|
|
|
|
m_VMStack.GetTop().setVectorValue(Vector(a->floatValue(), b->floatValue(), c->floatValue()));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD0:
|
|
|
|
{
|
|
|
|
execCmdCommon(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD1:
|
|
|
|
{
|
|
|
|
execCmdCommon(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD2:
|
|
|
|
{
|
|
|
|
execCmdCommon(2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD3:
|
|
|
|
{
|
|
|
|
execCmdCommon(3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD4:
|
|
|
|
{
|
|
|
|
execCmdCommon(4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD5:
|
|
|
|
{
|
|
|
|
execCmdCommon(5);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_COUNT1:
|
|
|
|
{
|
|
|
|
const op_parmNum_t numParms = fetchOpcodeValue<op_parmNum_t>();
|
|
|
|
execCmdCommon(numParms);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD0:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD1:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD2:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD3:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD4:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD5:
|
|
|
|
{
|
|
|
|
execCmdMethodCommon(5);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD_COUNT1:
|
|
|
|
{
|
|
|
|
const op_parmNum_t numParms = fetchOpcodeValue<op_parmNum_t>();
|
|
|
|
execCmdMethodCommon(numParms);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD0:
|
|
|
|
{
|
|
|
|
execMethodCommon(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD1:
|
|
|
|
{
|
|
|
|
execMethodCommon(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD2:
|
|
|
|
{
|
|
|
|
execMethodCommon(2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD3:
|
|
|
|
{
|
|
|
|
execMethodCommon(3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD4:
|
|
|
|
{
|
|
|
|
execMethodCommon(4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD5:
|
|
|
|
{
|
|
|
|
execMethodCommon(5);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD_COUNT1:
|
|
|
|
{
|
|
|
|
const op_parmNum_t numParms = fetchOpcodeValue<op_parmNum_t>();
|
|
|
|
execMethodCommon(numParms);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OP_FUNC:
|
|
|
|
{
|
|
|
|
execFunction(Director);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
case OP_JUMP4:
|
|
|
|
jump(fetchOpcodeValue<unsigned int>());
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
2023-08-13 17:59:49 +02:00
|
|
|
case OP_JUMP_BACK4:
|
|
|
|
jumpBack(fetchActualOpcodeValue<unsigned int>());
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_ARRAY_VAR:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
b = &m_VMStack.Pop();
|
|
|
|
c = &m_VMStack.Pop();
|
|
|
|
|
|
|
|
b->setArrayAt(*a, *c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_FIELD_VAR:
|
|
|
|
a = &m_VMStack.Pop();
|
|
|
|
|
|
|
|
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) {
|
|
|
|
m_VMStack.Pop();
|
|
|
|
|
|
|
|
if (!eventCalled) {
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_CONST_ARRAY1:
|
|
|
|
{
|
|
|
|
op_arrayParmNum_t numParms = fetchOpcodeValue<op_arrayParmNum_t>();
|
|
|
|
|
|
|
|
ScriptVariable& pTop = m_VMStack.PopAndGet(numParms - 1);
|
|
|
|
pTop.setConstArrayValue(&pTop, numParms);
|
|
|
|
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) {
|
|
|
|
m_VMStack.Pop();
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self->GetScriptOwner()) {
|
|
|
|
m_VMStack.Pop();
|
|
|
|
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) {
|
|
|
|
m_VMStack.Pop();
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTop(m_ScriptClass->m_Self);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_GAME_VAR:
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(&game);
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_GROUP_VAR:
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(m_ScriptClass);
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_LEVEL_VAR:
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(&level);
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_LOCAL_VAR:
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(m_Thread);
|
2023-07-05 21:23:39 +02:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(m_ScriptClass->m_Self->GetScriptOwner());
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_PARM_VAR:
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(&parm);
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_SELF_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self) {
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
2023-08-13 20:17:59 +02:00
|
|
|
loadStoreTop(m_ScriptClass->m_Self);
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MARK_STACK_POS:
|
|
|
|
m_StackPos = &m_VMStack.GetTop();
|
|
|
|
m_bMarkStack = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_PARAM:
|
|
|
|
if (fastEvent.dataSize) {
|
|
|
|
m_VMStack.SetTop(*(fastEvent.data++));
|
|
|
|
fastEvent.dataSize--;
|
|
|
|
} else {
|
|
|
|
m_VMStack.SetTop(*(m_StackPos + 1));
|
|
|
|
m_VMStack.GetTop().Clear();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_RESTORE_STACK_POS:
|
|
|
|
m_VMStack.SetTop(*m_StackPos);
|
|
|
|
m_bMarkStack = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_ARRAY:
|
|
|
|
m_VMStack.Pop();
|
|
|
|
m_VMStack.GetTop().evalArrayAt(*(m_VMStack.GetTopPtr() + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_ARRAY_REF:
|
|
|
|
m_VMStack.Pop();
|
|
|
|
m_VMStack.GetTop().setArrayRefValue(*(m_VMStack.GetTopPtr() + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_FIELD_REF:
|
2023-08-17 16:07:38 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Listener* listener = m_VMStack.GetTop().listenerValue();
|
2023-07-05 21:23:39 +02:00
|
|
|
|
2023-08-17 16:07:38 +02:00
|
|
|
if (listener == nullptr)
|
|
|
|
{
|
|
|
|
const op_name_t fieldName = fetchActualOpcodeValue<op_name_t>();
|
|
|
|
skipField();
|
|
|
|
ScriptError("Field '%s' applied to NULL listener", value.c_str());
|
2023-07-05 21:23:39 +02:00
|
|
|
}
|
2023-08-17 16:07:38 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ScriptVariable* const listenerVar = storeTop<true>(listener);
|
|
|
|
|
|
|
|
if (listenerVar)
|
|
|
|
{
|
|
|
|
// having a listener variable means the variable was just created
|
|
|
|
m_VMStack.GetTop().setRefValue(listenerVar);
|
|
|
|
}
|
2023-07-05 21:23:39 +02:00
|
|
|
}
|
2023-08-17 16:07:38 +02:00
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
ScriptVariable* const pTop = m_VMStack.GetTopPtr();
|
|
|
|
pTop->setRefValue(pTop);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-07-05 21:23:39 +02:00
|
|
|
|
2023-08-17 16:07:38 +02:00
|
|
|
case OP_STORE_FIELD:
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Listener* listener = m_VMStack.GetTop().listenerValue();
|
2023-07-05 21:23:39 +02:00
|
|
|
|
2023-08-17 16:07:38 +02:00
|
|
|
if (listener == nullptr)
|
|
|
|
{
|
|
|
|
ScriptError("Field '%s' applied to NULL listener", value.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
storeTop<true>(listener);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
skipField();
|
|
|
|
throw;
|
2023-07-05 21:23:39 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_FLOAT:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setFloatValue(*reinterpret_cast<float *>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(float);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT0:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setIntValue(0);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT1:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setIntValue(*m_CodePos++);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT2:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setIntValue(*reinterpret_cast<short *>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(short);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT3:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setIntValue(*reinterpret_cast<short3 *>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(short3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT4:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().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) {
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self->GetScriptOwner()) {
|
|
|
|
m_VMStack.Push();
|
|
|
|
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) {
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
storeTop(m_ScriptClass->m_Self);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_STORE_GAME:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(&game);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_STORE_GROUP:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(m_ScriptClass);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_STORE_LEVEL:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(&level);
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_STORE_LOCAL:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(m_Thread);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_OWNER:
|
|
|
|
m_VMStack.Push();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!m_ScriptClass->m_Self) {
|
|
|
|
m_VMStack.Push();
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_VMStack.GetTop().setListenerValue(m_ScriptClass->m_Self->GetScriptOwner());
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case OP_STORE_PARM:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(&parm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_SELF:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(m_ScriptClass->m_Self);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_NIL:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().Clear();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_NULL:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setListenerValue(NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_STRING:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setConstStringValue(fetchOpcodeValue<unsigned int>());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_VECTOR:
|
|
|
|
m_VMStack.Push();
|
|
|
|
m_VMStack.GetTop().setVectorValue(fetchOpcodeValue<Vector>());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SWITCH:
|
|
|
|
if (!Switch(fetchActualOpcodeValue<StateScript *>(), m_VMStack.Pop())) {
|
|
|
|
m_CodePos += sizeof(StateScript *);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_CAST_BOOLEAN:
|
|
|
|
m_VMStack.GetTop().CastBoolean();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_COMPLEMENT:
|
|
|
|
m_VMStack.GetTop().complement();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_MINUS:
|
|
|
|
m_VMStack.GetTop().minus();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_DEC:
|
|
|
|
m_VMStack.GetTop()--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_INC:
|
|
|
|
m_VMStack.GetTop()++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_SIZE:
|
|
|
|
m_VMStack.GetTop().setIntValue((int)m_VMStack.GetTop().size());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_TARGETNAME:
|
|
|
|
targetList = world->GetExistingTargetList(m_VMStack.GetTop().stringValue());
|
|
|
|
|
|
|
|
if (!targetList) {
|
|
|
|
value = m_VMStack.GetTop().stringValue();
|
|
|
|
|
|
|
|
m_VMStack.GetTop().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) {
|
|
|
|
m_VMStack.GetTop().setListenerValue(targetList->ObjectAt(1));
|
|
|
|
} else if (targetList->NumObjects() > 1) {
|
|
|
|
m_VMStack.GetTop().setContainerValue((Container<SafePtr<Listener>> *)targetList);
|
|
|
|
} else {
|
|
|
|
value = m_VMStack.GetTop().stringValue();
|
|
|
|
|
|
|
|
m_VMStack.GetTop().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:
|
|
|
|
m_VMStack.GetTop().setIntValue(m_VMStack.GetTop().booleanValue());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DONE:
|
|
|
|
End();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_NOP:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(!"Invalid opcode");
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
Director.stackCount--;
|
|
|
|
|
|
|
|
if (deleteThread || state == STATE_WAITING) {
|
|
|
|
delete m_Thread;
|
|
|
|
} else if (state == STATE_SUSPENDED) {
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (state == STATE_DESTROYED) {
|
|
|
|
delete this;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
HandleScriptException
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::HandleScriptException(ScriptException& exc)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (m_ScriptClass) {
|
|
|
|
m_ScriptClass->GetScript()->PrintSourcePos(m_PrevCodePos, true);
|
|
|
|
} else {
|
|
|
|
glbs.DPrintf("unknown source pos");
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (exc.bAbort) {
|
|
|
|
ScriptException e(exc.string);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
e.bAbort = exc.bAbort;
|
|
|
|
e.bIsForAnim = exc.bIsForAnim;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
state = STATE_EXECUTION;
|
|
|
|
throw e;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-08-16 02:43:22 +02:00
|
|
|
gi.DPrintf2("^~^~^ Script Error : %s\n\n", exc.string.c_str());
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (m_ScriptClass->GetScript()->ScriptCheck()) {
|
|
|
|
if (g_scriptcheck->integer != 2 || !exc.bIsForAnim) {
|
|
|
|
ScriptException e("Script check failed");
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
e.bAbort = exc.bAbort;
|
|
|
|
e.bIsForAnim = exc.bIsForAnim;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
state = STATE_EXECUTION;
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Director.cmdCount += 100;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
SetFastData
|
|
|
|
|
|
|
|
Sets the starting virtual machine parameters
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::SetFastData(ScriptVariable *data, int dataSize)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (fastEvent.data) {
|
|
|
|
fastEvent.data = m_pOldData;
|
|
|
|
fastEvent.dataSize = m_OldDataSize;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
fastEvent.Clear();
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_pOldData = NULL;
|
|
|
|
m_OldDataSize = 0;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (dataSize) {
|
|
|
|
fastEvent.data = new ScriptVariable[dataSize];
|
|
|
|
fastEvent.dataSize = dataSize;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
for (int i = 0; i < dataSize; i++) {
|
|
|
|
fastEvent.data[i] = data[i];
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_pOldData = fastEvent.data;
|
|
|
|
m_OldDataSize = fastEvent.dataSize;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
NotifyDelete
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::NotifyDelete(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
switch (state) {
|
|
|
|
case STATE_DESTROYED:
|
|
|
|
ScriptError("Attempting to delete a dead thread.");
|
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case STATE_RUNNING:
|
|
|
|
case STATE_SUSPENDED:
|
|
|
|
case STATE_WAITING:
|
|
|
|
state = STATE_DESTROYED;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (m_ScriptClass) {
|
|
|
|
m_ScriptClass->RemoveThread(this);
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
case STATE_EXECUTION:
|
|
|
|
state = STATE_DESTROYED;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (m_ScriptClass) {
|
|
|
|
m_ScriptClass->RemoveThread(this);
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
delete this;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Resume
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Resume(qboolean bForce)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (state == STATE_SUSPENDED || (bForce && state != STATE_DESTROYED)) {
|
|
|
|
state = STATE_RUNNING;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Suspend
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Suspend()
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
if (state == STATE_DESTROYED) {
|
|
|
|
ScriptError("Cannot suspend a dead thread.");
|
|
|
|
} else if (!state) {
|
|
|
|
state = STATE_SUSPENDED;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Switch
|
|
|
|
|
|
|
|
Switch statement
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
bool ScriptVM::Switch(StateScript *stateScript, ScriptVariable& var)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
unsigned char *pos;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
fastEvent.dataSize = 0;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
pos = stateScript->FindLabel(var.stringValue());
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!pos) {
|
|
|
|
pos = stateScript->FindLabel("");
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!pos) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_CodePos = pos;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return true;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Filename
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
str ScriptVM::Filename(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return m_ScriptClass->Filename();
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Label
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
str ScriptVM::Label(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
const_str label = m_ScriptClass->NearestLabel(m_CodePos);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!label) {
|
|
|
|
return "";
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return Director.GetString(label);
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
ScriptClass *ScriptVM::GetScriptClass(void)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return m_ScriptClass;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
IsSuspended
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::IsSuspended(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return state == STATE_SUSPENDED;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
State
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
int ScriptVM::State(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return state;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ThreadState
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
int ScriptVM::ThreadState(void)
|
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
return m_ThreadState;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EventGoto
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::EventGoto(Event *ev)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-08-13 22:07:33 +02:00
|
|
|
unsigned char* codePos;
|
|
|
|
const ScriptVariable& value = ev->GetValue(1);
|
|
|
|
if (value.type == VARIABLE_CONSTSTRING)
|
|
|
|
{
|
|
|
|
const_str label = value.constStringValue();
|
|
|
|
codePos = m_ScriptClass->FindLabel(label);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-08-13 22:07:33 +02:00
|
|
|
if (!codePos) {
|
|
|
|
ScriptError("ScriptVM::EventGoto: label '%s' does not exist in '%s'.", value.stringValue().c_str(), Filename().c_str());
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
}
|
2023-08-13 22:07:33 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
str label = value.stringValue();
|
|
|
|
codePos = m_ScriptClass->FindLabel(label);
|
|
|
|
|
|
|
|
if (!codePos) {
|
|
|
|
ScriptError("ScriptVM::EventGoto: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetFastData(ev->data + 1, ev->dataSize - 1);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_CodePos = codePos;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EventThrow
|
|
|
|
|
|
|
|
Called when throwing an exception
|
|
|
|
====================
|
|
|
|
*/
|
2023-07-05 21:23:39 +02:00
|
|
|
bool ScriptVM::EventThrow(Event *ev)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
str label = ev->GetString(1);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
SetFastData(ev->data, ev->dataSize);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
fastEvent.eventnum = ev->eventnum;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
while (1) {
|
|
|
|
StateScript *stateScript = m_ScriptClass->GetCatchStateScript(m_PrevCodePos, m_PrevCodePos);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (!stateScript) {
|
|
|
|
break;
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
m_CodePos = stateScript->FindLabel(label);
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
if (m_CodePos) {
|
|
|
|
fastEvent.data++;
|
|
|
|
fastEvent.dataSize--;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2023-01-29 20:59:31 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
return false;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
bool ScriptVM::CanScriptTracePrint(void)
|
2023-01-29 20:59:31 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
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;
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
2023-02-02 23:48:51 +01:00
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::fetchOpcodeValue(void *outValue, size_t size)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
Com_Memcpy(outValue, m_CodePos, size);
|
|
|
|
m_CodePos += size;
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:23:39 +02:00
|
|
|
void ScriptVM::fetchActualOpcodeValue(void *outValue, size_t size)
|
2023-02-02 23:48:51 +01:00
|
|
|
{
|
2023-07-05 21:23:39 +02:00
|
|
|
Com_Memcpy(outValue, m_CodePos, size);
|
2023-02-02 23:48:51 +01:00
|
|
|
}
|