Support calling & detouring gsc script functions
This commit is contained in:
parent
e3102651b2
commit
3d1e262c16
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,3 +19,6 @@
|
|||||||
[submodule "deps/stb"]
|
[submodule "deps/stb"]
|
||||||
path = deps/stb
|
path = deps/stb
|
||||||
url = https://github.com/nothings/stb.git
|
url = https://github.com/nothings/stb.git
|
||||||
|
[submodule "deps/asmjit"]
|
||||||
|
path = deps/asmjit
|
||||||
|
url = https://github.com/asmjit/asmjit.git
|
||||||
|
1
deps/asmjit
vendored
Submodule
1
deps/asmjit
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 0dd16b0a98ae1da48563c9cc62f757a9e6bbe9b6
|
34
deps/premake/asmjit.lua
vendored
Normal file
34
deps/premake/asmjit.lua
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
asmjit = {
|
||||||
|
source = path.join(dependencies.basePath, "asmjit"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function asmjit.import()
|
||||||
|
links { "asmjit" }
|
||||||
|
asmjit.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function asmjit.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(asmjit.source, "src")
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"ASMJIT_STATIC"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function asmjit.project()
|
||||||
|
project "asmjit"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
asmjit.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(asmjit.source, "src/**.cpp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, asmjit)
|
119
src/component/notifies.cpp
Normal file
119
src/component/notifies.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/entity.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "game/scripting/lua/value_conversion.hpp"
|
||||||
|
#include "game/scripting/lua/error.hpp"
|
||||||
|
#include "notifies.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace notifies
|
||||||
|
{
|
||||||
|
std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
||||||
|
bool hook_enabled = true;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
|
||||||
|
|
||||||
|
unsigned int local_id_to_entity(unsigned int local_id)
|
||||||
|
{
|
||||||
|
const auto variable = game::scr_VarGlob->objectVariableValue[local_id];
|
||||||
|
return variable.u.f.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool execute_vm_hook(const char* pos)
|
||||||
|
{
|
||||||
|
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hook_enabled && pos > (char*)vm_execute_hooks.size())
|
||||||
|
{
|
||||||
|
hook_enabled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hook = vm_execute_hooks[pos];
|
||||||
|
const auto state = hook.lua_state();
|
||||||
|
|
||||||
|
const auto self_id = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
|
||||||
|
const auto self = scripting::entity(self_id);
|
||||||
|
|
||||||
|
std::vector<sol::lua_value> args;
|
||||||
|
|
||||||
|
const auto top = game::scr_function_stack->top;
|
||||||
|
|
||||||
|
for (auto* value = top; value->type != game::SCRIPT_END; --value)
|
||||||
|
{
|
||||||
|
args.push_back(scripting::lua::convert(state, *value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = hook(self, sol::as_args(args));
|
||||||
|
scripting::lua::handle_error(result);
|
||||||
|
|
||||||
|
const auto value = scripting::lua::convert({ state, result });
|
||||||
|
const auto type = value.get_raw().type;
|
||||||
|
|
||||||
|
game::Scr_ClearOutParams();
|
||||||
|
|
||||||
|
if (result.valid() && type && type < game::SCRIPT_END)
|
||||||
|
{
|
||||||
|
scripting::push_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_execute_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto replace = a.newLabel();
|
||||||
|
const auto end = a.newLabel();
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
|
||||||
|
a.mov(rcx, r14);
|
||||||
|
a.call_aligned(execute_vm_hook);
|
||||||
|
|
||||||
|
a.cmp(al, 0);
|
||||||
|
a.jne(replace);
|
||||||
|
|
||||||
|
a.popad64();
|
||||||
|
a.jmp(end);
|
||||||
|
|
||||||
|
a.bind(end);
|
||||||
|
|
||||||
|
a.movzx(r15d, byte_ptr(r14));
|
||||||
|
a.inc(r14);
|
||||||
|
a.mov(dword_ptr(rbp, 0xA4), r15d);
|
||||||
|
|
||||||
|
a.jmp(game::base_address + 0x5C90B3);
|
||||||
|
|
||||||
|
a.bind(replace);
|
||||||
|
|
||||||
|
a.popad64();
|
||||||
|
a.mov(r14, (char*)empty_function);
|
||||||
|
a.jmp(end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_callbacks()
|
||||||
|
{
|
||||||
|
vm_execute_hooks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
utils::hook::jump(game::base_address + 0x5C90A5, utils::hook::assemble(vm_execute_stub), true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(notifies::component)
|
9
src/component/notifies.hpp
Normal file
9
src/component/notifies.hpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace notifies
|
||||||
|
{
|
||||||
|
extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
||||||
|
extern bool hook_enabled;
|
||||||
|
|
||||||
|
void clear_callbacks();
|
||||||
|
}
|
@ -4,9 +4,10 @@
|
|||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
#include "scripting.hpp"
|
||||||
|
|
||||||
#include "game/scripting/event.hpp"
|
#include "game/scripting/event.hpp"
|
||||||
#include "game/scripting/execution.hpp"
|
#include "game/scripting/functions.hpp"
|
||||||
#include "game/scripting/lua/engine.hpp"
|
#include "game/scripting/lua/engine.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
@ -14,6 +15,7 @@
|
|||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||||
|
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -24,6 +26,11 @@ namespace scripting
|
|||||||
|
|
||||||
utils::hook::detour scr_add_class_field_hook;
|
utils::hook::detour scr_add_class_field_hook;
|
||||||
|
|
||||||
|
utils::hook::detour scr_set_thread_position_hook;
|
||||||
|
utils::hook::detour process_script_hook;
|
||||||
|
|
||||||
|
std::string current_file;
|
||||||
|
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value,
|
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value,
|
||||||
@ -78,6 +85,28 @@ namespace scripting
|
|||||||
|
|
||||||
scr_add_class_field_hook.invoke<void>(classnum, _name, canonicalString, offset);
|
scr_add_class_field_hook.invoke<void>(classnum, _name, canonicalString, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void process_script_stub(const char* filename)
|
||||||
|
{
|
||||||
|
current_file = filename;
|
||||||
|
|
||||||
|
const auto file_id = atoi(filename);
|
||||||
|
if (file_id)
|
||||||
|
{
|
||||||
|
current_file = scripting::find_token(file_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
process_script_hook.invoke<void>(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scr_set_thread_position_stub(unsigned int threadName, const char* codePos)
|
||||||
|
{
|
||||||
|
const auto function_name = scripting::find_token(threadName);
|
||||||
|
|
||||||
|
script_function_table[current_file][function_name] = codePos;
|
||||||
|
|
||||||
|
scr_set_thread_position_hook.invoke<void>(threadName, codePos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -91,6 +120,8 @@ namespace scripting
|
|||||||
g_shutdown_game_hook.create(game::base_address + 0x4CBAD0, g_shutdown_game_stub);
|
g_shutdown_game_hook.create(game::base_address + 0x4CBAD0, g_shutdown_game_stub);
|
||||||
|
|
||||||
scr_add_class_field_hook.create(game::base_address + 0x5C2C30, scr_add_class_field_stub);
|
scr_add_class_field_hook.create(game::base_address + 0x5C2C30, scr_add_class_field_stub);
|
||||||
|
scr_set_thread_position_hook.create(game::base_address + 0x5BC7E0, scr_set_thread_position_stub);
|
||||||
|
process_script_hook.create(game::base_address + 0x5C6160, process_script_stub);
|
||||||
|
|
||||||
scheduler::loop([]()
|
scheduler::loop([]()
|
||||||
{
|
{
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||||
|
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||||
}
|
}
|
@ -16,14 +16,6 @@ namespace scripting
|
|||||||
return value_ptr;
|
return value_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_value(const script_value& value)
|
|
||||||
{
|
|
||||||
auto* value_ptr = allocate_argument();
|
|
||||||
*value_ptr = value.get_raw();
|
|
||||||
|
|
||||||
game::AddRefToValue(value_ptr->type, value_ptr->u);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_field_id(const int classnum, const std::string& field)
|
int get_field_id(const int classnum, const std::string& field)
|
||||||
{
|
{
|
||||||
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
|
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
|
||||||
@ -49,6 +41,14 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void push_value(const script_value& value)
|
||||||
|
{
|
||||||
|
auto* value_ptr = allocate_argument();
|
||||||
|
*value_ptr = value.get_raw();
|
||||||
|
|
||||||
|
game::AddRefToValue(value_ptr->type, value_ptr->u);
|
||||||
|
}
|
||||||
|
|
||||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
|
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
|
||||||
{
|
{
|
||||||
stack_isolation _;
|
stack_isolation _;
|
||||||
@ -122,6 +122,29 @@ namespace scripting
|
|||||||
return get_return_value();
|
return get_return_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* get_function_pos(const std::string& filename, const std::string& function)
|
||||||
|
{
|
||||||
|
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("File '" + filename + "' not found");
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto functions = scripting::script_function_table[filename];
|
||||||
|
if (functions.find(function) == functions.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return functions.at(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value call_script_function(const entity& entity, const std::string& filename,
|
||||||
|
const std::string& function, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
const auto pos = get_function_pos(filename, function);
|
||||||
|
return exec_ent_thread(entity, pos, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
static std::unordered_map<unsigned int, std::unordered_map<std::string, script_value>> custom_fields;
|
static std::unordered_map<unsigned int, std::unordered_map<std::string, script_value>> custom_fields;
|
||||||
|
|
||||||
script_value get_custom_field(const entity& entity, const std::string& field)
|
script_value get_custom_field(const entity& entity, const std::string& field)
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
|
void push_value(const script_value& value);
|
||||||
|
|
||||||
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
|
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
|
||||||
script_value call_function(const std::string& name, const entity& entity,
|
script_value call_function(const std::string& name, const entity& entity,
|
||||||
const std::vector<script_value>& arguments);
|
const std::vector<script_value>& arguments);
|
||||||
@ -22,6 +24,9 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
|
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
|
||||||
|
const char* get_function_pos(const std::string& filename, const std::string& function);
|
||||||
|
script_value call_script_function(const entity& entity, const std::string& filename,
|
||||||
|
const std::string& function, const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
void clear_entity_fields(const entity& entity);
|
void clear_entity_fields(const entity& entity);
|
||||||
void clear_custom_fields();
|
void clear_custom_fields();
|
||||||
|
@ -12,10 +12,10 @@ namespace scripting
|
|||||||
{"nullsub_428", 0x3},
|
{"nullsub_428", 0x3},
|
||||||
{"sub_502950", 0x4},
|
{"sub_502950", 0x4},
|
||||||
{"sub_504f40", 0x5},
|
{"sub_504f40", 0x5},
|
||||||
{"meleeapplyinitialvelocity", 0x6},
|
{"setphysicsgravitydir", 0x6},
|
||||||
{"sub_508680", 0x7},
|
{"gettimescale", 0x7},
|
||||||
{"sub_5086a0", 0x8},
|
{"settimescale", 0x8},
|
||||||
{"sub_5087a0", 0x9},
|
{"setslowmotionview", 0x9},
|
||||||
{"sub_5086c0", 0xA},
|
{"sub_5086c0", 0xA},
|
||||||
{"sub_5085a0", 0xB},
|
{"sub_5085a0", 0xB},
|
||||||
{"sub_504fd0", 0xC},
|
{"sub_504fd0", 0xC},
|
||||||
@ -125,11 +125,11 @@ namespace scripting
|
|||||||
{"setblur", 0x76},
|
{"setblur", 0x76},
|
||||||
{"musicplay", 0x77},
|
{"musicplay", 0x77},
|
||||||
{"musicstop", 0x78},
|
{"musicstop", 0x78},
|
||||||
{"sub_507890", 0x79},
|
{"soundfade", 0x79},
|
||||||
{"setteammode", 0x7A},
|
{"soundsettimescalefactor", 0x7A},
|
||||||
{"sub_4f6990", 0x7B},
|
{"soundresettimescale", 0x7B},
|
||||||
{"sub_4f6a20", 0x7C},
|
{"setocclusionpreset", 0x7C},
|
||||||
{"sub_4f6c90", 0x7D},
|
{"levelsoundfade", 0x7D},
|
||||||
{"sub_4f6da0", 0x7E},
|
{"sub_4f6da0", 0x7E},
|
||||||
{"sub_4f6dd0", 0x7F},
|
{"sub_4f6dd0", 0x7F},
|
||||||
{"sub_507a70", 0x80},
|
{"sub_507a70", 0x80},
|
||||||
@ -223,8 +223,8 @@ namespace scripting
|
|||||||
{"sub_504c60", 0xDB},
|
{"sub_504c60", 0xDB},
|
||||||
{"sub_505030", 0xDC},
|
{"sub_505030", 0xDC},
|
||||||
{"sub_5050a0", 0xDD},
|
{"sub_5050a0", 0xDD},
|
||||||
{"sub_505360", 0xDE},
|
{"getaiarray", 0xDE},
|
||||||
{"sub_505520", 0xDF},
|
{"getaispeciesarray", 0xDF},
|
||||||
{"getspawnerarray", 0xE0},
|
{"getspawnerarray", 0xE0},
|
||||||
{"getcorpsearray", 0xE1},
|
{"getcorpsearray", 0xE1},
|
||||||
{"getspawnerteamarray", 0xE2},
|
{"getspawnerteamarray", 0xE2},
|
||||||
@ -874,12 +874,12 @@ namespace scripting
|
|||||||
{"allowprone", 0x8123},
|
{"allowprone", 0x8123},
|
||||||
{"allowlean", 0x8124},
|
{"allowlean", 0x8124},
|
||||||
{"allowswim", 0x8125},
|
{"allowswim", 0x8125},
|
||||||
{"sub_4bb590", 0x8126},
|
{"setocclusion", 0x8126},
|
||||||
{"sub_4bb320", 0x8128},
|
{"deactivateocclusion", 0x8128},
|
||||||
{"sub_4bb3a0", 0x8129},
|
{"deactivateallocclusion", 0x8129},
|
||||||
{"sub_4bb730", 0x812A},
|
{"isocclusionenabled", 0x812A},
|
||||||
{"sub_4bb780", 0x812F},
|
{"setreverbfromtable", 0x812F},
|
||||||
{"sub_4bb3c0", 0x8130},
|
{"setvolmodfromtable", 0x8130},
|
||||||
{"sub_4bb7d0", 0x8131},
|
{"sub_4bb7d0", 0x8131},
|
||||||
{"sub_4bb930", 0x8132},
|
{"sub_4bb930", 0x8132},
|
||||||
{"sub_4bb240", 0x8133},
|
{"sub_4bb240", 0x8133},
|
||||||
@ -1772,7 +1772,13 @@ namespace scripting
|
|||||||
|
|
||||||
std::unordered_map<std::string, unsigned> token_map =
|
std::unordered_map<std::string, unsigned> token_map =
|
||||||
{
|
{
|
||||||
|
{"main", 616},
|
||||||
{"player", 794},
|
{"player", 794},
|
||||||
{"default_start", 10126},
|
{"default_start", 10126},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unordered_map<unsigned, std::string> file_list =
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,19 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string find_token(unsigned int id)
|
||||||
|
{
|
||||||
|
for (const auto& token : token_map)
|
||||||
|
{
|
||||||
|
if (token.second == id)
|
||||||
|
{
|
||||||
|
return token.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::string::va("_ID%i", id);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int find_token_id(const std::string& name)
|
unsigned int find_token_id(const std::string& name)
|
||||||
{
|
{
|
||||||
const auto result = token_map.find(name);
|
const auto result = token_map.find(name);
|
||||||
|
@ -6,9 +6,11 @@ namespace scripting
|
|||||||
extern std::unordered_map<std::string, unsigned> method_map;
|
extern std::unordered_map<std::string, unsigned> method_map;
|
||||||
extern std::unordered_map<std::string, unsigned> function_map;
|
extern std::unordered_map<std::string, unsigned> function_map;
|
||||||
extern std::unordered_map<std::string, unsigned> token_map;
|
extern std::unordered_map<std::string, unsigned> token_map;
|
||||||
|
extern std::unordered_map<unsigned, std::string> file_list;
|
||||||
|
|
||||||
using script_function = void(*)(game::scr_entref_t);
|
using script_function = void(*)(game::scr_entref_t);
|
||||||
|
|
||||||
|
std::string find_token(unsigned int id);
|
||||||
unsigned int find_token_id(const std::string& name);
|
unsigned int find_token_id(const std::string& name);
|
||||||
script_function find_function(const std::string& name, const bool prefer_global);
|
script_function find_function(const std::string& name, const bool prefer_global);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "../execution.hpp"
|
#include "../execution.hpp"
|
||||||
#include "../functions.hpp"
|
#include "../functions.hpp"
|
||||||
|
|
||||||
|
#include "../../../component/notifies.hpp"
|
||||||
|
#include "../../../component/scripting.hpp"
|
||||||
#include "../../../component/command.hpp"
|
#include "../../../component/command.hpp"
|
||||||
#include "../../../component/chat.hpp"
|
#include "../../../component/chat.hpp"
|
||||||
|
|
||||||
@ -373,6 +375,74 @@ namespace scripting::lua
|
|||||||
{
|
{
|
||||||
chat::print(msg);
|
chat::print(msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
game_type["detour"] = [](const game&, const sol::this_state s, const std::string& filename,
|
||||||
|
const std::string function_name, const sol::protected_function& function)
|
||||||
|
{
|
||||||
|
const auto pos = get_function_pos(filename, function_name);
|
||||||
|
notifies::vm_execute_hooks[pos] = function;
|
||||||
|
|
||||||
|
auto detour = sol::table::create(function.lua_state());
|
||||||
|
|
||||||
|
detour["disable"] = [pos]()
|
||||||
|
{
|
||||||
|
notifies::vm_execute_hooks.erase(pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
detour["enable"] = [pos, function]()
|
||||||
|
{
|
||||||
|
notifies::vm_execute_hooks[pos] = function;
|
||||||
|
};
|
||||||
|
|
||||||
|
detour["invoke"] = [filename, function_name](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
||||||
|
{
|
||||||
|
std::vector<script_value> arguments{};
|
||||||
|
|
||||||
|
for (auto arg : va)
|
||||||
|
{
|
||||||
|
arguments.push_back(convert({s, arg}));
|
||||||
|
}
|
||||||
|
|
||||||
|
notifies::hook_enabled = false;
|
||||||
|
const auto result = convert(s, call_script_function(entity, filename, function_name, arguments));
|
||||||
|
notifies::hook_enabled = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return detour;
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["getfunctions"] = [entity_type](const game&, const sol::this_state s, const std::string& filename)
|
||||||
|
{
|
||||||
|
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("File '" + filename + "' not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto functions = sol::table::create(s.lua_state());
|
||||||
|
|
||||||
|
for (const auto& function : scripting::script_function_table[filename])
|
||||||
|
{
|
||||||
|
functions[function.first] = [filename, function](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
||||||
|
{
|
||||||
|
std::vector<script_value> arguments{};
|
||||||
|
|
||||||
|
for (auto arg : va)
|
||||||
|
{
|
||||||
|
arguments.push_back(convert({s, arg}));
|
||||||
|
}
|
||||||
|
|
||||||
|
notifies::hook_enabled = false;
|
||||||
|
const auto result = convert(s, call_script_function(entity, filename, function.first, arguments));
|
||||||
|
notifies::hook_enabled = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return functions;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
|
#include "../../../component/notifies.hpp"
|
||||||
#include "../execution.hpp"
|
#include "../execution.hpp"
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
@ -46,6 +47,7 @@ namespace scripting::lua::engine
|
|||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
|
notifies::clear_callbacks();
|
||||||
get_scripts().clear();
|
get_scripts().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "value_conversion.hpp"
|
#include "value_conversion.hpp"
|
||||||
#include "../functions.hpp"
|
#include "../functions.hpp"
|
||||||
#include "../execution.hpp"
|
#include "../execution.hpp"
|
||||||
|
#include ".../../component/notifies.hpp"
|
||||||
|
|
||||||
namespace scripting::lua
|
namespace scripting::lua
|
||||||
{
|
{
|
||||||
@ -140,6 +141,20 @@ namespace scripting::lua
|
|||||||
return script_value(variable);
|
return script_value(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game::VariableValue convert_function(sol::lua_value value)
|
||||||
|
{
|
||||||
|
const auto function = value.as<sol::protected_function>();
|
||||||
|
const auto index = (char*)notifies::vm_execute_hooks.size() + 1;
|
||||||
|
|
||||||
|
notifies::vm_execute_hooks[index] = function;
|
||||||
|
|
||||||
|
game::VariableValue func;
|
||||||
|
func.type = game::SCRIPT_FUNCTION;
|
||||||
|
func.u.codePosValue = index;
|
||||||
|
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
sol::lua_value convert_function(lua_State* state, const char* pos)
|
sol::lua_value convert_function(lua_State* state, const char* pos)
|
||||||
{
|
{
|
||||||
return [pos](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
return [pos](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
||||||
|
@ -127,4 +127,5 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xB617C00};
|
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xB617C00};
|
||||||
WEAK symbol<scrVmPub_t> scr_VmPub{0xBA9EE40};
|
WEAK symbol<scrVmPub_t> scr_VmPub{0xBA9EE40};
|
||||||
|
WEAK symbol<function_stack_t> scr_function_stack{0xBAA93C0};
|
||||||
}
|
}
|
@ -24,6 +24,7 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#ifdef max
|
#ifdef max
|
||||||
#undef max
|
#undef max
|
||||||
@ -36,4 +37,7 @@
|
|||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
#include <gsl/gsl>
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
#include <asmjit/core/jitruntime.h>
|
||||||
|
#include <asmjit/x86/x86assembler.h>
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
@ -25,6 +25,76 @@ namespace utils::hook
|
|||||||
} __;
|
} __;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assembler::pushad64()
|
||||||
|
{
|
||||||
|
this->push(rax);
|
||||||
|
this->push(rcx);
|
||||||
|
this->push(rdx);
|
||||||
|
this->push(rbx);
|
||||||
|
this->push(rsp);
|
||||||
|
this->push(rbp);
|
||||||
|
this->push(rsi);
|
||||||
|
this->push(rdi);
|
||||||
|
|
||||||
|
this->sub(rsp, 0x40);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::popad64()
|
||||||
|
{
|
||||||
|
this->add(rsp, 0x40);
|
||||||
|
|
||||||
|
this->pop(rdi);
|
||||||
|
this->pop(rsi);
|
||||||
|
this->pop(rbp);
|
||||||
|
this->pop(rsp);
|
||||||
|
this->pop(rbx);
|
||||||
|
this->pop(rdx);
|
||||||
|
this->pop(rcx);
|
||||||
|
this->pop(rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::prepare_stack_for_call()
|
||||||
|
{
|
||||||
|
const auto reserve_callee_space = this->newLabel();
|
||||||
|
const auto stack_unaligned = this->newLabel();
|
||||||
|
|
||||||
|
this->test(rsp, 0xF);
|
||||||
|
this->jnz(stack_unaligned);
|
||||||
|
|
||||||
|
this->sub(rsp, 0x8);
|
||||||
|
this->push(rsp);
|
||||||
|
|
||||||
|
this->push(rax);
|
||||||
|
this->mov(rax, ptr(rsp, 8, 8));
|
||||||
|
this->add(rax, 0x8);
|
||||||
|
this->mov(ptr(rsp, 8, 8), rax);
|
||||||
|
this->pop(rax);
|
||||||
|
|
||||||
|
this->jmp(reserve_callee_space);
|
||||||
|
|
||||||
|
this->bind(stack_unaligned);
|
||||||
|
this->push(rsp);
|
||||||
|
|
||||||
|
this->bind(reserve_callee_space);
|
||||||
|
this->sub(rsp, 0x40);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::restore_stack_after_call()
|
||||||
|
{
|
||||||
|
this->lea(rsp, ptr(rsp, 0x40));
|
||||||
|
this->pop(rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::Error assembler::call(void* target)
|
||||||
|
{
|
||||||
|
return Assembler::call(size_t(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::Error assembler::jmp(void* target)
|
||||||
|
{
|
||||||
|
return Assembler::jmp(size_t(target));
|
||||||
|
}
|
||||||
|
|
||||||
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target)
|
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -115,7 +185,7 @@ namespace utils::hook
|
|||||||
copy(reinterpret_cast<void*>(place), data, length);
|
copy(reinterpret_cast<void*>(place), data, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_relatively_far(const void* pointer, const void* data, int offset)
|
bool is_relatively_far(const void* pointer, const void* data, const int offset)
|
||||||
{
|
{
|
||||||
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
||||||
const auto small_diff = int32_t(diff);
|
const auto small_diff = int32_t(diff);
|
||||||
@ -178,4 +248,47 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
return jump(pointer, reinterpret_cast<void*>(data), use_far);
|
return jump(pointer, reinterpret_cast<void*>(data), use_far);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* assemble(const std::function<void(assembler&)>& asm_function)
|
||||||
|
{
|
||||||
|
static asmjit::JitRuntime runtime;
|
||||||
|
|
||||||
|
asmjit::CodeHolder code;
|
||||||
|
code.init(runtime.environment());
|
||||||
|
|
||||||
|
assembler a(&code);
|
||||||
|
|
||||||
|
asm_function(a);
|
||||||
|
|
||||||
|
void* result = nullptr;
|
||||||
|
runtime.add(&result, &code);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inject(void* pointer, const void* data)
|
||||||
|
{
|
||||||
|
if (is_relatively_far(pointer, data, 4))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
set<int32_t>(pointer, int32_t(size_t(data) - (size_t(pointer) + 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void inject(const size_t pointer, const void* data)
|
||||||
|
{
|
||||||
|
return inject(reinterpret_cast<void*>(pointer), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* follow_branch(void* address)
|
||||||
|
{
|
||||||
|
auto* const data = static_cast<uint8_t*>(address);
|
||||||
|
if (*data != 0xE8 && *data != 0xE9)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No branch instruction found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return extract<void*>(data + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <asmjit/core/jitruntime.h>
|
||||||
|
#include <asmjit/x86/x86assembler.h>
|
||||||
|
|
||||||
|
using namespace asmjit::x86;
|
||||||
|
|
||||||
namespace utils::hook
|
namespace utils::hook
|
||||||
{
|
{
|
||||||
|
class assembler : public Assembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Assembler::Assembler;
|
||||||
|
using Assembler::call;
|
||||||
|
using Assembler::jmp;
|
||||||
|
|
||||||
|
void pushad64();
|
||||||
|
void popad64();
|
||||||
|
|
||||||
|
void prepare_stack_for_call();
|
||||||
|
void restore_stack_after_call();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void call_aligned(T&& target)
|
||||||
|
{
|
||||||
|
this->prepare_stack_for_call();
|
||||||
|
this->call(std::forward<T>(target));
|
||||||
|
this->restore_stack_after_call();
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::Error call(void* target);
|
||||||
|
asmjit::Error jmp(void* target);
|
||||||
|
};
|
||||||
|
|
||||||
class detour
|
class detour
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -15,7 +44,7 @@ namespace utils::hook
|
|||||||
this->operator=(std::move(other));
|
this->operator=(std::move(other));
|
||||||
}
|
}
|
||||||
|
|
||||||
detour& operator= (detour&& other) noexcept
|
detour& operator=(detour&& other) noexcept
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
{
|
{
|
||||||
@ -32,7 +61,7 @@ namespace utils::hook
|
|||||||
}
|
}
|
||||||
|
|
||||||
detour(const detour&) = delete;
|
detour(const detour&) = delete;
|
||||||
detour& operator= (const detour&) = delete;
|
detour& operator=(const detour&) = delete;
|
||||||
|
|
||||||
void enable() const;
|
void enable() const;
|
||||||
void disable() const;
|
void disable() const;
|
||||||
@ -47,8 +76,8 @@ namespace utils::hook
|
|||||||
return static_cast<T*>(this->get_original());
|
return static_cast<T*>(this->get_original());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T = void, typename... Args>
|
||||||
T invoke(Args... args)
|
T invoke(Args ... args)
|
||||||
{
|
{
|
||||||
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||||
}
|
}
|
||||||
@ -76,15 +105,17 @@ namespace utils::hook
|
|||||||
void jump(size_t pointer, void* data, bool use_far = false);
|
void jump(size_t pointer, void* data, bool use_far = false);
|
||||||
void jump(size_t pointer, size_t data, bool use_far = false);
|
void jump(size_t pointer, size_t data, bool use_far = false);
|
||||||
|
|
||||||
|
void* assemble(const std::function<void(assembler&)>& asm_function);
|
||||||
|
|
||||||
void inject(void* pointer, const void* data);
|
void inject(void* pointer, const void* data);
|
||||||
void inject(size_t pointer, const void* data);
|
void inject(size_t pointer, const void* data);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T extract(void* address)
|
T extract(void* address)
|
||||||
{
|
{
|
||||||
const auto data = static_cast<uint8_t*>(address);
|
auto* const data = static_cast<uint8_t*>(address);
|
||||||
const auto offset = *reinterpret_cast<int32_t*>(data);
|
const auto offset = *reinterpret_cast<int32_t*>(data);
|
||||||
return reinterpret_cast<T>(data + offset + 4);
|
return reinterpret_cast<T>(data + offset + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* follow_branch(void* address);
|
void* follow_branch(void* address);
|
||||||
@ -108,13 +139,13 @@ namespace utils::hook
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
static T invoke(size_t func, Args... args)
|
static T invoke(size_t func, Args ... args)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
|
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
static T invoke(void* func, Args... args)
|
static T invoke(void* func, Args ... args)
|
||||||
{
|
{
|
||||||
return static_cast<T(*)(Args ...)>(func)(args...);
|
return static_cast<T(*)(Args ...)>(func)(args...);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user