More LUI scripting support

This commit is contained in:
Federico Cecchetto 2021-10-01 00:55:14 +02:00
parent 401006ca8e
commit 2b6989692f
16 changed files with 1005 additions and 171 deletions

View File

@ -36,6 +36,12 @@ namespace lui
game::LUI_OpenMenu(0, params[1], 1, 0, 0);
});
command::add("lui_restart", []()
{
utils::hook::invoke<void>(game::base_address + 0x3203B0);
utils::hook::invoke<void>(game::base_address + 0x32D370);
});
}
};
}

View File

@ -21,19 +21,20 @@
namespace ui_scripting
{
std::unordered_map<std::string, game::hks::lua_function> functions;
std::unordered_map<std::string, game::hks::lua_function> methods;
namespace
{
utils::hook::detour hksi_open_lib_hook;
utils::hook::detour hksi_lual_error_hook;
utils::hook::detour hksi_lual_error_hook2;
utils::hook::detour hksi_add_function_hook;
utils::hook::detour hks_start_hook;
utils::hook::detour hks_shutdown_hook;
bool error_hook_enabled = false;
std::unordered_map<std::string, game::hks::lua_function> functions;
std::unordered_map<std::string, game::hks::lua_function> methods;
void* hksi_open_lib_stub(game::hks::lua_State* s, const char* libname, game::hks::luaL_Reg* l)
{
for (auto i = l; i->name; ++i)
@ -184,6 +185,7 @@ namespace ui_scripting
hks_shutdown_hook.create(game::base_address + 0x3203B0, hks_shutdown_stub);
hksi_open_lib_hook.create(game::base_address + 0x2E4530, hksi_open_lib_stub);
hksi_lual_error_hook.create(game::base_address + 0x2E3E40, hksi_lual_error_stub);
hksi_lual_error_hook2.create(game::base_address + 0x2DCB40, hksi_lual_error_stub);
hksi_add_function_hook.create(game::base_address + 0x2DB570, hksi_add_function_stub);
}
};

View File

@ -4,6 +4,9 @@
namespace ui_scripting
{
extern std::unordered_map<std::string, game::hks::lua_function> functions;
extern std::unordered_map<std::string, game::hks::lua_function> methods;
void enable_error_hook();
void disable_error_hook();

View File

@ -942,12 +942,14 @@ namespace game
char m_data[30];
};
struct HashTable;
union HksValue
{
void* cClosure;
void* closure;
UserData* userData;
void* table;
HashTable* table;
void* tstruct;
InternString* str;
void* thread;
@ -983,15 +985,31 @@ namespace game
HksValue v;
};
struct lua_State
struct CallStack
{
void* m_records;
void* m_lastrecord;
void* m_current;
const unsigned int* m_current_lua_pc;
const unsigned int* m_hook_return_addr;
int m_hook_level;
};
struct ApiStack
{
char __pad0[72];
HksObject* top;
HksObject* base;
HksObject* alloc_top;
HksObject* bottom;
};
struct lua_State
{
char __pad0[24];
CallStack m_callStack;
ApiStack m_apistack;
};
using lua_function = int(__fastcall*)(lua_State*);
struct luaL_Reg
@ -999,5 +1017,26 @@ namespace game
const char* name;
lua_function function;
};
struct Node
{
HksObject m_key;
HksObject m_value;
};
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;
};
}
}

View File

@ -155,5 +155,10 @@ namespace game
WEAK symbol<const char*(lua_State* s, const HksObject* obj, unsigned int* len)> hks_obj_tolstring{0x287410};
WEAK symbol<int(lua_State* s, const HksObject* obj, HksObject* ret)> hks_obj_getmetatable{0x2DA210};
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x2D9E20};
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x2DB040};
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x30AB60};
WEAK symbol<void(lua_State* s, int index)> hksi_lua_pushvalue{0x2DE040};
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x2C8290};
WEAK symbol<HksObject*(HashTable* t, HksObject* result, HksObject* key)> Hashtable_getNextHash{0x2D5150};
}
}

View File

@ -1,5 +1,4 @@
#include <std_include.hpp>
#include "value.hpp"
#include "execution.hpp"
#include "stack_isolation.hpp"
#include "component/ui_scripting.hpp"
@ -8,122 +7,206 @@
namespace ui_scripting
{
namespace
void push_value(const value& value)
{
bool valid_state()
{
return *game::hks::lua_state != 0;
}
void push_value(const value& value_)
{
if (!valid_state())
{
throw std::runtime_error("Invalid lua state");
}
const auto state = *game::hks::lua_state;
switch (value_.index())
{
case 1:
{
const auto value = std::get<bool>(value_);
state->top->t = game::hks::HksObjectType::TBOOLEAN;
state->top->v.boolean = value;
state->top++;
break;
}
case 2:
{
const auto value = std::get<int>(value_);
state->top->t = game::hks::HksObjectType::TNUMBER;
state->top->v.number = static_cast<float>(value);
state->top++;
break;
}
case 3:
{
const auto value = std::get<float>(value_);
state->top->t = game::hks::HksObjectType::TNUMBER;
state->top->v.number = value;
state->top++;
break;
}
case 4:
{
const auto str = std::get<std::string>(value_);
game::hks::hksi_lua_pushlstring(state, str.data(), (unsigned int)str.size());
break;
}
case 5:
{
const auto data = std::get<lightuserdata>(value_);
state->top->t = game::hks::HksObjectType::TLIGHTUSERDATA;
state->top->v.ptr = data.ptr;
state->top++;
break;
}
}
}
}
bool is_integer(float number)
{
return static_cast<int>(number) == number;
const auto state = *game::hks::lua_state;
const auto value_ = value.get_raw();
*state->m_apistack.top = value_;
state->m_apistack.top++;
}
value get_return_value(int offset)
{
if (!valid_state())
{
throw std::runtime_error("Invalid lua state");
}
const auto state = *game::hks::lua_state;
const auto top = &state->top[-1 - offset];
const auto top = &state->m_apistack.top[-1 - offset];
return *top;
}
switch (top->t)
void call_script_function(const function& function, const arguments& arguments)
{
const auto state = *game::hks::lua_state;
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
case game::hks::HksObjectType::TBOOLEAN:
{
return {top->v.boolean};
push_value(*i);
}
case game::hks::HksObjectType::TNUMBER:
push_value(function);
enable_error_hook();
const auto __ = gsl::finally([]()
{
const auto number = top->v.number;
if (is_integer(number))
{
return {static_cast<int>(top->v.number)};
}
else
{
return {top->v.number};
}
}
case game::hks::HksObjectType::TSTRING:
disable_error_hook();
});
// Not sure about this
try
{
const auto value = top->v.str->m_data;
return {std::string(value)};
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), 0, 0);
}
case game::hks::HksObjectType::TLIGHTUSERDATA:
catch (const std::exception& e)
{
return lightuserdata{top->v.ptr};
}
default:
{
return {};
}
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
}
}
std::vector<value> call(const std::string& name, const arguments& arguments)
value get_field(const userdata& self, const value& key)
{
if (!valid_state())
const auto state = *game::hks::lua_state;
stack_isolation _;
push_value(key);
enable_error_hook();
const auto __ = gsl::finally([]()
{
throw std::runtime_error("Invalid lua state");
disable_error_hook();
});
game::hks::HksObject value{};
game::hks::HksObject userdata{};
userdata.t = game::hks::TUSERDATA;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_getfield(&value, state, &userdata, &state->m_apistack.top[-1]);
return value;
}
catch (const std::exception& e)
{
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what());
}
}
value get_field(const table& self, const value& key)
{
const auto state = *game::hks::lua_state;
stack_isolation _;
push_value(key);
enable_error_hook();
const auto __ = gsl::finally([]()
{
disable_error_hook();
});
game::hks::HksObject value{};
game::hks::HksObject userdata{};
userdata.t = game::hks::TTABLE;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_getfield(&value, state, &userdata, &state->m_apistack.top[-1]);
return value;
}
catch (const std::exception& e)
{
throw std::runtime_error(std::string("Error getting table field: ") + e.what());
}
}
void set_field(const userdata& self, const value& key, const value& value)
{
const auto state = *game::hks::lua_state;
stack_isolation _;
enable_error_hook();
const auto __ = gsl::finally([]()
{
disable_error_hook();
});
game::hks::HksObject userdata{};
userdata.t = game::hks::TUSERDATA;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
}
catch (const std::exception& e)
{
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what());
}
}
void set_field(const table& self, const value& key, const value& value)
{
const auto state = *game::hks::lua_state;
stack_isolation _;
enable_error_hook();
const auto __ = gsl::finally([]()
{
disable_error_hook();
});
game::hks::HksObject userdata{};
userdata.t = game::hks::TTABLE;
userdata.v.ptr = self.ptr;
try
{
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
}
catch (const std::exception& e)
{
throw std::runtime_error(std::string("Error setting table field: ") + e.what());
}
}
arguments call_method(const userdata& self, const std::string& name, const arguments& arguments)
{
const auto function = ui_scripting::find_method(name);
if (!function)
{
throw std::runtime_error("Function " + name + " not found");
}
stack_isolation _;
push_value(self);
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
enable_error_hook();
const auto __ = gsl::finally([]()
{
disable_error_hook();
});
try
{
const auto count = function(*game::hks::lua_state);
std::vector<value> values;
for (auto i = 0; i < count; i++)
{
values.push_back(get_return_value(i));
}
if (values.size() == 0)
{
values.push_back({});
}
return values;
}
catch (const std::exception& e)
{
throw std::runtime_error("Error executing method " + name + ": " + e.what());
}
}
arguments call(const std::string& name, const arguments& arguments)
{
const auto function = ui_scripting::find_function(name);
if (!function)
{
@ -131,19 +214,17 @@ namespace ui_scripting
}
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
enable_error_hook();
const auto __ = gsl::finally([]()
{
disable_error_hook();
});
enable_error_hook();
for (const auto& value_ : arguments)
{
push_value(value_);
}
try
{
const auto count = function(*game::hks::lua_state);

View File

@ -1,7 +1,20 @@
#pragma once
#include "game/game.hpp"
#include "types.hpp"
#include "value.hpp"
namespace ui_scripting
{
std::vector<value> call(const std::string& name, const arguments& arguments);
void push_value(const value& value);
value get_return_value(int offset);
void call_script_function(const function& function, const arguments& arguments);
value get_field(const userdata& self, const value& key);
value get_field(const table& self, const value& key);
void set_field(const userdata& self, const value& key, const value& value);
void set_field(const table& self, const value& key, const value& value);
arguments call_method(const userdata& self, const std::string& name, const arguments& arguments);
arguments call(const std::string& name, const arguments& arguments);
}

View File

@ -1,6 +1,7 @@
#include <std_include.hpp>
#include "context.hpp"
#include "error.hpp"
#include "value_conversion.hpp"
#include "../../scripting/execution.hpp"
#include "../value.hpp"
#include "../execution.hpp"
@ -26,7 +27,7 @@ namespace ui_scripting::lua
{
const auto animation_script = utils::nt::load_resource(LUA_ANIMATION_SCRIPT);
scripting::script_value convert(const sol::lua_value& value)
scripting::script_value script_convert(const sol::lua_value& value)
{
if (value.is<int>())
{
@ -505,14 +506,7 @@ namespace ui_scripting::lua
for (auto arg : va)
{
if (arg.is<value>())
{
event.arguments.push_back(arg.as<value>());
}
else
{
event.arguments.push_back({});
}
event.arguments.push_back(convert({s, arg}));
}
notify(event);
@ -529,9 +523,9 @@ namespace ui_scripting::lua
}
);
element_type[sol::meta_function::new_index] = [](element& element, const std::string& attribute, const value& value)
element_type[sol::meta_function::new_index] = [](element& element, const sol::this_state s, const std::string& attribute, const sol::lua_value& value)
{
element.attributes[attribute] = value;
element.attributes[attribute] = convert({s, value});
};
element_type[sol::meta_function::index] = [](element& element, const sol::this_state s, const std::string& attribute)
@ -541,7 +535,7 @@ namespace ui_scripting::lua
return sol::lua_value{s, sol::lua_nil};
}
return sol::lua_value{s, element.attributes[attribute]};
return sol::lua_value{s, convert(s, element.attributes[attribute])};
};
auto menu_type = state.new_usertype<menu>("menu");
@ -579,14 +573,7 @@ namespace ui_scripting::lua
for (auto arg : va)
{
if (arg.is<value>())
{
event.arguments.push_back(arg.as<value>());
}
else
{
event.arguments.push_back({});
}
event.arguments.push_back(convert({s, arg}));
}
notify(event);
@ -655,11 +642,13 @@ namespace ui_scripting::lua
menu.close();
};
menu_type["getelement"] = [](menu& menu, const sol::this_state s, const value& value, const std::string& attribute)
menu_type["getelement"] = [](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute)
{
const auto value_ = convert({s, value});
for (const auto& element : menu.children)
{
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value)
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_)
{
return sol::lua_value{s, element};
}
@ -670,13 +659,14 @@ namespace ui_scripting::lua
menu_type["getelements"] = sol::overload
(
[](menu& menu, const sol::this_state s, const value& value, const std::string& attribute)
[](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute)
{
const auto value_ = convert({s, value});
auto result = sol::table::create(s.lua_state());
for (const auto& element : menu.children)
{
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value)
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_)
{
result.add(element);
}
@ -713,11 +703,13 @@ namespace ui_scripting::lua
return sol::lua_value{s, &menus[name]};
};
game_type["getelement"] = [](const game&, const sol::this_state s, const value& value, const std::string& attribute)
game_type["getelement"] = [](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute)
{
const auto value_ = convert({s, value});
for (const auto& element : elements)
{
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value)
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_)
{
return sol::lua_value{s, element};
}
@ -728,13 +720,14 @@ namespace ui_scripting::lua
game_type["getelements"] = sol::overload
(
[](const game&, const sol::this_state s, const value& value, const std::string& attribute)
[](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute)
{
const auto value_ = convert({s, value});
auto result = sol::table::create(s.lua_state());
for (const auto& element : elements)
{
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value)
if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_)
{
result.add(element);
}
@ -974,29 +967,128 @@ namespace ui_scripting::lua
{
return [name](const game&, const sol::this_state s, sol::variadic_args va)
{
arguments arguments;
arguments arguments{};
for (auto arg : va)
{
arguments.push_back(arg);
arguments.push_back(convert({s, arg}));
}
const auto values = call(name, arguments);
return sol::as_returns(values);
std::vector<sol::lua_value> returns;
for (const auto& value : values)
{
returns.push_back(convert(s, value));
}
return sol::as_returns(returns);
};
};
game_type["call"] = [](const game&, const sol::this_state s, const std::string& name, sol::variadic_args va)
{
arguments arguments;
arguments arguments{};
for (auto arg : va)
{
arguments.push_back(arg);
arguments.push_back(convert({s, arg}));
}
const auto values = call(name, arguments);
return sol::as_returns(values);
std::vector<sol::lua_value> returns;
for (const auto& value : values)
{
returns.push_back(convert(s, value));
}
return sol::as_returns(returns);
};
auto userdata_type = state.new_usertype<userdata>("userdata_");
for (const auto method : methods)
{
const auto name = method.first;
userdata_type[name] = [name](const userdata& userdata, const sol::this_state s, sol::variadic_args va)
{
arguments arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
const auto values = call_method(userdata, name, arguments);
std::vector<sol::lua_value> returns;
for (const auto& value : values)
{
returns.push_back(convert(s, value));
}
return sol::as_returns(returns);
};
}
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
const std::string& name)
{
return convert(s, userdata.get(name));
};
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
const std::string& name, const sol::lua_value& value)
{
userdata.set(name, convert({s, value}));
};
auto table_type = state.new_usertype<table>("table_");
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
const std::string& name)
{
return convert(s, table.get(name));
};
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
const std::string& name, const sol::lua_value& value)
{
table.set(name, convert({s, value}));
};
table_type[sol::meta_function::length] = [](const table& table)
{
return table.size();
};
table_type["getkeys"] = [](const table& table, const sol::this_state s)
{
std::vector<sol::lua_value> result;
const auto keys = table.get_keys();
for (const auto& key : keys)
{
result.push_back(convert(s, key));
}
return result;
};
auto function_type = state.new_usertype<function>("function");
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
{
arguments arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
function.call(arguments);
};
struct player
@ -1018,7 +1110,7 @@ namespace ui_scripting::lua
for (auto arg : args)
{
arguments.push_back(convert({s, arg}));
arguments.push_back(script_convert({s, arg}));
}
const auto player_value = scripting::call("getentbynum", {0});

View File

@ -3,6 +3,7 @@
#include "error.hpp"
#include "event_handler.hpp"
#include "value_conversion.hpp"
namespace ui_scripting::lua
{
@ -167,14 +168,7 @@ namespace ui_scripting::lua
for (const auto& argument : event.arguments)
{
if (argument.index() > 0)
{
arguments.emplace_back(argument);
}
else
{
arguments.emplace_back(sol::lua_value{this->state_, sol::lua_nil});
}
arguments.emplace_back(convert(this->state_, argument));
}
return arguments;

View File

@ -0,0 +1,121 @@
#include <std_include.hpp>
#include "value_conversion.hpp"
#include "../execution.hpp"
namespace ui_scripting::lua
{
namespace
{
table convert_table(const sol::table& t)
{
table res{};
t.for_each([res](const sol::object& key, const sol::object& value)
{
res.set(convert(key), convert(value));
});
return res;
}
}
value convert(const sol::lua_value& value)
{
if (value.is<int>())
{
return {value.as<int>()};
}
if (value.is<unsigned int>())
{
return {value.as<unsigned int>()};
}
if (value.is<bool>())
{
return {value.as<bool>()};
}
if (value.is<double>())
{
return {value.as<double>()};
}
if (value.is<float>())
{
return {value.as<float>()};
}
if (value.is<std::string>())
{
return {value.as<std::string>()};
}
if (value.is<lightuserdata>())
{
return {value.as<lightuserdata>()};
}
if (value.is<userdata>())
{
return {value.as<userdata>()};
}
if (value.is<table>())
{
return {value.as<table>()};
}
if (value.is<function>())
{
return {value.as<function>()};
}
if (value.is<sol::table>())
{
return {convert_table(value.as<sol::table>())};
}
return {};
}
sol::lua_value convert(lua_State* state, const value& value)
{
if (value.is<int>())
{
return {state, value.as<int>()};
}
if (value.is<float>())
{
return {state, value.as<float>()};
}
if (value.is<std::string>())
{
return {state, value.as<std::string>()};
}
if (value.is<lightuserdata>())
{
return {state, value.as<lightuserdata>()};
}
if (value.is<userdata>())
{
return {state, value.as<userdata>()};
}
if (value.is<table>())
{
return {state, value.as<table>()};
}
if (value.is<function>())
{
return {state, value.as<function>()};
}
return {state, sol::lua_nil};
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "context.hpp"
namespace ui_scripting::lua
{
value convert(const sol::lua_value& value);
sol::lua_value convert(lua_State* state, const value& value);
}

View File

@ -7,24 +7,24 @@ namespace ui_scripting
{
const auto state = *game::hks::lua_state;
this->top_ = state->top;
this->base_ = state->base;
this->alloc_top_ = state->alloc_top;
this->bottom_ = state->bottom;
this->top_ = state->m_apistack.top;
this->base_ = state->m_apistack.base;
this->alloc_top_ = state->m_apistack.alloc_top;
this->bottom_ = state->m_apistack.bottom;
state->top = this->stack_;
state->base = state->top;
state->alloc_top = &this->stack_[ARRAYSIZE(this->stack_) - 1];
state->bottom = state->top;
state->m_apistack.top = this->stack_;
state->m_apistack.base = state->m_apistack.top;
state->m_apistack.alloc_top = &this->stack_[ARRAYSIZE(this->stack_) - 1];
state->m_apistack.bottom = state->m_apistack.top;
}
stack_isolation::~stack_isolation()
{
const auto state = *game::hks::lua_state;
state->top = this->top_;
state->base = this->base_;
state->alloc_top = this->alloc_top_;
state->bottom = this->bottom_;
state->m_apistack.top = this->top_;
state->m_apistack.base = this->base_;
state->m_apistack.alloc_top = this->alloc_top_;
state->m_apistack.bottom = this->bottom_;
}
}

View File

@ -0,0 +1,98 @@
#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_)
{
}
void userdata::set(const value& key, const value& value) const
{
set_field(*this, key, value);
}
value userdata::get(const value& key) const
{
return get_field(*this, key);
}
arguments userdata::call(const std::string& name, const arguments& arguments) const
{
return call_method(this->ptr, name, arguments);
}
/***************************************************************
* Table
**************************************************************/
table::table()
{
const auto state = *game::hks::lua_state;
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
}
table::table(game::hks::HashTable* ptr_)
: ptr(ptr_)
{
}
arguments table::get_keys() const
{
arguments keys{};
auto current = this->ptr->m_hashPart;
while (current->m_key.t)
{
keys.push_back(current->m_key);
current++;
}
return keys;
}
unsigned int table::size() const
{
return static_cast<unsigned int>(this->get_keys().size());
}
void table::set(const value& key, const value& value) const
{
set_field(*this, key, value);
}
value table::get(const value& key) const
{
return get_field(*this, key);
}
/***************************************************************
* Function
**************************************************************/
function::function(void* ptr_)
: ptr(ptr_)
{
}
arguments function::call(const arguments& arguments) const
{
call_script_function(*this, arguments);
return {};
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "game/game.hpp"
#include "value.hpp"
namespace ui_scripting
{
class lightuserdata
{
public:
lightuserdata(void*);
void* ptr;
};
class userdata
{
public:
userdata(void*);
value get(const value& key) const;
void set(const value& key, const value& value) const;
arguments call(const std::string& name, const arguments& arguments) const;
void* ptr;
};
class table
{
public:
table();
table(game::hks::HashTable* ptr_);
arguments get_keys() const;
unsigned int size() const;
value get(const value& key) const;
void set(const value& key, const value& value) const;
game::hks::HashTable* ptr;
};
class function
{
public:
function(void*);
arguments call(const arguments& arguments) const;
void* ptr;
};
}

View File

@ -0,0 +1,280 @@
#include <std_include.hpp>
#include "execution.hpp"
#include "types.hpp"
#include "stack_isolation.hpp"
#include "value.hpp"
namespace ui_scripting
{
/***************************************************************
* Constructors
**************************************************************/
value::value(const game::hks::HksObject& value)
: value_(value)
{
}
value::value(const int value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
value::value(const unsigned int value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
value::value(const bool value)
: value(static_cast<unsigned>(value))
{
}
value::value(const float value)
{
game::hks::HksObject obj{};
obj.t = game::hks::TNUMBER;
obj.v.number = static_cast<float>(value);
this->value_ = obj;
}
value::value(const double value)
: value(static_cast<float>(value))
{
}
value::value(const char* value)
{
game::hks::HksObject obj{};
stack_isolation _;
const auto state = *game::hks::lua_state;
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value));
obj = state->m_apistack.top[-1];
this->value_ = obj;
}
value::value(const std::string& value)
: value(value.data())
{
}
value::value(const lightuserdata& value)
{
this->value_.t = game::hks::TLIGHTUSERDATA;
this->value_.v.ptr = value.ptr;
}
value::value(const userdata& value)
{
this->value_.t = game::hks::TUSERDATA;
this->value_.v.ptr = value.ptr;
}
value::value(const table& value)
{
this->value_.t = game::hks::TTABLE;
this->value_.v.ptr = value.ptr;
}
value::value(const function& value)
{
this->value_.t = game::hks::TIFUNCTION;
this->value_.v.ptr = value.ptr;
}
/***************************************************************
* Integer
**************************************************************/
template <>
bool value::is<int>() const
{
return this->get_raw().t == game::hks::TNUMBER;
}
template <>
bool value::is<unsigned int>() const
{
return this->is<int>();
}
template <>
bool value::is<bool>() const
{
return this->is<int>();
}
template <>
int value::get() const
{
return static_cast<int>(this->get_raw().v.number);
}
template <>
unsigned int value::get() const
{
return static_cast<unsigned int>(this->get_raw().v.number);
}
template <>
bool value::get() const
{
return this->get_raw().v.native != 0;
}
/***************************************************************
* Float
**************************************************************/
template <>
bool value::is<float>() const
{
return this->get_raw().t == game::hks::TNUMBER;
}
template <>
bool value::is<double>() const
{
return this->is<float>();
}
template <>
float value::get() const
{
return this->get_raw().v.number;
}
template <>
double value::get() const
{
return static_cast<double>(this->get_raw().v.number);
}
/***************************************************************
* String
**************************************************************/
template <>
bool value::is<const char*>() const
{
return this->get_raw().t == game::hks::TSTRING;
}
template <>
bool value::is<std::string>() const
{
return this->is<const char*>();
}
template <>
const char* value::get() const
{
return this->get_raw().v.str->m_data;
}
template <>
std::string value::get() const
{
return this->get<const char*>();
}
bool value::operator==(const value& other)
{
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;
}
/***************************************************************
* Lightuserdata
**************************************************************/
template <>
bool value::is<lightuserdata>() const
{
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
}
template <>
lightuserdata value::get() const
{
return this->get_raw().v.ptr;
}
/***************************************************************
* Userdata
**************************************************************/
template <>
bool value::is<userdata>() const
{
return this->get_raw().t == game::hks::TUSERDATA;
}
template <>
userdata value::get() const
{
return this->get_raw().v.ptr;
}
/***************************************************************
* Table
**************************************************************/
template <>
bool value::is<table>() const
{
return this->get_raw().t == game::hks::TTABLE;
}
template <>
table value::get() const
{
return this->get_raw().v.table;
}
/***************************************************************
* Function
**************************************************************/
template <>
bool value::is<function>() const
{
return this->get_raw().t == game::hks::TIFUNCTION
|| this->get_raw().t == game::hks::TCFUNCTION;
}
template <>
function value::get() const
{
return this->get_raw().v.ptr;
}
/***************************************************************
*
**************************************************************/
const game::hks::HksObject& value::get_raw() const
{
return this->value_;
}
}

View File

@ -3,15 +3,56 @@
namespace ui_scripting
{
struct lightuserdata
class lightuserdata;
class userdata;
class table;
class function;
class value
{
void* ptr;
bool operator==(const lightuserdata other) const noexcept
public:
value() = default;
value(const game::hks::HksObject& value);
value(int value);
value(unsigned int value);
value(bool value);
value(float value);
value(double value);
value(const char* value);
value(const std::string& value);
value(const lightuserdata& value);
value(const userdata& value);
value(const table& value);
value(const function& value);
bool value::operator==(const value& other);
template <typename T>
bool is() const;
template <typename T>
T as() const
{
return this->ptr == other.ptr;
if (!this->is<T>())
{
throw std::runtime_error("Invalid type");
}
return get<T>();
}
const game::hks::HksObject& get_raw() const;
private:
template <typename T>
T get() const;
game::hks::HksObject value_{};
};
using value = std::variant<std::monostate, bool, int, float, std::string, lightuserdata>;
using arguments = std::vector<value>;
}