mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2025-02-25 21:00:46 +00:00
For the varargs functions that used the Type field to validate their parameters, now a hidden additional argument is passed which contains a byte array with the type info for the current call's arguments. Since this is static per call location it can be better prepared once when the code is being compiled instead of being put in a runtime created array for each invocation. Everything else uses the per-function instance of the same data. The only thing that still needed the type field with a VMValue is the defaults array, so this uses a different struct type now to store its data. # Conflicts: # src/v_draw.cpp
700 lines
23 KiB
C++
700 lines
23 KiB
C++
/*
|
|
** vm.h
|
|
** VM <-> native interface
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright -2016 Randy Heit
|
|
** Copyright 2016 Christoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#ifndef VM_H
|
|
#define VM_H
|
|
|
|
#include "zstring.h"
|
|
#include "autosegs.h"
|
|
#include "vectors.h"
|
|
#include "cmdlib.h"
|
|
#include "doomerrors.h"
|
|
#include "memarena.h"
|
|
#include "scripting/backend/scopebarrier.h"
|
|
|
|
class DObject;
|
|
union VMOP;
|
|
class VMScriptFunction;
|
|
|
|
extern FMemArena ClassDataAllocator;
|
|
|
|
#define MAX_RETURNS 8 // Maximum number of results a function called by script code can return
|
|
#define MAX_TRY_DEPTH 8 // Maximum number of nested TRYs in a single function
|
|
|
|
|
|
typedef unsigned char VM_UBYTE;
|
|
typedef signed char VM_SBYTE;
|
|
typedef unsigned short VM_UHALF;
|
|
typedef signed short VM_SHALF;
|
|
typedef unsigned int VM_UWORD;
|
|
typedef signed int VM_SWORD;
|
|
|
|
#define VM_EPSILON (1/65536.0)
|
|
|
|
// Register types for VMParam
|
|
enum
|
|
{
|
|
REGT_INT = 0,
|
|
REGT_FLOAT = 1,
|
|
REGT_STRING = 2,
|
|
REGT_POINTER = 3,
|
|
REGT_TYPE = 3,
|
|
|
|
REGT_KONST = 4,
|
|
REGT_MULTIREG2 = 8,
|
|
REGT_MULTIREG3 = 16, // (e.g. a vector)
|
|
REGT_MULTIREG = 24,
|
|
REGT_ADDROF = 32, // used with PARAM: pass address of this register
|
|
|
|
REGT_NIL = 128 // parameter was omitted
|
|
};
|
|
|
|
#define RET_FINAL (0x80) // Used with RET and RETI in the destination slot: this is the final return value
|
|
|
|
|
|
enum EVMAbortException
|
|
{
|
|
X_OTHER,
|
|
X_READ_NIL,
|
|
X_WRITE_NIL,
|
|
X_TOO_MANY_TRIES,
|
|
X_ARRAY_OUT_OF_BOUNDS,
|
|
X_DIVISION_BY_ZERO,
|
|
X_BAD_SELF,
|
|
X_FORMAT_ERROR
|
|
};
|
|
|
|
class CVMAbortException : public CDoomError
|
|
{
|
|
public:
|
|
static FString stacktrace;
|
|
CVMAbortException(EVMAbortException reason, const char *moreinfo, va_list ap);
|
|
void MaybePrintMessage();
|
|
};
|
|
|
|
// This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown.
|
|
void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...);
|
|
void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException reason, const char *moreinfo, ...);
|
|
|
|
struct VMReturn
|
|
{
|
|
void *Location;
|
|
VM_UBYTE RegType; // Same as VMParam RegType, except REGT_KONST is invalid; only used by asserts
|
|
|
|
void SetInt(int val)
|
|
{
|
|
assert(RegType == REGT_INT);
|
|
*(int *)Location = val;
|
|
}
|
|
void SetFloat(double val)
|
|
{
|
|
assert(RegType == REGT_FLOAT);
|
|
*(double *)Location = val;
|
|
}
|
|
void SetVector(const double val[3])
|
|
{
|
|
assert(RegType == (REGT_FLOAT|REGT_MULTIREG3));
|
|
((double *)Location)[0] = val[0];
|
|
((double *)Location)[1] = val[1];
|
|
((double *)Location)[2] = val[2];
|
|
}
|
|
void SetVector(const DVector3 &val)
|
|
{
|
|
assert(RegType == (REGT_FLOAT | REGT_MULTIREG3));
|
|
((double *)Location)[0] = val[0];
|
|
((double *)Location)[1] = val[1];
|
|
((double *)Location)[2] = val[2];
|
|
}
|
|
void SetVector2(const double val[2])
|
|
{
|
|
assert(RegType == (REGT_FLOAT|REGT_MULTIREG2));
|
|
((double *)Location)[0] = val[0];
|
|
((double *)Location)[1] = val[1];
|
|
}
|
|
void SetVector2(const DVector2 &val)
|
|
{
|
|
assert(RegType == (REGT_FLOAT | REGT_MULTIREG2));
|
|
((double *)Location)[0] = val[0];
|
|
((double *)Location)[1] = val[1];
|
|
}
|
|
void SetString(const FString &val)
|
|
{
|
|
assert(RegType == REGT_STRING);
|
|
*(FString *)Location = val;
|
|
}
|
|
|
|
void SetPointer(void *val)
|
|
{
|
|
assert(RegType == REGT_POINTER);
|
|
*(void **)Location = val;
|
|
}
|
|
|
|
void SetObject(DObject *val)
|
|
{
|
|
assert(RegType == REGT_POINTER);
|
|
*(void **)Location = val;
|
|
}
|
|
|
|
void IntAt(int *loc)
|
|
{
|
|
Location = loc;
|
|
RegType = REGT_INT;
|
|
}
|
|
void FloatAt(double *loc)
|
|
{
|
|
Location = loc;
|
|
RegType = REGT_FLOAT;
|
|
}
|
|
void StringAt(FString *loc)
|
|
{
|
|
Location = loc;
|
|
RegType = REGT_STRING;
|
|
}
|
|
void PointerAt(void **loc)
|
|
{
|
|
Location = loc;
|
|
RegType = REGT_POINTER;
|
|
}
|
|
VMReturn() { }
|
|
VMReturn(int *loc) { IntAt(loc); }
|
|
VMReturn(double *loc) { FloatAt(loc); }
|
|
VMReturn(FString *loc) { StringAt(loc); }
|
|
VMReturn(void **loc) { PointerAt(loc); }
|
|
};
|
|
|
|
struct VMRegisters;
|
|
|
|
struct TypedVMValue
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
void *a;
|
|
double f;
|
|
struct { int pad[3]; VM_UBYTE Type; };
|
|
struct { int foo[4]; } biggest;
|
|
const FString *sp;
|
|
};
|
|
|
|
const FString &s() const { return *sp; }
|
|
|
|
TypedVMValue()
|
|
{
|
|
a = NULL;
|
|
Type = REGT_NIL;
|
|
}
|
|
TypedVMValue(const TypedVMValue &o)
|
|
{
|
|
biggest = o.biggest;
|
|
}
|
|
TypedVMValue(int v)
|
|
{
|
|
i = v;
|
|
Type = REGT_INT;
|
|
}
|
|
TypedVMValue(double v)
|
|
{
|
|
f = v;
|
|
Type = REGT_FLOAT;
|
|
}
|
|
|
|
TypedVMValue(const FString *s)
|
|
{
|
|
sp = s;
|
|
Type = REGT_STRING;
|
|
}
|
|
TypedVMValue(DObject *v)
|
|
{
|
|
a = v;
|
|
Type = REGT_POINTER;
|
|
}
|
|
TypedVMValue(void *v)
|
|
{
|
|
a = v;
|
|
Type = REGT_POINTER;
|
|
}
|
|
TypedVMValue &operator=(const TypedVMValue &o)
|
|
{
|
|
biggest = o.biggest;
|
|
return *this;
|
|
}
|
|
TypedVMValue &operator=(int v)
|
|
{
|
|
i = v;
|
|
Type = REGT_INT;
|
|
return *this;
|
|
}
|
|
TypedVMValue &operator=(double v)
|
|
{
|
|
f = v;
|
|
Type = REGT_FLOAT;
|
|
return *this;
|
|
}
|
|
TypedVMValue &operator=(const FString *v)
|
|
{
|
|
sp = v;
|
|
Type = REGT_STRING;
|
|
return *this;
|
|
}
|
|
TypedVMValue &operator=(DObject *v)
|
|
{
|
|
a = v;
|
|
Type = REGT_POINTER;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
struct VMValue
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
void *a;
|
|
double f;
|
|
struct { int foo[2]; } biggest;
|
|
const FString *sp;
|
|
};
|
|
|
|
const FString &s() const { return *sp; }
|
|
|
|
VMValue()
|
|
{
|
|
a = NULL;
|
|
}
|
|
VMValue(const VMValue &o)
|
|
{
|
|
biggest = o.biggest;
|
|
}
|
|
VMValue(int v)
|
|
{
|
|
i = v;
|
|
}
|
|
VMValue(double v)
|
|
{
|
|
f = v;
|
|
}
|
|
VMValue(const char *s) = delete;
|
|
VMValue(const FString &s) = delete;
|
|
|
|
VMValue(const FString *s)
|
|
{
|
|
sp = s;
|
|
}
|
|
VMValue(DObject *v)
|
|
{
|
|
a = v;
|
|
}
|
|
VMValue(void *v)
|
|
{
|
|
a = v;
|
|
}
|
|
VMValue &operator=(const VMValue &o)
|
|
{
|
|
biggest = o.biggest;
|
|
return *this;
|
|
}
|
|
VMValue &operator=(const TypedVMValue &o)
|
|
{
|
|
memcpy(&biggest, &o.biggest, sizeof(biggest));
|
|
return *this;
|
|
}
|
|
VMValue &operator=(int v)
|
|
{
|
|
i = v;
|
|
return *this;
|
|
}
|
|
VMValue &operator=(double v)
|
|
{
|
|
f = v;
|
|
return *this;
|
|
}
|
|
VMValue &operator=(const FString *v)
|
|
{
|
|
sp = v;
|
|
return *this;
|
|
}
|
|
VMValue &operator=(const FString &v) = delete;
|
|
VMValue &operator=(const char *v) = delete;
|
|
VMValue &operator=(DObject *v)
|
|
{
|
|
a = v;
|
|
return *this;
|
|
}
|
|
int ToInt(int Type)
|
|
{
|
|
if (Type == REGT_INT)
|
|
{
|
|
return i;
|
|
}
|
|
if (Type == REGT_FLOAT)
|
|
{
|
|
return int(f);
|
|
}
|
|
if (Type == REGT_STRING)
|
|
{
|
|
return s().ToLong();
|
|
}
|
|
// FIXME
|
|
return 0;
|
|
}
|
|
double ToDouble(int Type)
|
|
{
|
|
if (Type == REGT_FLOAT)
|
|
{
|
|
return f;
|
|
}
|
|
if (Type == REGT_INT)
|
|
{
|
|
return i;
|
|
}
|
|
if (Type == REGT_STRING)
|
|
{
|
|
return s().ToDouble();
|
|
}
|
|
// FIXME
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
class VMFunction
|
|
{
|
|
public:
|
|
bool Unsafe = false;
|
|
uint8_t ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
|
|
int VarFlags = 0; // [ZZ] this replaces 5+ bool fields
|
|
unsigned VirtualIndex = ~0u;
|
|
FName Name;
|
|
const uint8_t *RegTypes = nullptr;
|
|
TArray<TypedVMValue> DefaultArgs;
|
|
FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong.
|
|
|
|
class PPrototype *Proto;
|
|
TArray<uint32_t> ArgFlags; // Should be the same length as Proto->ArgumentTypes
|
|
|
|
int(*ScriptCall)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = nullptr;
|
|
|
|
VMFunction(FName name = NAME_None) : ImplicitArgs(0), Name(name), Proto(NULL)
|
|
{
|
|
AllFunctions.Push(this);
|
|
}
|
|
virtual ~VMFunction() {}
|
|
|
|
void *operator new(size_t size)
|
|
{
|
|
return ClassDataAllocator.Alloc(size);
|
|
}
|
|
|
|
void operator delete(void *block) {}
|
|
void operator delete[](void *block) {}
|
|
static void DeleteAll()
|
|
{
|
|
for (auto f : AllFunctions)
|
|
{
|
|
f->~VMFunction();
|
|
}
|
|
AllFunctions.Clear();
|
|
}
|
|
static void CreateRegUseInfo()
|
|
{
|
|
for (auto f : AllFunctions)
|
|
{
|
|
f->CreateRegUse();
|
|
}
|
|
}
|
|
static TArray<VMFunction *> AllFunctions;
|
|
protected:
|
|
void CreateRegUse();
|
|
};
|
|
|
|
// Use this in the prototype for a native function.
|
|
|
|
#ifdef NDEBUG
|
|
#define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret
|
|
#define VM_ARGS_NAMES param, numparam, ret, numret
|
|
#define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret)
|
|
#else
|
|
#define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret, const uint8_t *reginfo
|
|
#define VM_ARGS_NAMES param, numparam, ret, numret, reginfo
|
|
#define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret), (reginfo)
|
|
#endif
|
|
|
|
class VMNativeFunction : public VMFunction
|
|
{
|
|
public:
|
|
typedef int (*NativeCallType)(VM_ARGS);
|
|
|
|
// 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype.
|
|
VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
|
|
VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
|
|
VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; }
|
|
|
|
// Return value is the number of results.
|
|
NativeCallType NativeCall;
|
|
|
|
private:
|
|
static int NativeScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
|
|
};
|
|
|
|
int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/);
|
|
int VMCallWithDefaults(VMFunction *func, TArray<VMValue> ¶ms, VMReturn *results, int numresults/*, VMException **trap = NULL*/);
|
|
|
|
inline int VMCallAction(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/)
|
|
{
|
|
return VMCall(func, params, numparams, results, numresults);
|
|
}
|
|
|
|
// Use these to collect the parameters in a native function.
|
|
// variable name <x> at position <p>
|
|
void NullParam(const char *varname);
|
|
|
|
#ifndef NDEBUG
|
|
bool AssertObject(void * ob);
|
|
#endif
|
|
|
|
#define PARAM_NULLCHECK(ptr, var) (ptr == nullptr? NullParam(#var), ptr : ptr)
|
|
|
|
// This cannot assert because there is no info for varargs
|
|
#define PARAM_VA_POINTER(x) const uint8_t *x = (const uint8_t *)param[numparam-1].a;
|
|
|
|
// For required parameters.
|
|
#define PARAM_INT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); int x = param[p].i;
|
|
#define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); unsigned x = param[p].i;
|
|
#define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); bool x = !!param[p].i;
|
|
#define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FName x = ENamedName(param[p].i);
|
|
#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FSoundID x = param[p].i;
|
|
#define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); PalEntry x; x.d = param[p].i;
|
|
#define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); double x = param[p].f;
|
|
#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); DAngle x = param[p].f;
|
|
#define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_STRING); FString x = param[p].s();
|
|
#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass());
|
|
#define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass());
|
|
#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)param[p].a;
|
|
#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type x = (type )param[p].a;
|
|
#define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && AssertObject(param[p].a)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
|
|
#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
|
|
#define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x);
|
|
#define PARAM_OBJECT_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && (AssertObject(param[p].a))); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
|
|
#define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
|
|
|
|
|
|
// The above, but with an automatically increasing position index.
|
|
#define PARAM_PROLOGUE int paramnum = -1;
|
|
|
|
#define PARAM_INT(x) ++paramnum; PARAM_INT_AT(paramnum,x)
|
|
#define PARAM_UINT(x) ++paramnum; PARAM_UINT_AT(paramnum,x)
|
|
#define PARAM_BOOL(x) ++paramnum; PARAM_BOOL_AT(paramnum,x)
|
|
#define PARAM_NAME(x) ++paramnum; PARAM_NAME_AT(paramnum,x)
|
|
#define PARAM_SOUND(x) ++paramnum; PARAM_SOUND_AT(paramnum,x)
|
|
#define PARAM_COLOR(x) ++paramnum; PARAM_COLOR_AT(paramnum,x)
|
|
#define PARAM_FLOAT(x) ++paramnum; PARAM_FLOAT_AT(paramnum,x)
|
|
#define PARAM_ANGLE(x) ++paramnum; PARAM_ANGLE_AT(paramnum,x)
|
|
#define PARAM_STRING(x) ++paramnum; PARAM_STRING_AT(paramnum,x)
|
|
#define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x)
|
|
#define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x)
|
|
#define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type)
|
|
#define PARAM_POINTERTYPE(x,type) ++paramnum; PARAM_POINTERTYPE_AT(paramnum,x,type)
|
|
#define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type)
|
|
#define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
|
|
#define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
|
|
#define PARAM_POINTER_NOT_NULL(x,type) ++paramnum; PARAM_POINTER_NOT_NULL_AT(paramnum,x,type)
|
|
#define PARAM_OBJECT_NOT_NULL(x,type) ++paramnum; PARAM_OBJECT_NOT_NULL_AT(paramnum,x,type)
|
|
#define PARAM_CLASS_NOT_NULL(x,base) ++paramnum; PARAM_CLASS_NOT_NULL_AT(paramnum,x,base)
|
|
|
|
typedef int(*actionf_p)(VM_ARGS);
|
|
|
|
struct FieldDesc
|
|
{
|
|
const char *ClassName;
|
|
const char *FieldName;
|
|
size_t FieldOffset;
|
|
unsigned FieldSize;
|
|
int BitValue;
|
|
};
|
|
|
|
struct AFuncDesc
|
|
{
|
|
const char *ClassName;
|
|
const char *FuncName;
|
|
actionf_p Function;
|
|
VMNativeFunction **VMPointer;
|
|
};
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma section(".areg$u",read)
|
|
#pragma section(".freg$u",read)
|
|
|
|
#define MSVC_ASEG __declspec(allocate(".areg$u"))
|
|
#define MSVC_FSEG __declspec(allocate(".freg$u"))
|
|
#define GCC_ASEG
|
|
#define GCC_FSEG
|
|
#else
|
|
#define MSVC_ASEG
|
|
#define MSVC_FSEG
|
|
#define GCC_ASEG __attribute__((section(SECTION_AREG))) __attribute__((used))
|
|
#define GCC_FSEG __attribute__((section(SECTION_FREG))) __attribute__((used))
|
|
#endif
|
|
|
|
// Macros to handle action functions. These are here so that I don't have to
|
|
// change every single use in case the parameters change.
|
|
|
|
#define DEFINE_ACTION_FUNCTION(cls, name) \
|
|
static int AF_##cls##_##name(VM_ARGS); \
|
|
VMNativeFunction *cls##_##name##_VMPtr; \
|
|
static const AFuncDesc cls##_##name##_Hook = { #cls, #name, AF_##cls##_##name, &cls##_##name##_VMPtr }; \
|
|
extern AFuncDesc const *const cls##_##name##_HookPtr; \
|
|
MSVC_ASEG AFuncDesc const *const cls##_##name##_HookPtr GCC_ASEG = &cls##_##name##_Hook; \
|
|
static int AF_##cls##_##name(VM_ARGS)
|
|
|
|
// cls is the scripted class name, icls the internal one (e.g. player_t vs. Player)
|
|
#define DEFINE_FIELD_X(cls, icls, name) \
|
|
static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \
|
|
extern FieldDesc const *const VMField_##icls##_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##icls##_##name;
|
|
|
|
// This is for cases where the internal size does not match the part that gets exported.
|
|
#define DEFINE_FIELD_UNSIZED(cls, icls, name) \
|
|
static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), ~0u, 0 }; \
|
|
extern FieldDesc const *const VMField_##icls##_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##icls##_##name;
|
|
|
|
#define DEFINE_FIELD_NAMED_X(cls, icls, name, scriptname) \
|
|
static const FieldDesc VMField_##cls##_##scriptname = { "A" #cls, #scriptname, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \
|
|
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
|
|
|
|
#define DEFINE_FIELD_X_BIT(cls, icls, name, bitval) \
|
|
static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), bitval }; \
|
|
extern FieldDesc const *const VMField_##icls##_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##cls##_##name;
|
|
|
|
#define DEFINE_FIELD(cls, name) \
|
|
static const FieldDesc VMField_##cls##_##name = { #cls, #name, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), 0 }; \
|
|
extern FieldDesc const *const VMField_##cls##_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##cls##_##name##_HookPtr GCC_FSEG = &VMField_##cls##_##name;
|
|
|
|
#define DEFINE_FIELD_NAMED(cls, name, scriptname) \
|
|
static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), 0 }; \
|
|
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
|
|
|
|
#define DEFINE_FIELD_BIT(cls, name, scriptname, bitval) \
|
|
static const FieldDesc VMField_##cls##_##scriptname = { #cls, #scriptname, (unsigned)myoffsetof(cls, name), (unsigned)sizeof(cls::name), bitval }; \
|
|
extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
|
|
|
|
#define DEFINE_GLOBAL(name) \
|
|
static const FieldDesc VMGlobal_##name = { "", #name, (size_t)&name, (unsigned)sizeof(name), 0 }; \
|
|
extern FieldDesc const *const VMGlobal_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMGlobal_##name##_HookPtr GCC_FSEG = &VMGlobal_##name;
|
|
|
|
#define DEFINE_GLOBAL_NAMED(iname, name) \
|
|
static const FieldDesc VMGlobal_##name = { "", #name, (size_t)&iname, (unsigned)sizeof(iname), 0 }; \
|
|
extern FieldDesc const *const VMGlobal_##name##_HookPtr; \
|
|
MSVC_FSEG FieldDesc const *const VMGlobal_##name##_HookPtr GCC_FSEG = &VMGlobal_##name;
|
|
|
|
|
|
class AActor;
|
|
|
|
#define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_POINTER(v) do { void *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_OBJECT(v) do { auto state = v; if (numret > 0) { assert(ret != NULL); ret->SetObject(state); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_VEC2(v) do { DVector2 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector2(u); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_VEC3(v) do { DVector3 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector(u); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0)
|
|
#define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v)
|
|
#define ACTION_RETURN_STRING(v) do { FString u = v; if (numret > 0) { assert(ret != NULL); ret->SetString(u); return 1; } return 0; } while(0)
|
|
|
|
// Checks to see what called the current action function
|
|
#define ACTION_CALL_FROM_ACTOR() (stateinfo == nullptr || stateinfo->mStateType == STATE_Actor)
|
|
#define ACTION_CALL_FROM_PSPRITE() (self->player && stateinfo != nullptr && stateinfo->mStateType == STATE_Psprite)
|
|
#define ACTION_CALL_FROM_INVENTORY() (stateinfo != nullptr && stateinfo->mStateType == STATE_StateChain)
|
|
|
|
// Standard parameters for all action functions
|
|
// self - Actor this action is to operate on (player if a weapon)
|
|
// stateowner - Actor this action really belongs to (may be an item)
|
|
// callingstate - State this action was called from
|
|
#define PARAM_ACTION_PROLOGUE(type) \
|
|
PARAM_PROLOGUE; \
|
|
PARAM_OBJECT_NOT_NULL (self, AActor); \
|
|
PARAM_OBJECT (stateowner, type) \
|
|
PARAM_POINTER (stateinfo, FStateParamInfo) \
|
|
|
|
// Number of action paramaters
|
|
#define NAP 3
|
|
|
|
#define PARAM_SELF_PROLOGUE(type) \
|
|
PARAM_PROLOGUE; \
|
|
PARAM_OBJECT_NOT_NULL(self, type);
|
|
|
|
// for structs we cannot do a class validation
|
|
#define PARAM_SELF_STRUCT_PROLOGUE(type) \
|
|
PARAM_PROLOGUE; \
|
|
PARAM_POINTER_NOT_NULL(self, type);
|
|
|
|
class PFunction;
|
|
|
|
VMFunction *FindVMFunction(PClass *cls, const char *name);
|
|
#define DECLARE_VMFUNC(cls, name) static VMFunction *name; if (name == nullptr) name = FindVMFunction(RUNTIME_CLASS(cls), #name);
|
|
|
|
FString FStringFormat(VM_ARGS, int offset = 0);
|
|
|
|
|
|
unsigned GetVirtualIndex(PClass *cls, const char *funcname);
|
|
|
|
#define IFVIRTUALPTR(self, cls, funcname) \
|
|
static unsigned VIndex = ~0u; \
|
|
if (VIndex == ~0u) { \
|
|
VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \
|
|
assert(VIndex != ~0u); \
|
|
} \
|
|
auto clss = self->GetClass(); \
|
|
VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \
|
|
if (func != nullptr)
|
|
|
|
#define IFVIRTUAL(cls, funcname) IFVIRTUALPTR(this, cls, funcname)
|
|
|
|
#define IFVIRTUALPTRNAME(self, cls, funcname) \
|
|
static unsigned VIndex = ~0u; \
|
|
if (VIndex == ~0u) { \
|
|
VIndex = GetVirtualIndex(PClass::FindClass(cls), #funcname); \
|
|
assert(VIndex != ~0u); \
|
|
} \
|
|
auto clss = self->GetClass(); \
|
|
VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \
|
|
if (func != nullptr)
|
|
|
|
#endif
|