Files
scummvm-cursorfix/engines/ags/engine/script/runtime_script_value.h
2026-02-02 04:50:13 +01:00

446 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//=============================================================================
//
// Runtime script value struct
//
//=============================================================================
#ifndef AGS_ENGINE_SCRIPT_RUNTIME_SCRIPT_VALUE_H
#define AGS_ENGINE_SCRIPT_RUNTIME_SCRIPT_VALUE_H
#include "ags/engine/ac/dynobj/cc_script_object.h"
#include "ags/engine/ac/dynobj/cc_static_array.h"
#include "ags/engine/script/script_api.h"
#include "ags/shared/util/memory.h"
#include "ags/plugins/plugin_base.h"
namespace AGS3 {
enum ScriptValueType {
kScValUndefined, // to detect errors
kScValInteger, // as strictly 32-bit integer (for integer math)
kScValFloat, // as float (for floating point math), 32-bit
kScValPluginArg, // an 32-bit value, passed to a script function when called
// directly by plugin; is allowed to represent object pointer
kScValStackPtr, // as a pointer to stack entry
kScValData, // as a container for randomly sized data (usually array)
kScValGlobalVar, // as a pointer to script variable; used only for global vars,
// as pointer to local vars must have StackPtr type so that the
// stack allocation could work
kScValStringLiteral,// as a pointer to literal string (array of chars)
kScValStaticArray, // as a pointer to static global array (of static or dynamic objects)
kScValScriptObject, // as a pointer to managed script object
kScValPluginObject, // as a pointer to object managed by plugin (similar to
// kScValScriptObject, but has backward-compatible limitations)
kScValStaticFunction,// as a pointer to static function
kScValPluginFunction,// temporary workaround for plugins (unsafe function ptr)
kScValObjectFunction,// as a pointer to object member function, gets object pointer as
// first parameter
kScValCodePtr // as a pointer to element in byte-code array
};
struct RuntimeScriptValue {
public:
RuntimeScriptValue() {
Type = kScValUndefined;
IValue = 0;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 0;
}
RuntimeScriptValue(int32_t val) {
Type = kScValInteger;
IValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 4;
}
ScriptValueType Type;
Common::String methodName;
// The 32-bit value used for integer/float math and for storing
// variable/element offset relative to object (and array) address
union {
int32_t IValue; // access Value as int32 type
float FValue; // access Value as float type
};
// Pointer is used for storing... pointers - to objects, arrays,
// functions and stack entries (other RSV)
union {
void *Ptr; // generic data pointer
uint8_t *PtrU8; // byte buffer pointer
char *CStr; // char buffer pointer
RuntimeScriptValue *RValue;// access ptr as a pointer to Runtime Value
ScriptAPIFunction *SPfn; // access ptr as a pointer to Script API Static Function
ScriptAPIObjectFunction *ObjPfn; // access ptr as a pointer to Script API Object Function
};
// TODO: separation to Ptr and MgrPtr is only needed so far as there's
// a separation between Script*, Dynamic* and game entity classes.
// Once those classes are merged, it will no longer be needed.
union {
void *MgrPtr; // generic object manager pointer
IScriptObject *ObjMgr; // script object manager
CCStaticArray *ArrMgr; // static array manager
};
// The "real" size of data, either one stored in I/FValue,
// or the one referenced by Ptr. Used for calculating stack
// offsets.
// Original AGS scripts always assumed pointer is 32-bit.
// Therefore for stored pointers Size is always 4 both for x32
// and x64 builds, so that the script is interpreted correctly.
int Size;
inline bool IsValid() const {
return Type != kScValUndefined;
}
inline bool IsNull() const {
return Ptr == nullptr && IValue == 0;
}
inline bool GetAsBool() const {
return !IsNull();
}
inline void *GetPtrWithOffset() const {
return PtrU8 + IValue;
}
inline void *GetRValuePtrWithOffset() const {
return static_cast<uint8_t *>(RValue->GetPtrWithOffset()) + this->IValue;
}
inline RuntimeScriptValue &Invalidate() {
*this = RuntimeScriptValue();
return *this;
}
inline RuntimeScriptValue &SetUInt8(uint8_t val) {
Type = kScValInteger;
methodName.clear();
IValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 1;
return *this;
}
inline RuntimeScriptValue &SetInt16(int16_t val) {
Type = kScValInteger;
methodName.clear();
IValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 2;
return *this;
}
inline RuntimeScriptValue &SetInt32(int32_t val) {
Type = kScValInteger;
methodName.clear();
IValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetFloat(float val) {
Type = kScValFloat;
methodName.clear();
FValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetInt32AsBool(bool val) {
return SetInt32(val ? 1 : 0);
}
inline RuntimeScriptValue &SetFloatAsBool(bool val) {
return SetFloat(val ? 1.0F : 0.0F);
}
inline RuntimeScriptValue &SetPluginArgument(int32_t val) {
Type = kScValPluginArg;
methodName.clear();
IValue = val;
Ptr = nullptr;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetStackPtr(RuntimeScriptValue *stack_entry) {
Type = kScValStackPtr;
methodName.clear();
IValue = 0;
RValue = stack_entry;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetData(void *data, int size) {
Type = kScValData;
methodName.clear();
IValue = 0;
Ptr = data;
MgrPtr = nullptr;
Size = size;
return *this;
}
inline RuntimeScriptValue &SetGlobalVar(RuntimeScriptValue *glvar_value) {
Type = kScValGlobalVar;
methodName.clear();
IValue = 0;
RValue = glvar_value;
MgrPtr = nullptr;
Size = 4;
return *this;
}
// TODO: size?
inline RuntimeScriptValue &SetStringLiteral(const char *str) {
Type = kScValStringLiteral;
methodName.clear();
IValue = 0;
Ptr = const_cast<char *>(str);
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetStaticArray(void *object, CCStaticArray *manager) {
Type = kScValStaticArray;
methodName.clear();
IValue = 0;
Ptr = object;
ArrMgr = manager;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetScriptObject(void *object, IScriptObject *manager) {
Type = kScValScriptObject;
methodName.clear();
IValue = 0;
Ptr = object;
ObjMgr = manager;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetPluginObject(void *object, IScriptObject *manager) {
Type = kScValPluginObject;
methodName.clear();
IValue = 0;
Ptr = object;
ObjMgr = manager;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetScriptObject(ScriptValueType type, void *object, IScriptObject *manager) {
Type = type;
IValue = 0;
Ptr = object;
ObjMgr = manager;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetStaticFunction(ScriptAPIFunction *pfn) {
Type = kScValStaticFunction;
methodName.clear();
IValue = 0;
SPfn = pfn;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetPluginMethod(Plugins::ScriptContainer *sc, const Common::String &method) {
Type = kScValPluginFunction;
methodName = method;
Ptr = sc;
MgrPtr = nullptr;
IValue = 0;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetObjectFunction(ScriptAPIObjectFunction *pfn) {
Type = kScValObjectFunction;
methodName.clear();
IValue = 0;
ObjPfn = pfn;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue &SetCodePtr(void *ptr) {
Type = kScValCodePtr;
methodName.clear();
IValue = 0;
Ptr = ptr;
MgrPtr = nullptr;
Size = 4;
return *this;
}
inline RuntimeScriptValue operator !() const {
return RuntimeScriptValue().SetInt32AsBool(!GetAsBool());
}
inline bool operator ==(const RuntimeScriptValue &rval) const {
if (rval.Type == kScValPluginFunction) {
assert(!rval.methodName.empty());
return (Type == kScValPluginFunction) && (rval.methodName == methodName);
}
return ((intptr_t)Ptr + (intptr_t)IValue) == ((intptr_t)rval.Ptr + (intptr_t)rval.IValue);
}
inline bool operator !=(const RuntimeScriptValue &rval) const {
return !(*this == rval);
}
// FIXME: find out all certain cases when we are reading a pointer and store it
// as 32-bit value here. There should be a solution to distinct these cases and
// store value differently, otherwise it won't work for 64-bit build.
inline RuntimeScriptValue ReadValue() const {
switch (this->Type) {
case kScValStackPtr: {
// FIXME: join the kScValStackPtr with kScValData using some flag?
switch (RValue->Type) {
case kScValData:
// read from the stack memory buffer
return RuntimeScriptValue().SetInt32(*(int32_t *)(GetRValuePtrWithOffset()));
default:
// return the stack entry itself
return *RValue;
}
}
case kScValGlobalVar: {
// FIXME: join the kScValGlobalVar with kScValData using some flag?
switch (RValue->Type) {
case kScValData:
// read from the global memory buffer
return RuntimeScriptValue().SetInt32(AGS::Shared::Memory::ReadInt32LE(GetRValuePtrWithOffset()));
default:
// return the gvar entry itself
return *RValue;
}
}
case kScValStaticArray:
case kScValScriptObject:
return RuntimeScriptValue().SetInt32(this->ObjMgr->ReadInt32(this->Ptr, this->IValue));
default:
return RuntimeScriptValue().SetInt32(*(int32_t *)this->GetPtrWithOffset());
}
}
// Notice, that there are only two valid cases when a pointer may be written:
// when the destination is a stack entry or global variable of free type
// (not kScValData type).
// In any other case, only the numeric value (integer/float) will be written.
inline void WriteValue(const RuntimeScriptValue &rval) {
switch (this->Type) {
case kScValStackPtr: {
// FIXME: join the kScValStackPtr with kScValData using some flag?
switch (RValue->Type) {
case kScValData:
// write into the stack memory buffer
*(int32_t *)(GetRValuePtrWithOffset()) = rval.IValue;
break;
default:
// write into the stack entry
*RValue = rval;
// On stack we assume each item has at least 4 bytes (with exception
// of arrays - kScValData). This is why we fixup the size in case
// the assigned value is less (char, int16).
RValue->Size = 4;
break;
}
break;
}
case kScValGlobalVar: {
// FIXME: join the kScValGlobalVar with kScValData using some flag?
switch (RValue->Type) {
case kScValData:
// write into the global memory buffer
AGS::Shared::Memory::WriteInt32LE(GetRValuePtrWithOffset(), rval.IValue);
break;
default:
// write into the gvar entry
*RValue = rval;
break;
}
break;
}
case kScValStaticArray:
case kScValScriptObject: {
this->ObjMgr->WriteInt32(this->Ptr, this->IValue, rval.IValue);
break;
}
default: {
*((int32_t *)this->GetPtrWithOffset()) = rval.IValue;
break;
}
}
}
Plugins::PluginMethod pluginMethod() const {
return Plugins::PluginMethod((Plugins::PluginBase *)Ptr, methodName);
}
// Helper functions for reading or writing values from/to
// object, referenced by this Runtime Value.
// Copy implementation depends on value type.
uint8_t ReadByte() const;
int16_t ReadInt16() const;
int32_t ReadInt32() const;
void WriteByte(uint8_t val);
void WriteInt16(int16_t val);
void WriteInt32(int32_t val);
// Convert to most simple pointer type by resolving RValue ptrs and applying offsets;
// non pointer types are left unmodified
RuntimeScriptValue &DirectPtr();
// Similar to above, a slightly speed-optimised version for situations when we can
// tell for certain that we are expecting a pointer to the object and not its (first) field.
RuntimeScriptValue &DirectPtrObj();
// Resolve and return direct pointer to the referenced data; non pointer types return IValue
void *GetDirectPtr() const;
};
} // namespace AGS3
#endif