From 74f6a66374d3ae3d57887d7ab3ba0a8d8b194e6a Mon Sep 17 00:00:00 2001 From: JezuzLizard Date: Fri, 24 Mar 2023 15:04:25 -0700 Subject: [PATCH] Add scheduler, add support for GSC method adding, command adding. Some cleanup. Add exception handler. --- README.md | 2 + premake5.lua | 6 +- src/component/exception.cpp | 137 +++++++++++++++++++++++++ src/component/gsc.cpp | 148 +++++++++++++++++++++++++++ src/component/scheduler.cpp | 188 +++++++++++++++++++++++++++++++++++ src/component/scheduler.hpp | 23 +++++ src/component/signatures.cpp | 72 -------------- src/component/signatures.hpp | 6 -- src/component/test.cpp | 52 ++++++++++ src/dllmain.cpp | 16 --- src/exception/minidump.cpp | 94 ++++++++++++++++++ src/exception/minidump.hpp | 6 ++ src/game/game.cpp | 56 ++++++++++- src/game/game.hpp | 7 ++ src/game/structs.hpp | 155 +++++++++++++++++++++++++++++ src/game/symbols.hpp | 7 +- src/stdinc.hpp | 6 ++ src/utils/hook.cpp | 5 + src/utils/hook.hpp | 2 + 19 files changed, 886 insertions(+), 102 deletions(-) create mode 100644 src/component/exception.cpp create mode 100644 src/component/gsc.cpp create mode 100644 src/component/scheduler.cpp create mode 100644 src/component/scheduler.hpp delete mode 100644 src/component/signatures.cpp delete mode 100644 src/component/signatures.hpp create mode 100644 src/component/test.cpp create mode 100644 src/exception/minidump.cpp create mode 100644 src/exception/minidump.hpp diff --git a/README.md b/README.md index c056a73..b0e5810 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # T4SP-Server-Plugin 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. \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index d7f9333..cfffcc9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -30,7 +30,7 @@ end dependencies.load() -workspace "t6-gsc-utils" +workspace "t4sp-server-plugin" location "./build" objdir "%{wks.location}/obj/%{cfg.buildcfg}" targetdir "%{wks.location}/bin/%{cfg.buildcfg}" @@ -66,9 +66,9 @@ workspace "t6-gsc-utils" defines { "DEBUG", "_DEBUG" } filter {} - startproject "t6-gsc-utils" + startproject "t4sp-server-plugin" - project "t6-gsc-utils" + project "t4sp-server-plugin" kind "SharedLib" language "C++" diff --git a/src/component/exception.cpp b/src/component/exception.cpp new file mode 100644 index 0000000..96b4192 --- /dev/null +++ b/src/component/exception.cpp @@ -0,0 +1,137 @@ +#include +#include "loader/component_loader.hpp" + +#include +#include +#include +#include +#include + +#include + +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(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(&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(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub); + } + }; +} + +REGISTER_COMPONENT(exception::component) diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp new file mode 100644 index 0000000..16ef2db --- /dev/null +++ b/src/component/gsc.cpp @@ -0,0 +1,148 @@ +#include +#include "loader/component_loader.hpp" + +#include +#include +#include + +namespace gsc +{ + std::unordered_map> 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) + diff --git a/src/component/scheduler.cpp b/src/component/scheduler.cpp new file mode 100644 index 0000000..9ba313a --- /dev/null +++ b/src/component/scheduler.cpp @@ -0,0 +1,188 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "scheduler.hpp" + +#include +#include + +namespace scheduler +{ + namespace + { + struct task + { + std::function handler{}; + std::chrono::milliseconds interval{}; + std::chrono::high_resolution_clock::time_point last_call{}; + }; + + using task_list = std::vector; + + 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 new_callbacks_; + utils::concurrency::container callbacks_; + + void merge_callbacks() + { + callbacks_.access([&](task_list& tasks) + { + new_callbacks_.access([&](task_list& new_tasks) + { + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_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> 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(); + on_post_init_hook(); + } + } + + void schedule(const std::function& 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& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_continue; + }, type, delay); + } + + void once(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_end; + }, type, delay); + } + + void on_init(const std::function& 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) diff --git a/src/component/scheduler.hpp b/src/component/scheduler.hpp new file mode 100644 index 0000000..baf6454 --- /dev/null +++ b/src/component/scheduler.hpp @@ -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& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); + void loop(const std::function& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); + void once(const std::function& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); + + void on_init(const std::function& callback); +} diff --git a/src/component/signatures.cpp b/src/component/signatures.cpp deleted file mode 100644 index 1be06b2..0000000 --- a/src/component/signatures.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include - -#include "game/game.hpp" -#include "signatures.hpp" - -#include -#include - -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(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(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(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(); - } -} diff --git a/src/component/signatures.hpp b/src/component/signatures.hpp deleted file mode 100644 index 046cf80..0000000 --- a/src/component/signatures.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace signatures -{ - bool process(); -} \ No newline at end of file diff --git a/src/component/test.cpp b/src/component/test.cpp new file mode 100644 index 0000000..1b2de3e --- /dev/null +++ b/src/component/test.cpp @@ -0,0 +1,52 @@ +#include +#include "loader/component_loader.hpp" + +#include "scheduler.hpp" + +#include +#include + +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) diff --git a/src/dllmain.cpp b/src/dllmain.cpp index aba749e..f628648 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -3,28 +3,12 @@ #include "game/game.hpp" -#include "component/signatures.hpp" #include BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/) { 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(&printf), game::plutonium::printf); - } - component_loader::post_unpack(); } diff --git a/src/exception/minidump.cpp b/src/exception/minidump.cpp new file mode 100644 index 0000000..7e20c3c --- /dev/null +++ b/src/exception/minidump.cpp @@ -0,0 +1,94 @@ +#include +#include "minidump.hpp" + +#include +#pragma comment(lib, "dbghelp.lib") + +#include + +namespace exception +{ + namespace + { + constexpr MINIDUMP_TYPE get_minidump_type() + { + const auto type = MiniDumpIgnoreInaccessibleMemory // + | MiniDumpWithHandleData // + | MiniDumpScanMemory // + | MiniDumpWithProcessThreadData // + | MiniDumpWithFullMemoryInfo // + | MiniDumpWithThreadInfo // + | MiniDumpWithUnloadedModules; + + return static_cast(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); + } +} diff --git a/src/exception/minidump.hpp b/src/exception/minidump.hpp new file mode 100644 index 0000000..9378639 --- /dev/null +++ b/src/exception/minidump.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace exception +{ + std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo); +} diff --git a/src/game/game.cpp b/src/game/game.cpp index 2975417..e512e15 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2,6 +2,7 @@ #include "game.hpp" #include +#include 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(); + 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 { - bool is_up_to_date() - { - return true; - } } } diff --git a/src/game/game.hpp b/src/game/game.hpp index 99ce037..2b3a933 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -21,6 +21,13 @@ namespace game 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 class symbol { diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 731be76..d40a4a4 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -6,4 +6,159 @@ namespace game typedef vec_t vec2_t[2]; typedef vec_t vec3_t[3]; 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); } \ No newline at end of file diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index 3f17147..b6e2f3d 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -6,10 +6,15 @@ namespace game { // Functions + WEAK symbol Com_PrintF{ 0x0, 0x59A2C0 }; + WEAK symbol Dvar_RegisterVariant{ 0x0, 0x5EED90 }; + // Variables + WEAK symbol cmd_functions{ 0x0, 0x1F416F4 }; + WEAK symbol cmd_args{ 0x0, 0x1F41670 }; + namespace plutonium { - WEAK symbol printf{0, 0}; } } diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 8f012d3..e357684 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -53,4 +53,10 @@ #pragma comment(lib, "iphlpapi.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; \ No newline at end of file diff --git a/src/utils/hook.cpp b/src/utils/hook.cpp index 7248bec..50d1c77 100644 --- a/src/utils/hook.cpp +++ b/src/utils/hook.cpp @@ -244,4 +244,9 @@ namespace utils::hook return result; } + + void* get_displacement_addr(int original) + { + return reinterpret_cast(*reinterpret_cast(original + 1) + original + 5); + } } \ No newline at end of file diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp index 160ea38..7e2f89a 100644 --- a/src/utils/hook.hpp +++ b/src/utils/hook.hpp @@ -125,6 +125,8 @@ namespace utils::hook void* assemble(const std::function& asm_function); + void* get_displacement_addr(int original); + template T extract(void* address) {