Add UI Scripting support

This commit is contained in:
Jari van der Kaap 2023-01-02 01:12:14 +01:00
parent 59586e18e6
commit 27d081700a
12 changed files with 2403 additions and 20 deletions

View File

@ -0,0 +1,370 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/ui_scripting/execution.hpp"
#include "ui_scripting.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/finally.hpp>
namespace ui_scripting
{
namespace
{
std::unordered_map<game::hks::cclosure*, std::function<arguments(const function_arguments& args)>> converted_functions;
utils::hook::detour ui_cod_init_hook;
utils::hook::detour ui_shutdown_hook;
utils::hook::detour hks_package_require_hook;
utils::hook::detour lua_cod_getrawfile_hook;
struct globals_t
{
std::string in_require_script;
std::unordered_map<std::string, std::string> loaded_scripts;
std::unordered_map<std::string, std::string> local_scripts;
bool load_raw_script{};
std::string raw_script_name{};
};
globals_t globals;
bool is_loaded_script(const std::string& name)
{
return globals.loaded_scripts.contains(name);
}
bool is_local_script(const std::string& name)
{
return globals.local_scripts.contains(name);
}
std::string get_root_script(const std::string& name)
{
const auto itr = globals.loaded_scripts.find(name);
return (itr == globals.loaded_scripts.end()) ? std::string() : itr->second;
}
table get_globals()
{
const auto state = *game::hks::lua_state;
return state->globals.v.table;
}
void print_error(const std::string& error)
{
printf("************** LUI script execution error **************\n");
printf("%s\n", error.data());
printf("********************************************************\n");
}
void print_loading_script(const std::string& name)
{
printf("Loading LUI script '%s'\n", name.data());
}
std::string get_current_script(game::hks::lua_State* state)
{
game::hks::lua_Debug info{};
game::hks::hksi_lua_getstack(state, 1, &info);
game::hks::hksi_lua_getinfo(state, "nSl", &info);
return info.short_src;
}
int load_buffer(const std::string& name, const std::string& data)
{
const auto state = *game::hks::lua_state;
const auto sharing_mode = state->m_global->m_bytecodeSharingMode;
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
const auto _0 = utils::finally([&]
{
state->m_global->m_bytecodeSharingMode = sharing_mode;
});
game::hks::HksCompilerSettings compiler_settings{};
return game::hks::hksi_hksL_loadbuffer(state, &compiler_settings, data.data(), data.size(), name.data());
}
void load_script(const std::string& name, const std::string& data)
{
globals.loaded_scripts[name] = name;
const auto state = *game::hks::lua_state;
const auto lua = get_globals();
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
const auto load_results = lua["loadstring"](data, name);
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_SECURE;
if (load_results[0].is<function>())
{
const auto results = lua["pcall"](load_results);
if (!results[0].as<bool>())
{
print_error(results[1].as<std::string>());
}
}
else if (load_results[1].is<std::string>())
{
print_error(load_results[1].as<std::string>());
}
}
void load_local_script_files(const std::string& script_dir)
{
if (!utils::io::directory_exists(script_dir))
{
return;
}
const auto scripts = utils::io::list_files(script_dir);
for (const auto& script : scripts)
{
if (std::filesystem::is_regular_file(script))
{
const std::string file_path = script.substr(script.find("ui_scripts") + 11);
globals.local_scripts[file_path.c_str()] = script;
}
else if (std::filesystem::is_directory(script))
{
load_local_script_files(script);
}
}
}
void load_scripts(const std::string& script_dir)
{
if (!utils::io::directory_exists(script_dir))
{
return;
}
load_local_script_files(script_dir);
const auto scripts = utils::io::list_files(script_dir);
for (const auto& script : scripts)
{
std::string data;
if (std::filesystem::is_directory(script) && utils::io::read_file(script + "/__init__.lua", &data))
{
print_loading_script(script);
load_script(script + "/__init__.lua", data);
}
}
}
void setup_functions()
{
const auto lua = get_globals();
using game = table;
auto game_type = game();
lua["game"] = game_type;
}
void enable_globals()
{
const auto lua = get_globals();
const std::string code =
"local g = getmetatable(_G)\n"
"if not g then\n"
"g = {}\n"
"setmetatable(_G, g)\n"
"end\n"
"g.__newindex = nil\n";
const auto state = *game::hks::lua_state;
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
lua["loadstring"](code)[0]();
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_SECURE;
}
void start()
{
globals = {};
const auto lua = get_globals();
enable_globals();
setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x141D30290_g)); // hks::base_print
lua["table"]["unpack"] = lua["unpack"];
lua["luiglobals"] = lua;
load_scripts(game::get_host_library().get_folder().append("/data/ui_scripts/").string());
load_scripts("boiii/ui_scripts/");
load_scripts("data/ui_scripts/");
}
void try_start()
{
try
{
start();
}
catch (const std::exception& ex)
{
printf("Failed to load LUI scripts: %s\n", ex.what());
}
}
void ui_cod_init_stub(void* a1, void* a2)
{
ui_cod_init_hook.invoke(a1, a2);
const auto _0 = utils::finally(&try_start);
}
void ui_shutdown_stub()
{
converted_functions.clear();
globals = {};
return ui_shutdown_hook.invoke<void>();
}
void* hks_package_require_stub(game::hks::lua_State* state)
{
const auto script = get_current_script(state);
const auto root = get_root_script(script);
globals.in_require_script = root;
return hks_package_require_hook.invoke<void*>(state);
}
int hks_load_stub(game::hks::lua_State* state, void* compiler_options, void* reader, void* reader_data, void* debug_reader, void* debug_reader_data, const char* chunk_name)
{
if (globals.load_raw_script)
{
globals.load_raw_script = false;
globals.loaded_scripts[globals.raw_script_name] = globals.in_require_script;
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
}
return utils::hook::invoke<int>(0x141D3AFB0_g, state, compiler_options, reader, reader_data, debug_reader, debug_reader_data, chunk_name);
}
game::XAssetHeader lua_cod_getrawfile_stub(char* filename)
{
game::XAssetHeader header{ .luaFile = nullptr };
if (!is_local_script(filename))
{
return lua_cod_getrawfile_hook.invoke<game::XAssetHeader>(filename);
}
std::string target_script = globals.local_scripts[filename];
if (utils::io::file_exists(target_script))
{
globals.load_raw_script = true;
globals.raw_script_name = target_script;
header.luaFile = reinterpret_cast<game::LuaFile*>(1);
return header;
}
return lua_cod_getrawfile_hook.invoke<game::XAssetHeader>(filename);
}
int luaopen_stub([[maybe_unused]] game::hks::lua_State* l)
{
return 0;
}
int hks_base_stub([[maybe_unused]] game::hks::lua_State* l)
{
return 0;
}
};
int main_handler(game::hks::lua_State* state)
{
const auto value = state->m_apistack.base[-1];
if (value.t != game::hks::TCFUNCTION)
{
return 0;
}
const auto closure = value.v.cClosure;
if (!converted_functions.contains(closure))
{
return 0;
}
const auto& function = converted_functions[closure];
try
{
const auto args = get_return_values();
const auto results = function(args);
for (const auto& result : results)
{
push_value(result);
}
return static_cast<int>(results.size());
}
catch (const std::exception& ex)
{
game::hks::hksi_luaL_error(state, ex.what());
}
return 0;
}
template <typename F>
game::hks::cclosure* convert_function(F f)
{
const auto state = *game::hks::lua_state;
const auto closure = game::hks::cclosure_Create(state, main_handler, 0, 0, 0);
converted_functions[closure] = wrap_function(f);
return closure;
}
class component final : public client_component
{
public:
void post_unpack() override
{
// Do not allow the HKS vm to open LUA's libraries
utils::hook::jump(0x141D33510_g, luaopen_stub); // io
utils::hook::jump(0x141D33D20_g, luaopen_stub); // os
utils::hook::jump(0x141D34B40_g, luaopen_stub); // serialize
utils::hook::jump(0x141D34B10_g, luaopen_stub); // havokscript
utils::hook::jump(0x141D34190_g, luaopen_stub); // debug
// Disable unsafe functions
utils::hook::jump(0x141D300B0_g, hks_base_stub); // base_loadfile
utils::hook::jump(0x141D31EE0_g, hks_base_stub); // base_load
utils::hook::jump(0x141D2CF00_g, hks_base_stub); // string_dump
utils::hook::jump(0x141D2AF90_g, hks_base_stub); // os_execute
utils::hook::call(0x141D4979A_g, hks_load_stub);
hks_package_require_hook.create(0x141D28EF0_g, hks_package_require_stub);
ui_cod_init_hook.create(0x141F298B0_g, ui_cod_init_stub);
ui_shutdown_hook.create(0x14270E9C0_g, ui_shutdown_stub);
lua_cod_getrawfile_hook.create(0x141F0F880_g, lua_cod_getrawfile_stub);
scheduler::once([]() {
game::dvar_t* dvar_callstack_ship = game::Dvar_FindVar("ui_error_callstack_ship");
dvar_callstack_ship->flags = (game::dvarFlags_e)0;
game::dvar_t* dvar_report_delay= game::Dvar_FindVar("ui_error_report_delay");
dvar_report_delay->flags = (game::dvarFlags_e)0;
game::Dvar_SetFromStringByName("ui_error_callstack_ship", "1", true);
game::Dvar_SetFromStringByName("ui_error_report_delay", "0", true);
}, scheduler::pipeline::main);
}
};
}
REGISTER_COMPONENT(ui_scripting::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace ui_scripting
{
template <typename F>
game::hks::cclosure* convert_function(F f);
}

View File

@ -1,12 +1,9 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "game.hpp" #include "game.hpp"
#include <utils/nt.hpp>
namespace game namespace game
{ {
namespace
{
const utils::nt::library& get_host_library() const utils::nt::library& get_host_library()
{ {
static auto host_library = [] static auto host_library = []
@ -22,7 +19,6 @@ namespace game
return host_library; return host_library;
} }
}
size_t get_base() size_t get_base()
{ {

View File

@ -1,9 +1,11 @@
#pragma once #pragma once
#include "structs.hpp" #include "structs.hpp"
#include <utils/nt.hpp>
namespace game namespace game
{ {
const utils::nt::library& get_host_library();
size_t get_base(); size_t get_base();
bool is_server(); bool is_server();

View File

@ -414,6 +414,132 @@ namespace game
ERROR_SOFTRESTART_KEEPDW = 0x800, ERROR_SOFTRESTART_KEEPDW = 0x800,
}; };
enum XAssetType
{
ASSET_TYPE_PHYSPRESET = 0x0,
ASSET_TYPE_PHYSCONSTRAINTS = 0x1,
ASSET_TYPE_DESTRUCTIBLEDEF = 0x2,
ASSET_TYPE_XANIMPARTS = 0x3,
ASSET_TYPE_XMODEL = 0x4,
ASSET_TYPE_XMODELMESH = 0x5,
ASSET_TYPE_MATERIAL = 0x6,
ASSET_TYPE_COMPUTE_SHADER_SET = 0x7,
ASSET_TYPE_TECHNIQUE_SET = 0x8,
ASSET_TYPE_IMAGE = 0x9,
ASSET_TYPE_SOUND = 0xA,
ASSET_TYPE_SOUND_PATCH = 0xB,
ASSET_TYPE_CLIPMAP = 0xC,
ASSET_TYPE_COMWORLD = 0xD,
ASSET_TYPE_GAMEWORLD = 0xE,
ASSET_TYPE_MAP_ENTS = 0xF,
ASSET_TYPE_GFXWORLD = 0x10,
ASSET_TYPE_LIGHT_DEF = 0x11,
ASSET_TYPE_LENSFLARE_DEF = 0x12,
ASSET_TYPE_UI_MAP = 0x13,
ASSET_TYPE_FONT = 0x14,
ASSET_TYPE_FONTICON = 0x15,
ASSET_TYPE_LOCALIZE_ENTRY = 0x16,
ASSET_TYPE_WEAPON = 0x17,
ASSET_TYPE_WEAPONDEF = 0x18,
ASSET_TYPE_WEAPON_VARIANT = 0x19,
ASSET_TYPE_WEAPON_FULL = 0x1A,
ASSET_TYPE_CGMEDIA = 0x1B,
ASSET_TYPE_PLAYERSOUNDS = 0x1C,
ASSET_TYPE_PLAYERFX = 0x1D,
ASSET_TYPE_SHAREDWEAPONSOUNDS = 0x1E,
ASSET_TYPE_ATTACHMENT = 0x1F,
ASSET_TYPE_ATTACHMENT_UNIQUE = 0x20,
ASSET_TYPE_WEAPON_CAMO = 0x21,
ASSET_TYPE_CUSTOMIZATION_TABLE = 0x22,
ASSET_TYPE_CUSTOMIZATION_TABLE_FE_IMAGES = 0x23,
ASSET_TYPE_CUSTOMIZATION_TABLE_COLOR = 0x24,
ASSET_TYPE_SNDDRIVER_GLOBALS = 0x25,
ASSET_TYPE_FX = 0x26,
ASSET_TYPE_TAGFX = 0x27,
ASSET_TYPE_NEW_LENSFLARE_DEF = 0x28,
ASSET_TYPE_IMPACT_FX = 0x29,
ASSET_TYPE_IMPACT_SOUND = 0x2A,
ASSET_TYPE_PLAYER_CHARACTER = 0x2B,
ASSET_TYPE_AITYPE = 0x2C,
ASSET_TYPE_CHARACTER = 0x2D,
ASSET_TYPE_XMODELALIAS = 0x2E,
ASSET_TYPE_RAWFILE = 0x2F,
ASSET_TYPE_STRINGTABLE = 0x30,
ASSET_TYPE_STRUCTURED_TABLE = 0x31,
ASSET_TYPE_LEADERBOARD = 0x32,
ASSET_TYPE_DDL = 0x33,
ASSET_TYPE_GLASSES = 0x34,
ASSET_TYPE_TEXTURELIST = 0x35,
ASSET_TYPE_SCRIPTPARSETREE = 0x36,
ASSET_TYPE_KEYVALUEPAIRS = 0x37,
ASSET_TYPE_VEHICLEDEF = 0x38,
ASSET_TYPE_ADDON_MAP_ENTS = 0x39,
ASSET_TYPE_TRACER = 0x3A,
ASSET_TYPE_SLUG = 0x3B,
ASSET_TYPE_SURFACEFX_TABLE = 0x3C,
ASSET_TYPE_SURFACESOUNDDEF = 0x3D,
ASSET_TYPE_FOOTSTEP_TABLE = 0x3E,
ASSET_TYPE_ENTITYFXIMPACTS = 0x3F,
ASSET_TYPE_ENTITYSOUNDIMPACTS = 0x40,
ASSET_TYPE_ZBARRIER = 0x41,
ASSET_TYPE_VEHICLEFXDEF = 0x42,
ASSET_TYPE_VEHICLESOUNDDEF = 0x43,
ASSET_TYPE_TYPEINFO = 0x44,
ASSET_TYPE_SCRIPTBUNDLE = 0x45,
ASSET_TYPE_SCRIPTBUNDLELIST = 0x46,
ASSET_TYPE_RUMBLE = 0x47,
ASSET_TYPE_BULLETPENETRATION = 0x48,
ASSET_TYPE_LOCDMGTABLE = 0x49,
ASSET_TYPE_AIMTABLE = 0x4A,
ASSET_TYPE_ANIMSELECTORTABLESET = 0x4B,
ASSET_TYPE_ANIMMAPPINGTABLE = 0x4C,
ASSET_TYPE_ANIMSTATEMACHINE = 0x4D,
ASSET_TYPE_BEHAVIORTREE = 0x4E,
ASSET_TYPE_BEHAVIORSTATEMACHINE = 0x4F,
ASSET_TYPE_TTF = 0x50,
ASSET_TYPE_SANIM = 0x51,
ASSET_TYPE_LIGHT_DESCRIPTION = 0x52,
ASSET_TYPE_SHELLSHOCK = 0x53,
ASSET_TYPE_XCAM = 0x54,
ASSET_TYPE_BG_CACHE = 0x55,
ASSET_TYPE_TEXTURE_COMBO = 0x56,
ASSET_TYPE_FLAMETABLE = 0x57,
ASSET_TYPE_BITFIELD = 0x58,
ASSET_TYPE_ATTACHMENT_COSMETIC_VARIANT = 0x59,
ASSET_TYPE_MAPTABLE = 0x5A,
ASSET_TYPE_MAPTABLE_LOADING_IMAGES = 0x5B,
ASSET_TYPE_MEDAL = 0x5C,
ASSET_TYPE_MEDALTABLE = 0x5D,
ASSET_TYPE_OBJECTIVE = 0x5E,
ASSET_TYPE_OBJECTIVE_LIST = 0x5F,
ASSET_TYPE_UMBRA_TOME = 0x60,
ASSET_TYPE_NAVMESH = 0x61,
ASSET_TYPE_NAVVOLUME = 0x62,
ASSET_TYPE_BINARYHTML = 0x63,
ASSET_TYPE_LASER = 0x64,
ASSET_TYPE_BEAM = 0x65,
ASSET_TYPE_STREAMER_HINT = 0x66,
ASSET_TYPE_COUNT = 0x67,
ASSET_TYPE_STRING = 0x68,
ASSET_TYPE_ASSETLIST = 0x69,
ASSET_TYPE_REPORT = 0x6A,
ASSET_TYPE_DEPEND = 0x68,
ASSET_TYPE_FULL_COUNT = 0x6C,
};
struct LuaFile
{
const char* name;
int len;
const char* buffer;
};
union XAssetHeader
{
void* data;
LuaFile* luaFile;
};
struct XZoneBuffer struct XZoneBuffer
{ {
const void* data; const void* data;
@ -495,6 +621,23 @@ namespace game
DVAR_TYPE_COUNT = 0x10, DVAR_TYPE_COUNT = 0x10,
}; };
enum dvarFlags_e
{
DVAR_ARCHIVE = 1 << 0,
DVAR_USERINFO = 1 << 1,
DVAR_SYSTEMINFO = 1 << 2,
DVAR_CODINFO = 1 << 3,
DVAR_LATCH = 1 << 4,
DVAR_ROM = 1 << 5,
DVAR_SAVED = 1 << 6,
DVAR_INIT = 1 << 7,
DVAR_CHEAT = 1 << 8,
//DVAR_UNKNOWN = 1 << 9,
DVAR_EXTERNAL = 1 << 10,
//DVAR_UNKNOWN3x = 1 << 11-13,
DVAR_SESSIONMODE = 1 << 15
};
typedef float vec_t; typedef float vec_t;
union vec4_t union vec4_t
@ -870,6 +1013,512 @@ namespace game
JoinResult joinResult; JoinResult joinResult;
}; };
namespace hks
{
struct lua_State;
struct HashTable;
struct StringTable;
struct cclosure;
typedef int hksBool;
typedef char hksChar;
typedef unsigned __int8 hksByte;
typedef __int16 hksShort16;
typedef unsigned __int16 hksUshort16;
typedef float HksNumber;
typedef int hksInt32;
typedef unsigned int hksUint32;
typedef __int64 hksInt64;
typedef unsigned __int64 hksUint64;
typedef int HksGcCost;
typedef size_t hksSize;
typedef void* (*lua_Alloc)(void*, void*, size_t, size_t);
typedef hksInt32(*lua_CFunction)(lua_State*);
struct GenericChunkHeader
{
hksSize m_flags;
};
struct ChunkHeader : GenericChunkHeader
{
ChunkHeader* m_next;
};
struct ChunkList
{
ChunkHeader m_head;
};
struct UserData : ChunkHeader
{
unsigned __int64 m_envAndSizeOffsetHighBits;
unsigned __int64 m_metaAndSizeOffsetLowBits;
char m_data[8];
};
struct InternString
{
unsigned __int64 m_flags;
unsigned __int64 m_lengthbits;
unsigned int m_hash;
char m_data[30];
};
union HksValue
{
cclosure* cClosure;
void* closure;
UserData* userData;
HashTable* table;
void* tstruct;
InternString* str;
void* thread;
void* ptr;
float number;
unsigned int native;
bool boolean;
};
enum HksObjectType
{
TANY = 0xFFFFFFFE,
TNONE = 0xFFFFFFFF,
TNIL = 0x0,
TBOOLEAN = 0x1,
TLIGHTUSERDATA = 0x2,
TNUMBER = 0x3,
TSTRING = 0x4,
TTABLE = 0x5,
TFUNCTION = 0x6, // idk
TUSERDATA = 0x7,
TTHREAD = 0x8,
TIFUNCTION = 0x9, // Lua function
TCFUNCTION = 0xA, // C function
TUI64 = 0xB,
TSTRUCT = 0xC,
NUM_TYPE_OBJECTS = 0xE,
};
struct HksObject
{
HksObjectType t;
HksValue v;
};
const struct hksInstruction
{
unsigned int code;
};
struct ActivationRecord
{
HksObject* m_base;
const hksInstruction* m_returnAddress;
__int16 m_tailCallDepth;
__int16 m_numVarargs;
int m_numExpectedReturns;
};
struct CallStack
{
ActivationRecord* m_records;
ActivationRecord* m_lastrecord;
ActivationRecord* m_current;
const hksInstruction* m_current_lua_pc;
const hksInstruction* m_hook_return_addr;
int m_hook_level;
};
struct ApiStack
{
HksObject* top;
HksObject* base;
HksObject* alloc_top;
HksObject* bottom;
};
struct UpValue : ChunkHeader
{
HksObject m_storage;
HksObject* loc;
UpValue* m_next;
};
struct CallSite
{
_SETJMP_FLOAT128 m_jumpBuffer[16];
CallSite* m_prev;
};
enum Status
{
NEW = 0x1,
RUNNING = 0x2,
YIELDED = 0x3,
DEAD_ERROR = 0x4,
};
enum HksError
{
HKS_NO_ERROR = 0,
HKS_ERRSYNTAX = -4,
HKS_ERRFILE = -5,
HKS_ERRRUN = -100,
HKS_ERRMEM = -200,
HKS_ERRERR = -300,
HKS_THROWING_ERROR = -500,
HKS_GC_YIELD = 1,
};
struct lua_Debug
{
int event;
const char* name;
const char* namewhat;
const char* what;
const char* source;
int currentline;
int nups;
int nparams;
int ishksfunc;
int linedefined;
int lastlinedefined;
char short_src[512];
int callstack_level;
int is_tail_call;
};
using lua_function = int(__fastcall*)(lua_State*);
struct luaL_Reg
{
const char* name;
lua_function function;
};
struct Node
{
HksObject m_key;
HksObject m_value;
};
struct StringPinner
{
struct Node
{
InternString* m_strings[32];
Node* m_prev;
};
lua_State* const m_state;
StringPinner* const m_prev;
InternString** m_nextStringsPlace;
Node m_firstNode;
Node* m_currentNode;
};
struct StringTable
{
InternString** m_data;
unsigned int m_count;
unsigned int m_mask;
StringPinner* m_pinnedStrings;
};
struct Metatable
{
};
struct HashTable : ChunkHeader
{
Metatable* m_meta;
unsigned int m_version;
unsigned int m_mask;
Node* m_hashPart;
HksObject* m_arrayPart;
unsigned int m_arraySize;
Node* m_freeNode;
};
struct cclosure : ChunkHeader
{
lua_function m_function;
HashTable* m_env;
__int16 m_numUpvalues;
__int16 m_flags;
InternString* m_name;
HksObject m_upvalues[1];
};
enum HksCompilerSettings_BytecodeSharingFormat
{
BYTECODE_DEFAULT = 0x0,
BYTECODE_INPLACE = 0x1,
BYTECODE_REFERENCED = 0x2,
};
enum HksCompilerSettings_IntLiteralOptions
{
INT_LITERALS_NONE = 0x0,
INT_LITERALS_LUD = 0x1,
INT_LITERALS_32BIT = 0x1,
INT_LITERALS_UI64 = 0x2,
INT_LITERALS_64BIT = 0x2,
INT_LITERALS_ALL = 0x3,
};
struct HksCompilerSettings
{
int m_emitStructCode;
const char** m_stripNames;
int m_emitGlobalMemoization;
int _m_isHksGlobalMemoTestingMode;
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
int(*m_debugMap)(const char*, int);
};
enum HksBytecodeSharingMode : __int64
{
HKS_BYTECODE_SHARING_OFF = 0,
HKS_BYTECODE_SHARING_ON = 1,
HKS_BYTECODE_SHARING_SECURE = 2
};
struct HksGcWeights
{
int m_removeString;
int m_finalizeUserdataNoMM;
int m_finalizeUserdataGcMM;
int m_cleanCoroutine;
int m_removeWeak;
int m_markObject;
int m_traverseString;
int m_traverseUserdata;
int m_traverseCoroutine;
int m_traverseWeakTable;
int m_freeChunk;
int m_sweepTraverse;
};
struct GarbageCollector_Stack
{
void* m_storage;
unsigned int m_numEntries;
unsigned int m_numAllocated;
};
struct ProtoList
{
void** m_protoList;
unsigned __int16 m_protoSize;
unsigned __int16 m_protoAllocSize;
};
struct MemoryManager;
struct GarbageCollector
{
struct ResumeStack
{
void* m_storage;
hksInt32 m_numEntries;
hksUint32 m_numAllocated;
};
struct GreyStack
{
HksObject* m_storage;
hksSize m_numEntries;
hksSize m_numAllocated;
};
struct RemarkStack
{
HashTable** m_storage;
hksSize m_numAllocated;
hksSize m_numEntries;
};
struct WeakStack_Entry
{
hksInt32 m_weakness;
HashTable* m_table;
};
struct WeakStack
{
WeakStack_Entry* m_storage;
hksInt32 m_numEntries;
hksUint32 m_numAllocated;
};
HksGcCost m_target;
HksGcCost m_stepsLeft;
HksGcCost m_stepLimit;
HksGcWeights m_costs;
HksGcCost m_unit;
void* m_jumpPoint;
lua_State* m_mainState;
lua_State* m_finalizerState;
MemoryManager* m_memory;
void* m_emergencyGCMemory;
hksInt32 m_phase;
ResumeStack m_resumeStack;
GreyStack m_greyStack;
RemarkStack m_remarkStack;
WeakStack m_weakStack;
hksBool m_finalizing;
HksObject m_safeTableValue;
lua_State* m_startOfStateStackList;
lua_State* m_endOfStateStackList;
lua_State* m_currentState;
HksObject m_safeValue;
void* m_compiler;
void* m_bytecodeReader;
void* m_bytecodeWriter;
hksInt32 m_pauseMultiplier;
HksGcCost m_stepMultiplier;
hksSize m_emergencyMemorySize;
bool m_stopped;
lua_CFunction m_gcPolicy;
hksSize m_pauseTriggerMemoryUsage;
hksInt32 m_stepTriggerCountdown;
hksUint32 m_stringTableIndex;
hksUint32 m_stringTableSize;
UserData* m_lastBlackUD;
UserData* m_activeUD;
};
enum MemoryManager_ChunkColor
{
RED = 0x0,
BLACK = 0x1,
};
enum Hks_DeleteCheckingMode
{
HKS_DELETE_CHECKING_OFF = 0x0,
HKS_DELETE_CHECKING_ACCURATE = 0x1,
HKS_DELETE_CHECKING_SAFE = 0x2,
};
struct MemoryManager
{
enum ChunkColor : __int32
{
WHITE = 0x0,
BLACK = 0x1,
};
lua_Alloc m_allocator;
void* m_allocatorUd;
ChunkColor m_chunkColor;
hksSize m_used;
hksSize m_highwatermark;
ChunkList m_allocationList;
ChunkList m_sweepList;
ChunkHeader* m_lastKeptChunk;
lua_State* m_state;
};
struct StaticStringCache
{
HksObject m_objects[41];
};
enum HksBytecodeEndianness
{
HKS_BYTECODE_DEFAULT_ENDIAN = 0x0,
HKS_BYTECODE_BIG_ENDIAN = 0x1,
HKS_BYTECODE_LITTLE_ENDIAN = 0x2,
};
struct RuntimeProfileData_Stats
{
unsigned __int64 hksTime;
unsigned __int64 callbackTime;
unsigned __int64 gcTime;
unsigned __int64 cFinalizerTime;
unsigned __int64 compilerTime;
unsigned int hkssTimeSamples;
unsigned int callbackTimeSamples;
unsigned int gcTimeSamples;
unsigned int compilerTimeSamples;
unsigned int num_newuserdata;
unsigned int num_tablerehash;
unsigned int num_pushstring;
unsigned int num_pushcfunction;
unsigned int num_newtables;
};
struct RuntimeProfileData
{
__int64 stackDepth;
__int64 callbackDepth;
unsigned __int64 lastTimer;
RuntimeProfileData_Stats frameStats;
unsigned __int64 gcStartTime;
unsigned __int64 finalizerStartTime;
unsigned __int64 compilerStartTime;
unsigned __int64 compilerStartGCTime;
unsigned __int64 compilerStartGCFinalizerTime;
unsigned __int64 compilerCallbackStartTime;
__int64 compilerDepth;
void* outFile;
lua_State* rootState;
};
struct HksGlobal
{
MemoryManager m_memory;
GarbageCollector m_collector;
StringTable m_stringTable;
__int64 padding3;
HksBytecodeSharingMode m_bytecodeSharingMode;
int padding;
HksObject m_registry;
ChunkList m_userDataList;
lua_State* m_root;
StaticStringCache m_staticStringCache;
void* m_debugger;
void* m_profiler;
RuntimeProfileData m_runProfilerData;
HksCompilerSettings m_compilerSettings;
int(*m_panicFunction)(lua_State*);
void* m_luaplusObjectList;
int m_heapAssertionFrequency;
int m_heapAssertionCount;
void (*m_logFunction)(lua_State*, const char*, ...);
void (*m_emergencyGCFailFunction)(lua_State*, size_t);
HksBytecodeEndianness m_bytecodeDumpEndianness;
int padding2;
};
struct lua_State
{
ChunkHeader baseclass;
HksGlobal* m_global;
CallStack m_callStack;
ApiStack m_apistack;
UpValue* pending;
HksObject globals;
HksObject m_cEnv;
CallSite* m_callsites;
int m_numberOfCCalls;
void* m_context;
InternString* m_name;
lua_State* m_nextState;
lua_State* m_nextStateStack;
Status m_status;
HksError m_error;
};
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -35,6 +35,7 @@ namespace game
WEAK symbol<void(XZoneInfo* zoneInfo, uint32_t zoneCount, bool sync, bool suppressSync)> DB_LoadXAssets{ WEAK symbol<void(XZoneInfo* zoneInfo, uint32_t zoneCount, bool sync, bool suppressSync)> DB_LoadXAssets{
0x1414236A0 0x1414236A0
}; };
WEAK symbol<XAssetHeader(XAssetType type, const char* name, bool errorIfMissing, int waitTime)> DB_FindXAssetHeader{ 0x141420ED0 };
// Live // Live
WEAK symbol<bool(uint64_t, int*, bool)> Live_GetConnectivityInformation{0x141E0C410}; WEAK symbol<bool(uint64_t, int*, bool)> Live_GetConnectivityInformation{0x141E0C410};
@ -53,7 +54,7 @@ namespace game
WEAK symbol<TLSData*()> Sys_GetTLS{0x142184210}; WEAK symbol<TLSData*()> Sys_GetTLS{0x142184210};
// Dvar // Dvar
WEAK symbol<const dvar_t*(const char* dvarName)> Dvar_FindVar{0x1422BD730}; WEAK symbol<dvar_t*(const char* dvarName)> Dvar_FindVar{0x1422BD730};
WEAK symbol<unsigned int(const char* str)> Dvar_GenerateHash{0x14133DBF0}; WEAK symbol<unsigned int(const char* str)> Dvar_GenerateHash{0x14133DBF0};
WEAK symbol<dvar_t*(unsigned int hash)> Dvar_FindMalleableVar{0x1422BD6A0}; WEAK symbol<dvar_t*(unsigned int hash)> Dvar_FindMalleableVar{0x1422BD6A0};
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_GetDebugName{0x1422BDCB0}; WEAK symbol<const char*(const dvar_t* dvar)> Dvar_GetDebugName{0x1422BDCB0};
@ -97,4 +98,24 @@ namespace game
// Re-implementations // Re-implementations
eModes Com_SessionMode_GetMode(); eModes Com_SessionMode_GetMode();
namespace hks
{
WEAK symbol<lua_State*> lua_state { 0x159C78D88 };
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{ 0x140A18430 };
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{ 0x141D4B660 };
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{ 0x141D4ABF0 };
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{ 0x141D71070 };
WEAK symbol<HashTable* (lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{ 0x141D3B5F0 };
WEAK symbol<cclosure* (lua_State* s, lua_function function, int num_upvalues, int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{ 0x141D3B7E0 };
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{ 0x141D4D1A0 };
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{ 0x141D4D320 };
WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff, unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{ 0x141D4BD80 };
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{ 0x141D4D960 };
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{ 0x141D4DC20 };
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{ 0x141D4D050 };
WEAK symbol<const char*> s_compilerTypeName{ 0x140A18430 };
}
} }

View File

@ -0,0 +1,171 @@
#include <std_include.hpp>
#include "execution.hpp"
//#include "component/console.hpp"
namespace ui_scripting
{
namespace
{
script_value get_field(void* ptr, game::hks::HksObjectType type, const script_value& key)
{
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(key);
game::hks::HksObject value{};
game::hks::HksObject obj{};
obj.t = type;
obj.v.ptr = ptr;
game::hks::hks_obj_gettable(&value, state, &obj, &state->m_apistack.top[-1]);
state->m_apistack.top = top;
return value;
}
void set_field(void* ptr, game::hks::HksObjectType type, const script_value& key, const script_value& value)
{
const auto state = *game::hks::lua_state;
game::hks::HksObject obj{};
obj.t = type;
obj.v.ptr = ptr;
game::hks::hks_obj_settable(state, &obj, &key.get_raw(), &value.get_raw());
}
}
void push_value(const script_value& value)
{
const auto state = *game::hks::lua_state;
*state->m_apistack.top = value.get_raw();
state->m_apistack.top++;
}
void push_value(const game::hks::HksObject& value)
{
const auto state = *game::hks::lua_state;
*state->m_apistack.top = value;
state->m_apistack.top++;
}
script_value get_return_value(std::int64_t offset)
{
const auto state = *game::hks::lua_state;
return state->m_apistack.top[-1 - offset];
}
arguments get_return_values()
{
const auto state = *game::hks::lua_state;
const auto count = state->m_apistack.top - state->m_apistack.base;
arguments values;
for (auto i = count - 1; i >= 0; i--)
{
values.push_back(get_return_value(i));
}
if (values.empty())
{
values.push_back({});
}
return values;
}
arguments get_return_values(game::hks::HksObject* base)
{
const auto state = *game::hks::lua_state;
const auto count = state->m_apistack.top - base;
arguments values;
for (auto i = count - 1; i >= 0; i--)
{
values.push_back(get_return_value(i));
}
if (values.empty())
{
values.push_back({});
}
return values;
}
bool notify(const std::string& name, const event_arguments& arguments)
{
const auto state = *game::hks::lua_state;
if (state == nullptr)
{
return false;
}
//const auto _0 = gsl::finally(game::LUI_LeaveCriticalSection);
//game::LUI_EnterCriticalSection();
try
{
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
const auto engine = globals.get("Engine").as<table>();
const auto root = engine.get("GetLuiRoot")()[0].as<userdata>();
const auto process_event = root.get("processEvent");
table event{};
event.set("name", name);
event.set("dispatchChildren", true);
for (const auto& arg : arguments)
{
event.set(arg.first, arg.second);
}
process_event(root, event);
return true;
}
catch (const std::exception& ex)
{
printf("Error processing event '%s' %s\n", name.data(), ex.what());
}
return false;
}
arguments call_script_function(const function& function, const arguments& arguments)
{
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(function);
for (auto i = arguments.begin(); i != arguments.end(); ++i)
{
push_value(*i);
}
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, nullptr);
const auto args = get_return_values(top);
state->m_apistack.top = top;
return args;
}
script_value get_field(const userdata& self, const script_value& key)
{
return get_field(self.ptr, game::hks::TUSERDATA, key);
}
script_value get_field(const table& self, const script_value& key)
{
return get_field(self.ptr, game::hks::TTABLE, key);
}
void set_field(const userdata& self, const script_value& key, const script_value& value)
{
set_field(self.ptr, game::hks::TUSERDATA, key, value);
}
void set_field(const table& self, const script_value& key, const script_value& value)
{
set_field(self.ptr, game::hks::TTABLE, key, value);
}
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "game/game.hpp"
#include "types.hpp"
#include "script_value.hpp"
namespace ui_scripting
{
void push_value(const script_value& value);
void push_value(const game::hks::HksObject& value);
script_value get_return_value(std::int64_t offset);
arguments get_return_values();
arguments get_return_values(game::hks::HksObject* base);
bool notify(const std::string& name, const event_arguments& arguments);
arguments call_script_function(const function& function, const arguments& arguments);
script_value get_field(const userdata& self, const script_value& key);
script_value get_field(const table& self, const script_value& key);
void set_field(const userdata& self, const script_value& key, const script_value& value);
void set_field(const table& self, const script_value& key, const script_value& value);
}

View File

@ -0,0 +1,400 @@
#include <std_include.hpp>
#include "execution.hpp"
#include "types.hpp"
#include "script_value.hpp"
namespace ui_scripting
{
hks_object::hks_object(const game::hks::HksObject& value)
{
this->assign(value);
}
hks_object::hks_object(const hks_object& other) noexcept
{
this->operator=(other);
}
hks_object::hks_object(hks_object&& other) noexcept
{
this->operator=(std::move(other));
}
hks_object& hks_object::operator=(const hks_object& other) noexcept
{
if (this != &other)
{
this->release();
this->assign(other.value_);
}
return *this;
}
hks_object& hks_object::operator=(hks_object&& other) noexcept
{
if (this != &other)
{
this->release();
this->value_ = other.value_;
other.value_.t = game::hks::TNONE;
}
return *this;
}
hks_object::~hks_object()
{
this->release();
}
const game::hks::HksObject& hks_object::get() const
{
return this->value_;
}
void hks_object::assign(const game::hks::HksObject& value)
{
this->value_ = value;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(this->value_);
this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void hks_object::release()
{
if (this->ref_)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_);
this->value_.t = game::hks::TNONE;
}
}
/***************************************************************
* Constructors
**************************************************************/
script_value::script_value(const game::hks::HksObject& value)
: value_(value)
{
}
script_value::script_value(const int value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
script_value::script_value(const unsigned int value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
script_value::script_value(const bool value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TBOOLEAN;
obj.v.boolean = value;
this->value_ = obj;
}
script_value::script_value(const float value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
script_value::script_value(const double value)
: script_value(static_cast<float>(value))
{
}
script_value::script_value(const char* value, const std::size_t len)
{
game::hks::HksObject obj{};
const auto state = *game::hks::lua_state;
if (state == nullptr)
{
return;
}
const auto top = state->m_apistack.top;
game::hks::hksi_lua_pushlstring(state, value, static_cast<std::uint32_t>(len));
obj = state->m_apistack.top[-1];
state->m_apistack.top = top;
this->value_ = obj;
}
script_value::script_value(const char* value)
: script_value(value, std::strlen(value))
{
}
script_value::script_value(const std::string& value)
: script_value(value.data(), value.size())
{
}
script_value::script_value(const lightuserdata& value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TLIGHTUSERDATA;
obj.v.ptr = value.ptr;
this->value_ = obj;
}
script_value::script_value(const userdata& value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TUSERDATA;
obj.v.ptr = value.ptr;
this->value_ = obj;
}
script_value::script_value(const table& value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TTABLE;
obj.v.ptr = value.ptr;
this->value_ = obj;
}
script_value::script_value(const function& value)
{
game::hks::HksObject obj{};
obj.t = value.type;
obj.v.ptr = value.ptr;
this->value_ = obj;
}
/***************************************************************
* Integer
**************************************************************/
template <>
bool script_value::is<int>() const
{
const auto number = this->get_raw().v.number;
return this->get_raw().t == game::hks::TNUMBER && static_cast<int>(number) == number;
}
template <>
bool script_value::is<unsigned int>() const
{
return this->is<int>();
}
template <>
int script_value::get() const
{
return static_cast<int>(this->get_raw().v.number);
}
template <>
unsigned int script_value::get() const
{
return static_cast<unsigned int>(this->get_raw().v.number);
}
/***************************************************************
* Boolean
**************************************************************/
template <>
bool script_value::is<bool>() const
{
return this->get_raw().t == game::hks::TBOOLEAN;
}
template <>
bool script_value::get() const
{
return this->get_raw().v.boolean;
}
/***************************************************************
* Float
**************************************************************/
template <>
bool script_value::is<float>() const
{
return this->get_raw().t == game::hks::TNUMBER;
}
template <>
bool script_value::is<double>() const
{
return this->is<float>();
}
template <>
float script_value::get() const
{
return this->get_raw().v.number;
}
template <>
double script_value::get() const
{
return this->get_raw().v.number;
}
/***************************************************************
* String
**************************************************************/
template <>
bool script_value::is<const char*>() const
{
return this->get_raw().t == game::hks::TSTRING;
}
template <>
bool script_value::is<std::string>() const
{
return this->is<const char*>();
}
template <>
const char* script_value::get() const
{
return this->get_raw().v.str->m_data;
}
template <>
std::string script_value::get() const
{
return this->get<const char*>();
}
/***************************************************************
* Lightuserdata
**************************************************************/
template <>
bool script_value::is<lightuserdata>() const
{
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
}
template <>
lightuserdata script_value::get() const
{
return this->get_raw().v.ptr;
}
/***************************************************************
* Userdata
**************************************************************/
template <>
bool script_value::is<userdata>() const
{
return this->get_raw().t == game::hks::TUSERDATA;
}
template <>
userdata script_value::get() const
{
return this->get_raw().v.ptr;
}
/***************************************************************
* Table
**************************************************************/
template <>
bool script_value::is<table>() const
{
return this->get_raw().t == game::hks::TTABLE;
}
template <>
table script_value::get() const
{
return this->get_raw().v.table;
}
/***************************************************************
* Function
**************************************************************/
template <>
bool script_value::is<function>() const
{
return this->get_raw().t == game::hks::TIFUNCTION
|| this->get_raw().t == game::hks::TCFUNCTION;
}
template <>
function script_value::get() const
{
return { this->get_raw().v.cClosure, this->get_raw().t };
}
/***************************************************************
*
**************************************************************/
const game::hks::HksObject& script_value::get_raw() const
{
return this->value_.get();
}
bool script_value::operator==(const script_value& other) const
{
if (this->get_raw().t != other.get_raw().t)
{
return false;
}
if (this->get_raw().t == game::hks::TSTRING)
{
return this->get<std::string>() == other.get<std::string>();
}
return this->get_raw().v.native == other.get_raw().v.native;
}
arguments script_value::operator()() const
{
return this->as<function>()();
}
arguments script_value::operator()(const arguments& arguments) const
{
return this->as<function>()(arguments);
}
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
: values_(args), value_(value), index_(index)
{
}
function_arguments::function_arguments(const arguments& values)
: values_(values)
{
}
}

View File

@ -0,0 +1,251 @@
#pragma once
#include "game/game.hpp"
#include <utils/string.hpp>
namespace ui_scripting
{
class lightuserdata;
class userdata_value;
class userdata;
class table_value;
class table;
class function;
class script_value;
template <typename T>
std::string get_typename()
{
auto& info = typeid(T);
if (info == typeid(std::string) ||
info == typeid(const char*))
{
return "string";
}
if (info == typeid(lightuserdata))
{
return "lightuserdata";
}
if (info == typeid(userdata))
{
return "userdata";
}
if (info == typeid(table))
{
return "table";
}
if (info == typeid(function))
{
return "function";
}
if (info == typeid(int) ||
info == typeid(float) ||
info == typeid(unsigned int))
{
return "number";
}
if (info == typeid(bool))
{
return "boolean";
}
return info.name();
}
class hks_object
{
public:
hks_object() = default;
hks_object(const game::hks::HksObject& value);
hks_object(const hks_object& other) noexcept;
hks_object(hks_object&& other) noexcept;
hks_object& operator=(const hks_object& other) noexcept;
hks_object& operator=(hks_object&& other) noexcept;
~hks_object();
const game::hks::HksObject& get() const;
private:
void assign(const game::hks::HksObject& value);
void release();
game::hks::HksObject value_{ game::hks::TNONE, {} };
int ref_{};
};
using arguments = std::vector<script_value>;
using event_arguments = std::unordered_map<std::string, script_value>;
class script_value
{
public:
script_value() = default;
script_value(const game::hks::HksObject& value);
script_value(int value);
script_value(unsigned int value);
script_value(bool value);
script_value(float value);
script_value(double value);
script_value(const char* value, std::size_t len);
script_value(const char* value);
script_value(const std::string& value);
script_value(const lightuserdata& value);
script_value(const userdata& value);
script_value(const table& value);
script_value(const function& value);
template <template<class, class> class C, class T, typename TableType = table>
script_value(const C<T, std::allocator<T>>& container)
{
TableType table_{};
int index = 1;
for (const auto& value : container)
{
table_.set(index++, value);
}
game::hks::HksObject obj{};
obj.t = game::hks::TTABLE;
obj.v.ptr = table_.ptr;
this->value_ = obj;
}
template <typename F>
script_value(F f)
: script_value(function(f))
{
}
bool operator==(const script_value& other) const;
arguments operator()() const;
arguments operator()(const arguments& arguments) const;
template<class ...T>
arguments operator()(T... arguments) const
{
return this->as<function>().call({ arguments... });
}
template <size_t Size>
table_value operator[](const char(&key)[Size]) const
{
return { this->as<table>(), key };
}
template <typename T = script_value>
table_value operator[](const T& key) const
{
return { this->as<table>(), key };
}
template <typename T>
[[nodiscard]] bool is() const;
template <typename T>
T as() const
{
if (!this->is<T>())
{
const auto hks_typename = game::hks::s_compilerTypeName[this->get_raw().t + 2];
const auto typename_ = get_typename<T>();
throw std::runtime_error(utils::string::va("%s expected, got %s", typename_.data(), hks_typename));
}
return get<T>();
}
template <typename T>
operator T() const
{
return this->as<T>();
}
[[nodiscard]] const game::hks::HksObject& get_raw() const;
hks_object value_{};
private:
template <typename T>
T get() const;
};
class variadic_args : public arguments
{
};
class function_argument
{
public:
function_argument(const arguments& args, const script_value& value, const int index);
template <typename T>
T as() const
{
try
{
return this->value_.as<T>();
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("bad argument #%d (%s)", this->index_ + 1, e.what()));
}
}
template <>
variadic_args as() const
{
variadic_args args{};
for (auto i = this->index_; i < this->values_.size(); i++)
{
args.push_back(this->values_[i]);
}
return args;
}
template <typename T>
operator T() const
{
return this->as<T>();
}
private:
arguments values_{};
script_value value_{};
int index_{};
};
class function_arguments
{
public:
function_arguments(const arguments& values);
function_argument operator[](const int index) const
{
if (static_cast<std::size_t>(index) >= values_.size())
{
return { values_, {}, index };
}
return { values_, values_[index], index };
}
private:
arguments values_{};
};
}

View File

@ -0,0 +1,351 @@
#include <std_include.hpp>
#include "types.hpp"
#include "execution.hpp"
namespace ui_scripting
{
/***************************************************************
* Lightuserdata
**************************************************************/
lightuserdata::lightuserdata(void* ptr_)
: ptr(ptr_)
{
}
/***************************************************************
* Userdata
**************************************************************/
userdata::userdata(void* ptr_)
: ptr(ptr_)
{
this->add();
}
userdata::userdata(const userdata& other)
{
this->operator=(other);
}
userdata::userdata(userdata&& other) noexcept
{
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
userdata::~userdata()
{
this->release();
}
userdata& userdata::operator=(const userdata& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
userdata& userdata::operator=(userdata&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void userdata::add()
{
game::hks::HksObject value{};
value.v.ptr = this->ptr;
value.t = game::hks::TUSERDATA;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void userdata::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void userdata::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
script_value userdata::get(const script_value& key) const
{
return get_field(*this, key);
}
userdata_value userdata::operator[](const script_value& key) const
{
return { *this, key };
}
userdata_value::userdata_value(const userdata& table, const script_value& key)
: userdata_(table), key_(key)
{
this->value_ = this->userdata_.get(key).get_raw();
}
void userdata_value::operator=(const script_value& value)
{
this->userdata_.set(this->key_, value);
this->value_ = value.get_raw();
}
bool userdata_value::operator==(const script_value& value)
{
return this->userdata_.get(this->key_) == value;
}
/***************************************************************
* Table
**************************************************************/
table::table()
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
this->add();
}
table::table(game::hks::HashTable* ptr_)
: ptr(ptr_)
{
this->add();
}
table::table(const table& other)
{
this->operator=(other);
}
table::table(table&& other) noexcept
{
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
table::~table()
{
this->release();
}
table& table::operator=(const table& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
this->add();
}
return *this;
}
table& table::operator=(table&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void table::add()
{
game::hks::HksObject value{};
value.v.table = this->ptr;
value.t = game::hks::TTABLE;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void table::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
void table::set(const script_value& key, const script_value& value) const
{
set_field(*this, key, value);
}
table_value table::operator[](const script_value& key) const
{
return { *this, key };
}
script_value table::get(const script_value& key) const
{
return get_field(*this, key);
}
table_value::table_value(const table& table, const script_value& key)
: table_(table), key_(key)
{
this->value_ = this->table_.get(key).get_raw();
}
void table_value::operator=(const script_value& value)
{
this->table_.set(this->key_, value);
this->value_ = value.get_raw();
}
void table_value::operator=(const table_value& value)
{
this->table_.set(this->key_, value);
this->value_ = value.get_raw();
}
bool table_value::operator==(const script_value& value)
{
return this->table_.get(this->key_) == value;
}
bool table_value::operator==(const table_value& value)
{
return this->table_.get(this->key_) == value;
}
/***************************************************************
* Function
**************************************************************/
function::function(game::hks::lua_function func)
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0);
this->type = game::hks::HksObjectType::TCFUNCTION;
this->add();
}
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
: ptr(ptr_), type(type_)
{
this->add();
}
function::function(const function& other)
{
this->operator=(other);
}
function::function(function&& other) noexcept
{
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
function::~function()
{
this->release();
}
function& function::operator=(const function& other)
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
this->add();
}
return *this;
}
function& function::operator=(function&& other) noexcept
{
if (&other != this)
{
this->release();
this->ptr = other.ptr;
this->type = other.type;
this->ref = other.ref;
other.ref = 0;
}
return *this;
}
void function::add()
{
game::hks::HksObject value{};
value.v.cClosure = this->ptr;
value.t = this->type;
const auto state = *game::hks::lua_state;
const auto top = state->m_apistack.top;
push_value(value);
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
state->m_apistack.top = top;
}
void function::release()
{
if (this->ref)
{
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
}
}
arguments function::call(const arguments& arguments) const
{
return call_script_function(*this, arguments);
}
arguments function::operator()(const arguments& arguments) const
{
return this->call(arguments);
}
arguments function::operator()() const
{
return this->call({});
}
}

View File

@ -0,0 +1,142 @@
#pragma once
#include "game/game.hpp"
#include "script_value.hpp"
#include "component/ui_scripting.hpp"
namespace ui_scripting
{
class lightuserdata
{
public:
lightuserdata(void*);
void* ptr;
};
class userdata_value;
class userdata
{
public:
userdata(void*);
userdata(const userdata& other);
userdata(userdata&& other) noexcept;
~userdata();
userdata& operator=(const userdata& other);
userdata& operator=(userdata&& other) noexcept;
script_value get(const script_value& key) const;
void set(const script_value& key, const script_value& value) const;
userdata_value operator[](const script_value& key) const;
void* ptr;
private:
void add();
void release();
int ref{};
};
class userdata_value : public script_value
{
public:
userdata_value(const userdata& table, const script_value& key);
void operator=(const script_value& value);
bool operator==(const script_value& value);
private:
userdata userdata_;
script_value key_;
};
class table_value;
class table
{
public:
table();
table(game::hks::HashTable* ptr_);
table(const table& other);
table(table&& other) noexcept;
~table();
table& operator=(const table& other);
table& operator=(table&& other) noexcept;
[[nodiscard]] script_value get(const script_value& key) const;
void set(const script_value& key, const script_value& value) const;
table_value operator[](const script_value& key) const;
game::hks::HashTable* ptr;
private:
void add();
void release();
int ref{};
};
class table_value : public script_value
{
public:
table_value(const table& table, const script_value& key);
void operator=(const script_value& value);
void operator=(const table_value& value);
bool operator==(const script_value& value);
bool operator==(const table_value& value);
private:
table table_;
script_value key_;
};
class function
{
public:
function(game::hks::lua_function);
function(game::hks::cclosure*, game::hks::HksObjectType);
template <typename F>
function(F f)
{
this->ptr = ui_scripting::convert_function(f);
this->type = game::hks::TCFUNCTION;
}
function(const function& other);
function(function&& other) noexcept;
~function();
function& operator=(const function& other);
function& operator=(function&& other) noexcept;
arguments call(const arguments& arguments) const;
arguments operator()(const arguments& arguments) const;
template<class ...T>
arguments operator()(T... arguments) const
{
return this->call({ arguments... });
}
arguments operator()() const;
game::hks::cclosure* ptr;
game::hks::HksObjectType type;
private:
void add();
void release();
int ref{};
};
}