mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-04-19 21:22:54 +00:00
Add scheduler, add support for GSC method adding, command adding.
Some cleanup. Add exception handler.
This commit is contained in:
parent
34b90247c9
commit
74f6a66374
@ -1,2 +1,4 @@
|
|||||||
# T4SP-Server-Plugin
|
# T4SP-Server-Plugin
|
||||||
A plugin that has code that hopefully compiles and the game will load it to do things. Stability not guaranteed.
|
A plugin that has code that hopefully compiles and the game will load it to do things. Stability not guaranteed.
|
||||||
|
|
||||||
|
Credit to https://github.com/fedddddd for t6-gsc-utils which this is based on.
|
@ -30,7 +30,7 @@ end
|
|||||||
|
|
||||||
dependencies.load()
|
dependencies.load()
|
||||||
|
|
||||||
workspace "t6-gsc-utils"
|
workspace "t4sp-server-plugin"
|
||||||
location "./build"
|
location "./build"
|
||||||
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
||||||
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||||
@ -66,9 +66,9 @@ workspace "t6-gsc-utils"
|
|||||||
defines { "DEBUG", "_DEBUG" }
|
defines { "DEBUG", "_DEBUG" }
|
||||||
filter {}
|
filter {}
|
||||||
|
|
||||||
startproject "t6-gsc-utils"
|
startproject "t4sp-server-plugin"
|
||||||
|
|
||||||
project "t6-gsc-utils"
|
project "t4sp-server-plugin"
|
||||||
kind "SharedLib"
|
kind "SharedLib"
|
||||||
language "C++"
|
language "C++"
|
||||||
|
|
||||||
|
137
src/component/exception.cpp
Normal file
137
src/component/exception.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/thread.hpp>
|
||||||
|
#include <utils/compression.hpp>
|
||||||
|
|
||||||
|
#include <exception/minidump.hpp>
|
||||||
|
|
||||||
|
namespace exception
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
thread_local struct
|
||||||
|
{
|
||||||
|
DWORD code = 0;
|
||||||
|
PVOID address = nullptr;
|
||||||
|
} exception_data;
|
||||||
|
|
||||||
|
void show_mouse_cursor()
|
||||||
|
{
|
||||||
|
while (ShowCursor(TRUE) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_error_dialog()
|
||||||
|
{
|
||||||
|
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n"
|
||||||
|
"A minidump has been written.\n\n",
|
||||||
|
exception_data.code, exception_data.address);
|
||||||
|
|
||||||
|
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
|
||||||
|
|
||||||
|
utils::thread::suspend_other_threads();
|
||||||
|
show_mouse_cursor();
|
||||||
|
MessageBoxA(nullptr, error_str.data(), "Plutonium T4 ERROR", MB_ICONERROR);
|
||||||
|
TerminateProcess(GetCurrentProcess(), exception_data.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_state()
|
||||||
|
{
|
||||||
|
display_error_dialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_reset_state_stub()
|
||||||
|
{
|
||||||
|
static auto* stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
a.sub(esp, 0x10);
|
||||||
|
a.or_(esp, 0x8);
|
||||||
|
a.jmp(reset_state);
|
||||||
|
});
|
||||||
|
|
||||||
|
return reinterpret_cast<size_t>(stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
std::string info{};
|
||||||
|
const auto line = [&info](const std::string& text)
|
||||||
|
{
|
||||||
|
info.append(text);
|
||||||
|
info.append("\r\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
line("Plutonium T4 Crash Dump");
|
||||||
|
line("");
|
||||||
|
line("Timestamp: "s + utils::string::get_timestamp());
|
||||||
|
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
|
||||||
|
line(utils::string::va("Address: 0x%lX", exceptioninfo->ExceptionRecord->ExceptionAddress));
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4996)
|
||||||
|
OSVERSIONINFOEXA version_info;
|
||||||
|
ZeroMemory(&version_info, sizeof(version_info));
|
||||||
|
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
||||||
|
GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&version_info));
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion));
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
const std::string crash_name = utils::string::va("minidumps/plutonium-t4-crash-%s.zip",
|
||||||
|
utils::string::get_timestamp().data());
|
||||||
|
|
||||||
|
utils::compression::zip::archive zip_file{};
|
||||||
|
zip_file.add("crash.dmp", create_minidump(exceptioninfo));
|
||||||
|
zip_file.add("info.txt", generate_crash_info(exceptioninfo));
|
||||||
|
zip_file.write(crash_name, "Plutonium T4 Crash Dump");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_harmless_error(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
const auto code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||||
|
return code == STATUS_INTEGER_OVERFLOW || code == STATUS_FLOAT_OVERFLOW || code == STATUS_SINGLE_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
if (is_harmless_error(exceptioninfo))
|
||||||
|
{
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_minidump(exceptioninfo);
|
||||||
|
|
||||||
|
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||||
|
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
|
||||||
|
exceptioninfo->ContextRecord->Eip = get_reset_state_stub();
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI set_unhandled_exception_filter_stub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
||||||
|
{
|
||||||
|
// Don't register anything here...
|
||||||
|
return &exception_filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
SetUnhandledExceptionFilter(exception_filter);
|
||||||
|
utils::hook::jump(reinterpret_cast<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(exception::component)
|
148
src/component/gsc.cpp
Normal file
148
src/component/gsc.cpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include <json.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace gsc
|
||||||
|
{
|
||||||
|
std::unordered_map<unsigned int, std::pair<std::string, void*>> functions;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void* original_scr_get_gsc_funcs_jump_loc;
|
||||||
|
|
||||||
|
void* original_scr_get_method_funcs_call_loc;
|
||||||
|
|
||||||
|
void ebic_func()
|
||||||
|
{
|
||||||
|
game::Com_PrintF(game::CON_CHANNEL_SERVER, "Oof \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
game::BuiltinFunction plutonium_scr_get_gsc_funcs_stub(const char** pName, int* type)
|
||||||
|
{
|
||||||
|
//printf( "%s %d\n", * pName, * type);
|
||||||
|
/*
|
||||||
|
if (*pName == "isdefined"s)
|
||||||
|
{
|
||||||
|
return &ebic_func;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::BuiltinMethod plutonium_scr_get_gsc_methods_stub(const char** pName, int* type)
|
||||||
|
{
|
||||||
|
printf("%s %d\n", *pName, *type);
|
||||||
|
/*
|
||||||
|
if (*pName == "isdefined"s)
|
||||||
|
{
|
||||||
|
return &ebic_func;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) original_scr_get_gsc_funcs_hook()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax;
|
||||||
|
pushad;
|
||||||
|
|
||||||
|
lea eax, [esp + 0x24 + 0x2C - 0x1C];
|
||||||
|
push eax;
|
||||||
|
push edx;
|
||||||
|
call plutonium_scr_get_gsc_funcs_stub;
|
||||||
|
add esp, 8;
|
||||||
|
|
||||||
|
mov[esp + 0x20], eax;
|
||||||
|
|
||||||
|
popad;
|
||||||
|
pop eax;
|
||||||
|
|
||||||
|
test eax, eax;
|
||||||
|
|
||||||
|
jnz og;
|
||||||
|
|
||||||
|
// pluto
|
||||||
|
push original_scr_get_gsc_funcs_jump_loc;
|
||||||
|
retn;
|
||||||
|
|
||||||
|
og:
|
||||||
|
// retn
|
||||||
|
add esp, 4;
|
||||||
|
push 0x682DC8;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) original_scr_get_method_funcs_hook()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax;
|
||||||
|
pushad;
|
||||||
|
|
||||||
|
push edi;
|
||||||
|
push esi;
|
||||||
|
call plutonium_scr_get_gsc_methods_stub;
|
||||||
|
add esp, 8;
|
||||||
|
mov[esp + 0x20], eax; // move answer into eax when pop happens
|
||||||
|
|
||||||
|
popad;
|
||||||
|
pop eax;
|
||||||
|
|
||||||
|
test eax, eax;
|
||||||
|
jz pluto_code;
|
||||||
|
|
||||||
|
retn;
|
||||||
|
|
||||||
|
pluto_code:
|
||||||
|
|
||||||
|
push original_scr_get_method_funcs_call_loc;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int find_function(const std::string& name)
|
||||||
|
{
|
||||||
|
for (const auto& function : functions)
|
||||||
|
{
|
||||||
|
if (function.second.first == name)
|
||||||
|
{
|
||||||
|
return function.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace function
|
||||||
|
{
|
||||||
|
void add(const std::string& name, const void*& function)
|
||||||
|
{
|
||||||
|
const auto id = functions.size() + 1;
|
||||||
|
//functions[id] = std::make_pair(name, function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
original_scr_get_gsc_funcs_jump_loc = utils::hook::get_displacement_addr(0x682D99);
|
||||||
|
original_scr_get_method_funcs_call_loc = utils::hook::get_displacement_addr(0x683043);
|
||||||
|
utils::hook::jump(0x682D99, original_scr_get_gsc_funcs_hook);
|
||||||
|
utils::hook::call(0x683043, original_scr_get_method_funcs_hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(gsc::component)
|
||||||
|
|
188
src/component/scheduler.cpp
Normal file
188
src/component/scheduler.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct task
|
||||||
|
{
|
||||||
|
std::function<bool()> handler{};
|
||||||
|
std::chrono::milliseconds interval{};
|
||||||
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using task_list = std::vector<task>;
|
||||||
|
|
||||||
|
class task_pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(task&& task)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
this->merge_callbacks();
|
||||||
|
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - i->last_call;
|
||||||
|
|
||||||
|
if (diff < i->interval)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->last_call = now;
|
||||||
|
|
||||||
|
const auto res = i->handler();
|
||||||
|
if (res == cond_end)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::concurrency::container<task_list> new_callbacks_;
|
||||||
|
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||||
|
|
||||||
|
void merge_callbacks()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||||
|
new_tasks = {};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::thread thread;
|
||||||
|
task_pipeline pipelines[pipeline::count];
|
||||||
|
|
||||||
|
void execute(const pipeline type)
|
||||||
|
{
|
||||||
|
assert(type >= 0 && type < pipeline::count);
|
||||||
|
pipelines[type].execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_server()
|
||||||
|
{
|
||||||
|
execute(pipeline::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour com_init_hook;
|
||||||
|
|
||||||
|
std::vector<std::function<void()>> post_init_funcs;
|
||||||
|
bool inited = false;
|
||||||
|
|
||||||
|
void on_post_init_hook()
|
||||||
|
{
|
||||||
|
if (inited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inited = true;
|
||||||
|
for (const auto& func : post_init_funcs)
|
||||||
|
{
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void com_init_stub()
|
||||||
|
{
|
||||||
|
com_init_hook.invoke<void>();
|
||||||
|
on_post_init_hook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
assert(type >= 0 && type < pipeline::count);
|
||||||
|
|
||||||
|
task task;
|
||||||
|
task.handler = callback;
|
||||||
|
task.interval = delay;
|
||||||
|
task.last_call = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
pipelines[type].add(std::move(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return cond_continue;
|
||||||
|
}, type, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void once(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return cond_end;
|
||||||
|
}, type, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_init(const std::function<void()>& callback)
|
||||||
|
{
|
||||||
|
if (inited)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
post_init_funcs.push_back(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
thread = std::thread([]()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
execute(pipeline::async);
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
com_init_hook.create(0x59D710, com_init_stub);
|
||||||
|
|
||||||
|
utils::hook::call(0x503B5D, execute_server);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(scheduler::component)
|
23
src/component/scheduler.hpp
Normal file
23
src/component/scheduler.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
enum pipeline
|
||||||
|
{
|
||||||
|
server,
|
||||||
|
async,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const bool cond_continue = false;
|
||||||
|
static const bool cond_end = true;
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void loop(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void once(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
|
||||||
|
void on_init(const std::function<void()>& callback);
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
#include <stdinc.hpp>
|
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "signatures.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace signatures
|
|
||||||
{
|
|
||||||
size_t load_image_size()
|
|
||||||
{
|
|
||||||
MODULEINFO info{};
|
|
||||||
GetModuleInformation(GetCurrentProcess(),
|
|
||||||
GetModuleHandle("plutonium-bootstrapper-win32.exe"), &info, sizeof(MODULEINFO));
|
|
||||||
return info.SizeOfImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t get_image_size()
|
|
||||||
{
|
|
||||||
static const auto image_size = load_image_size();
|
|
||||||
return image_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t find_string_ptr(const std::string& string)
|
|
||||||
{
|
|
||||||
const char* string_ptr = nullptr;
|
|
||||||
std::string mask(string.size(), 'x');
|
|
||||||
const auto base = reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
|
||||||
utils::hook::signature signature(base, get_image_size() - base);
|
|
||||||
|
|
||||||
signature.add({
|
|
||||||
string,
|
|
||||||
mask,
|
|
||||||
[&](char* address)
|
|
||||||
{
|
|
||||||
string_ptr = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
signature.process();
|
|
||||||
return reinterpret_cast<size_t>(string_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t find_string_ref(const std::string& string)
|
|
||||||
{
|
|
||||||
char bytes[4] = {0};
|
|
||||||
const auto string_ptr = find_string_ptr(string);
|
|
||||||
memcpy(bytes, &string_ptr, sizeof(bytes));
|
|
||||||
return find_string_ptr({bytes, 4});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool process_printf()
|
|
||||||
{
|
|
||||||
const auto string_ref = find_string_ref("A critical exception occured!\n");
|
|
||||||
if (!string_ref)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto offset = *reinterpret_cast<size_t*>(string_ref + 5);
|
|
||||||
OutputDebugString(utils::string::va("%p\n", string_ref + 4 + 5 + offset));
|
|
||||||
game::plutonium::printf.set(string_ref + 4 + 5 + offset);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool process()
|
|
||||||
{
|
|
||||||
return process_printf();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace signatures
|
|
||||||
{
|
|
||||||
bool process();
|
|
||||||
}
|
|
52
src/component/test.cpp
Normal file
52
src/component/test.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
game::dvar_s* custom_dvar;
|
||||||
|
game::dvar_s* custom_string_dvar;
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
game::Cmd_AddCommand("testcmd", []()
|
||||||
|
{
|
||||||
|
if (game::Cmd_Argc() == 2)
|
||||||
|
{
|
||||||
|
printf("test %s\n", game::Cmd_Argv(1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("test\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
custom_dvar = game::Dvar_RegisterInt("testdvar1", 69, -69, 420, game::DVAR_FLAG_NONE, "This dvar is a dvar");
|
||||||
|
|
||||||
|
scheduler::on_init([]()
|
||||||
|
{
|
||||||
|
custom_string_dvar = game::Dvar_RegisterString("testdvar2", "This might be a dvar value", game::DVAR_FLAG_NONE, "This dvar is a dvar");
|
||||||
|
printf("We initeded bois\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler::loop([]()
|
||||||
|
{
|
||||||
|
//printf("Biggie Spam McCheese\n");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(test::component)
|
@ -3,28 +3,12 @@
|
|||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include "component/signatures.hpp"
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
||||||
{
|
{
|
||||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
{
|
{
|
||||||
if (!signatures::process())
|
|
||||||
{
|
|
||||||
MessageBoxA(NULL,
|
|
||||||
"This version of T4SP-Server-Plugin is outdated.\n" \
|
|
||||||
"Download the latest dll from here: https://github.com/fedddddd/t6-gsc-utils/releases",
|
|
||||||
"ERROR", MB_ICONERROR);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game::plutonium::printf.get() != nullptr)
|
|
||||||
{
|
|
||||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), game::plutonium::printf);
|
|
||||||
}
|
|
||||||
|
|
||||||
component_loader::post_unpack();
|
component_loader::post_unpack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
94
src/exception/minidump.cpp
Normal file
94
src/exception/minidump.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "minidump.hpp"
|
||||||
|
|
||||||
|
#include <DbgHelp.h>
|
||||||
|
#pragma comment(lib, "dbghelp.lib")
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
namespace exception
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr MINIDUMP_TYPE get_minidump_type()
|
||||||
|
{
|
||||||
|
const auto type = MiniDumpIgnoreInaccessibleMemory //
|
||||||
|
| MiniDumpWithHandleData //
|
||||||
|
| MiniDumpScanMemory //
|
||||||
|
| MiniDumpWithProcessThreadData //
|
||||||
|
| MiniDumpWithFullMemoryInfo //
|
||||||
|
| MiniDumpWithThreadInfo //
|
||||||
|
| MiniDumpWithUnloadedModules;
|
||||||
|
|
||||||
|
return static_cast<MINIDUMP_TYPE>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_temp_filename()
|
||||||
|
{
|
||||||
|
char filename[MAX_PATH] = {0};
|
||||||
|
char pathname[MAX_PATH] = {0};
|
||||||
|
|
||||||
|
GetTempPathA(sizeof(pathname), pathname);
|
||||||
|
GetTempFileNameA(pathname, "Plutonium T4 -", 0, filename);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE write_dump_to_temp_file(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
MINIDUMP_EXCEPTION_INFORMATION minidump_exception_info = {GetCurrentThreadId(), exceptioninfo, FALSE};
|
||||||
|
|
||||||
|
auto* const file_handle = CreateFileA(get_temp_filename().data(), GENERIC_WRITE | GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, get_minidump_type(),
|
||||||
|
&minidump_exception_info,
|
||||||
|
nullptr,
|
||||||
|
nullptr))
|
||||||
|
{
|
||||||
|
MessageBoxA(nullptr, "There was an error creating the minidump! Hit OK to close the program.",
|
||||||
|
"Minidump Error", MB_OK | MB_ICONERROR);
|
||||||
|
TerminateProcess(GetCurrentProcess(), 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read_file(HANDLE file_handle)
|
||||||
|
{
|
||||||
|
FlushFileBuffers(file_handle);
|
||||||
|
SetFilePointer(file_handle, 0, nullptr, FILE_BEGIN);
|
||||||
|
|
||||||
|
std::string buffer{};
|
||||||
|
|
||||||
|
DWORD bytes_read = 0;
|
||||||
|
char temp_bytes[0x2000];
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!ReadFile(file_handle, temp_bytes, sizeof(temp_bytes), &bytes_read, nullptr))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append(temp_bytes, bytes_read);
|
||||||
|
}
|
||||||
|
while (bytes_read == sizeof(temp_bytes));
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string create_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||||
|
{
|
||||||
|
auto* const file_handle = write_dump_to_temp_file(exceptioninfo);
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([file_handle]()
|
||||||
|
{
|
||||||
|
CloseHandle(file_handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
return read_file(file_handle);
|
||||||
|
}
|
||||||
|
}
|
6
src/exception/minidump.hpp
Normal file
6
src/exception/minidump.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace exception
|
||||||
|
{
|
||||||
|
std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo);
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
#include "game.hpp"
|
#include "game.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/memory.hpp>
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
@ -22,11 +23,58 @@ namespace game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Cmd_Argv(int index)
|
||||||
|
{
|
||||||
|
static const auto call_addr = SELECT(0x0, 0x435CE0);
|
||||||
|
const char* answer;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax, index;
|
||||||
|
call call_addr;
|
||||||
|
mov answer, eax;
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Cmd_Argc()
|
||||||
|
{
|
||||||
|
return cmd_args->argc[cmd_args->nesting];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cmd_AddCommand(const char* name, void(__cdecl* function)())
|
||||||
|
{
|
||||||
|
auto newCmd = utils::memory::allocate<game::cmd_function_s>();
|
||||||
|
auto name_dup = utils::memory::duplicate_string(name);
|
||||||
|
|
||||||
|
*newCmd = {};
|
||||||
|
newCmd->next = *game::cmd_functions;
|
||||||
|
newCmd->function = function;
|
||||||
|
newCmd->name = name_dup;
|
||||||
|
|
||||||
|
*game::cmd_functions = newCmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
dvar_s* Dvar_RegisterInt(const char* name, int value, int min, int max, DvarFlags flags, const char* desc)
|
||||||
|
{
|
||||||
|
DvarLimits limits;
|
||||||
|
DvarValue dvar_value;
|
||||||
|
limits.integer.min = min;
|
||||||
|
limits.integer.max = max;
|
||||||
|
dvar_value.integer = value;
|
||||||
|
return Dvar_RegisterVariant(name, game::DVAR_TYPE_INT, flags, dvar_value, limits, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
dvar_s* Dvar_RegisterString(const char* name, const char* value, DvarFlags flags, const char* desc)
|
||||||
|
{
|
||||||
|
DvarLimits limits = {};
|
||||||
|
DvarValue dvar_value;
|
||||||
|
dvar_value.string = value;
|
||||||
|
return Dvar_RegisterVariant(name, game::DVAR_TYPE_STRING, flags, dvar_value, limits, desc);
|
||||||
|
}
|
||||||
|
|
||||||
namespace plutonium
|
namespace plutonium
|
||||||
{
|
{
|
||||||
bool is_up_to_date()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,13 @@ namespace game
|
|||||||
bool t4sp();
|
bool t4sp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Cmd_Argv(int index);
|
||||||
|
unsigned int Cmd_Argc();
|
||||||
|
void Cmd_AddCommand(const char* name, void(__cdecl* function)());
|
||||||
|
|
||||||
|
dvar_s* Dvar_RegisterInt(const char* name, int value, int min, int max, DvarFlags flags, const char* desc);
|
||||||
|
dvar_s* Dvar_RegisterString(const char* name, const char* value, DvarFlags flags, const char* desc);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class symbol
|
class symbol
|
||||||
{
|
{
|
||||||
|
@ -6,4 +6,159 @@ namespace game
|
|||||||
typedef vec_t vec2_t[2];
|
typedef vec_t vec2_t[2];
|
||||||
typedef vec_t vec3_t[3];
|
typedef vec_t vec3_t[3];
|
||||||
typedef vec_t vec4_t[4];
|
typedef vec_t vec4_t[4];
|
||||||
|
|
||||||
|
struct scr_entref_s
|
||||||
|
{
|
||||||
|
__int16 entnum;
|
||||||
|
__int16 classnum;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void(__cdecl* BuiltinFunction)();
|
||||||
|
|
||||||
|
typedef void(__cdecl* BuiltinMethod)(scr_entref_s);
|
||||||
|
|
||||||
|
enum con_channel_e
|
||||||
|
{
|
||||||
|
CON_CHANNEL_DONT_FILTER = 0x0,
|
||||||
|
CON_CHANNEL_ERROR = 0x1,
|
||||||
|
CON_CHANNEL_GAMENOTIFY = 0x2,
|
||||||
|
CON_CHANNEL_BOLDGAME = 0x3,
|
||||||
|
CON_CHANNEL_SUBTITLE = 0x4,
|
||||||
|
CON_CHANNEL_OBITUARY = 0x5,
|
||||||
|
CON_CHANNEL_LOGFILEONLY = 0x6,
|
||||||
|
CON_CHANNEL_CONSOLEONLY = 0x7,
|
||||||
|
CON_CHANNEL_GFX = 0x8,
|
||||||
|
CON_CHANNEL_SOUND = 0x9,
|
||||||
|
CON_CHANNEL_FILES = 0xA,
|
||||||
|
CON_CHANNEL_DEVGUI = 0xB,
|
||||||
|
CON_CHANNEL_PROFILE = 0xC,
|
||||||
|
CON_CHANNEL_UI = 0xD,
|
||||||
|
CON_CHANNEL_CLIENT = 0xE,
|
||||||
|
CON_CHANNEL_SERVER = 0xF,
|
||||||
|
CON_CHANNEL_SYSTEM = 0x10,
|
||||||
|
CON_CHANNEL_PLAYERWEAP = 0x11,
|
||||||
|
CON_CHANNEL_AI = 0x12,
|
||||||
|
CON_CHANNEL_ANIM = 0x13,
|
||||||
|
CON_CHANNEL_PHYS = 0x14,
|
||||||
|
CON_CHANNEL_FX = 0x15,
|
||||||
|
CON_CHANNEL_LEADERBOARDS = 0x16,
|
||||||
|
CON_CHANNEL_LIVE = 0x17,
|
||||||
|
CON_CHANNEL_PARSERSCRIPT = 0x18,
|
||||||
|
CON_CHANNEL_SCRIPT = 0x19,
|
||||||
|
CON_CHANNEL_SPAWNSYSTEM = 0x1A,
|
||||||
|
CON_CHANNEL_COOPINFO = 0x1B,
|
||||||
|
CON_CHANNEL_SERVERDEMO = 0x1C,
|
||||||
|
CON_CHANNEL_DDL = 0x1D,
|
||||||
|
CON_CHANNEL_NETWORK = 0x1E,
|
||||||
|
CON_CHANNEL_SCHEDULER = 0x1F,
|
||||||
|
CON_FIRST_DEBUG_CHANNEL = 0x1F,
|
||||||
|
CON_CHANNEL_TASK = 0x20,
|
||||||
|
CON_CHANNEL_SPU = 0x21,
|
||||||
|
CON_BUILTIN_CHANNEL_COUNT = 0x22,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdArgs
|
||||||
|
{
|
||||||
|
int nesting;
|
||||||
|
int localClientNum[8];
|
||||||
|
int controllerIndex[8];
|
||||||
|
int argc[8];
|
||||||
|
char** argv[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_function_s
|
||||||
|
{
|
||||||
|
cmd_function_s* next;
|
||||||
|
const char* name;
|
||||||
|
char unk[8];
|
||||||
|
void(__cdecl* function)();
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DvarFlags : unsigned __int16
|
||||||
|
{
|
||||||
|
DVAR_FLAG_NONE = 0x0,
|
||||||
|
DVAR_FLAG_ARCHIVE = 0x1,
|
||||||
|
DVAR_FLAG_USERINFO = 0x2,
|
||||||
|
DVAR_FLAG_SERVERINFO = 0x4,
|
||||||
|
DVAR_FLAG_SYSTEMINFO = 0x8,
|
||||||
|
DVAR_FLAG_INIT = 0x10,
|
||||||
|
DVAR_FLAG_LATCH = 0x20,
|
||||||
|
DVAR_FLAG_ROM = 0x40,
|
||||||
|
DVAR_FLAG_CHEAT = 0x80,
|
||||||
|
DVAR_FLAG_CONFIG = 0x100,
|
||||||
|
DVAR_FLAG_SAVED = 0x200,
|
||||||
|
DVAR_FLAG_NORESTART = 0x400,
|
||||||
|
DVAR_FLAG_UNK3 = 0x800,
|
||||||
|
DVAR_FLAG_CHANGEABLE_RESET = 0x1000,
|
||||||
|
DVAR_FLAG_UNK4 = 0x2000,
|
||||||
|
DVAR_FLAG_EXTERNAL = 0x4000,
|
||||||
|
DVAR_FLAG_AUTOEXEC = 0x8000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dvarType_t : unsigned __int8
|
||||||
|
{
|
||||||
|
DVAR_TYPE_BOOL = 0x0,
|
||||||
|
DVAR_TYPE_FLOAT = 0x1,
|
||||||
|
DVAR_TYPE_FLOAT_2 = 0x2,
|
||||||
|
DVAR_TYPE_FLOAT_3 = 0x3,
|
||||||
|
DVAR_TYPE_FLOAT_4 = 0x4,
|
||||||
|
DVAR_TYPE_INT = 0x5,
|
||||||
|
DVAR_TYPE_ENUM = 0x6,
|
||||||
|
DVAR_TYPE_STRING = 0x7,
|
||||||
|
DVAR_TYPE_COLOR = 0x8,
|
||||||
|
DVAR_TYPE_COUNT = 0x9,
|
||||||
|
};
|
||||||
|
|
||||||
|
union DvarValue
|
||||||
|
{
|
||||||
|
bool enabled;
|
||||||
|
int integer;
|
||||||
|
unsigned int unsignedInt;
|
||||||
|
float value;
|
||||||
|
float vector[4];
|
||||||
|
const char* string;
|
||||||
|
char color[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
union __declspec(align(4)) DvarLimits
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int stringCount;
|
||||||
|
const char** strings;
|
||||||
|
} enumeration;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
} integer;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
} value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
} vector;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __declspec(align(4)) dvar_s
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
const char* description;
|
||||||
|
DvarFlags flags;
|
||||||
|
dvarType_t type;
|
||||||
|
char modified;
|
||||||
|
int pad;
|
||||||
|
DvarValue current;
|
||||||
|
DvarValue latched;
|
||||||
|
DvarValue reset;
|
||||||
|
DvarValue saved;
|
||||||
|
DvarLimits domain;
|
||||||
|
dvar_s* hashNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(dvar_s) == 0x5C);
|
||||||
}
|
}
|
@ -6,10 +6,15 @@ namespace game
|
|||||||
{
|
{
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
|
WEAK symbol<void(con_channel_e channel, const char* fmt, ...)> Com_PrintF{ 0x0, 0x59A2C0 };
|
||||||
|
WEAK symbol<dvar_s*(const char* name, dvarType_t type, DvarFlags flags, DvarValue dval, DvarLimits dom, const char* desc)> Dvar_RegisterVariant{ 0x0, 0x5EED90 };
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
|
||||||
|
WEAK symbol<cmd_function_s*> cmd_functions{ 0x0, 0x1F416F4 };
|
||||||
|
WEAK symbol<CmdArgs> cmd_args{ 0x0, 0x1F41670 };
|
||||||
|
|
||||||
namespace plutonium
|
namespace plutonium
|
||||||
{
|
{
|
||||||
WEAK symbol<int(const char* fmt, ...)> printf{0, 0};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,4 +53,10 @@
|
|||||||
#pragma comment(lib, "iphlpapi.lib")
|
#pragma comment(lib, "iphlpapi.lib")
|
||||||
#pragma comment(lib, "Crypt32.lib")
|
#pragma comment(lib, "Crypt32.lib")
|
||||||
|
|
||||||
|
#include "game/structs.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#define printf(__fmt__, ...) \
|
||||||
|
game::Com_PrintF(game::CON_CHANNEL_SERVER, __fmt__, __VA_ARGS__)
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
@ -244,4 +244,9 @@ namespace utils::hook
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* get_displacement_addr(int original)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<void*>(*reinterpret_cast<int*>(original + 1) + original + 5);
|
||||||
|
}
|
||||||
}
|
}
|
@ -125,6 +125,8 @@ namespace utils::hook
|
|||||||
|
|
||||||
void* assemble(const std::function<void(assembler&)>& asm_function);
|
void* assemble(const std::function<void(assembler&)>& asm_function);
|
||||||
|
|
||||||
|
void* get_displacement_addr(int original);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T extract(void* address)
|
T extract(void* address)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user