diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d719e80..e60acb2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - Release steps: - name: Check out files - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 @@ -27,10 +27,9 @@ jobs: lfs: false - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.3.1 - name: Generate project files - #run: tools/premake5 vs2022 --ci-build run: tools/premake5 vs2022 - name: Set up problem matching @@ -40,7 +39,7 @@ jobs: run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:PlatformTarget=x86 build/t5-gsc-utils.sln - name: Upload ${{matrix.configuration}} binaries - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.3 with: name: ${{matrix.configuration}} binaries path: | diff --git a/deps/curl b/deps/curl index 134963a..ab885ec 160000 --- a/deps/curl +++ b/deps/curl @@ -1 +1 @@ -Subproject commit 134963a5efdc3906257c88ce62dba8d46c292908 +Subproject commit ab885eccd6c82a949e21cfab9af778c0fd44d0ae diff --git a/premake5.lua b/premake5.lua index 50600a3..00bf2af 100644 --- a/premake5.lua +++ b/premake5.lua @@ -92,7 +92,6 @@ workspace "t5-gsc-utils" pchheader "stdinc.hpp" pchsource "src/stdinc.cpp" - buildoptions { "/Zm100 -Zm100" } dependencies.imports() diff --git a/src/component/command.cpp b/src/component/command.cpp index 42baa29..fbde68e 100644 --- a/src/component/command.cpp +++ b/src/component/command.cpp @@ -29,7 +29,7 @@ namespace command game::CmdArgs* get_cmd_args() { - return reinterpret_cast(game::Sys_GetValue(4)); + return static_cast(game::Sys_GetValue(4)); } void main_handler() @@ -315,7 +315,7 @@ namespace command gsc::method::add("tell", [](const scripting::entity& player, const std::string& msg) { const auto entref = player.get_entity_reference(); - if (entref.classnum != 0 || entref.entnum >= 18) + if (entref.classnum) { throw std::runtime_error("Not a player entity"); } diff --git a/src/component/exception.cpp b/src/component/exception.cpp index c15c277..c6acf94 100644 --- a/src/component/exception.cpp +++ b/src/component/exception.cpp @@ -19,129 +19,129 @@ namespace exception { - namespace - { - thread_local struct - { - DWORD code = 0; - PVOID address = nullptr; - } exception_data; + namespace + { + thread_local struct + { + DWORD code = 0; + PVOID address = nullptr; + } exception_data; - void show_mouse_cursor() - { - while (ShowCursor(TRUE) < 0); - } + 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); + 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!"; + 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 T5 ERROR", MB_ICONERROR); - TerminateProcess(GetCurrentProcess(), exception_data.code); - } + utils::thread::suspend_other_threads(); + show_mouse_cursor(); + MessageBoxA(nullptr, error_str.data(), "Plutonium T5 ERROR", MB_ICONERROR); + TerminateProcess(GetCurrentProcess(), exception_data.code); + } - void reset_state() - { - display_error_dialog(); - } + 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); - }); + 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); - } + 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"); - }; + 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 T5 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)); + line("Plutonium T5 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)); + 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)); + line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion)); - return info; - } + return info; + } - void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo) - { - const std::string crash_name = utils::string::va("minidumps/plutonium-t5-crash-%s.zip", - utils::string::get_timestamp().data()); + void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo) + { + const std::string crash_name = utils::string::va("minidumps/plutonium-t5-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 T5 Crash Dump"); - } + 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 T5 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; - } + 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; - } + LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo) + { + if (is_harmless_error(exceptioninfo)) + { + return EXCEPTION_CONTINUE_EXECUTION; + } - write_minidump(exceptioninfo); + write_minidump(exceptioninfo); - exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode; - exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress; - exceptioninfo->ContextRecord->Eip = get_reset_state_stub(); + exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode; + exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress; + exceptioninfo->ContextRecord->Eip = get_reset_state_stub(); - return EXCEPTION_CONTINUE_EXECUTION; - } + 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; - } - } + 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 - { + class component final : public component_interface + { + public: + void post_unpack() override + { #ifdef DEBUG - SetUnhandledExceptionFilter(exception_filter); - utils::hook::jump(reinterpret_cast(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub); + SetUnhandledExceptionFilter(exception_filter); + utils::hook::jump(reinterpret_cast(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub); #endif - } - }; + } + }; } REGISTER_COMPONENT(exception::component) diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index 563b4d1..cd87a5c 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -6,336 +6,333 @@ #include "gsc.hpp" -#include "scheduler.hpp" -#include "scripting.hpp" - #include -#include #include +#include namespace gsc { - namespace - { - utils::hook::detour get_function_hook; - utils::hook::detour get_method_hook; + namespace + { + utils::hook::detour get_function_hook; + utils::hook::detour get_method_hook; - std::unordered_map functions; - std::unordered_map methods; + std::unordered_map functions; + std::unordered_map methods; - std::unordered_map function_wraps; - std::unordered_map method_wraps; + std::unordered_map function_wraps; + std::unordered_map method_wraps; - std::vector get_arguments() - { - std::vector args; + std::vector get_arguments() + { + std::vector args; - for (auto i = 0; static_cast(i) < game::scr_VmPub->outparamcount; i++) - { - const auto value = game::scr_VmPub->top[-i]; - args.push_back(value); - } + for (auto i = 0; static_cast(i) < game::scr_VmPub->outparamcount; i++) + { + const auto value = game::scr_VmPub->top[-i]; + args.push_back(value); + } - return args; - } + return args; + } - void return_value(const scripting::script_value& value) - { - if (game::scr_VmPub->outparamcount) - { - game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER); - } + void return_value(const scripting::script_value& value) + { + if (game::scr_VmPub->outparamcount) + { + game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER); + } - scripting::push_value(value); - } + scripting::push_value(value); + } - void call_function(const char* name) - { - if (functions.find(name) == functions.end()) - { - return; - } + void call_function(const char* name) + { + if (functions.find(name) == functions.end()) + { + return; + } - const auto args = get_arguments(); - const auto& function = functions[name]; + const auto args = get_arguments(); + const auto& function = functions[name]; - try - { - const auto value = function(args); - return_value(value); - } - catch (const std::exception& e) - { - game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false); - } - } + try + { + const auto value = function(args); + return_value(value); + } + catch (const std::exception& e) + { + game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false); + } + } - void call_method(const char* name, const game::scr_entref_t entref) - { - if (methods.find(name) == methods.end()) - { - return; - } + void call_method(const char* name, const game::scr_entref_t entref) + { + if (methods.find(name) == methods.end()) + { + return; + } - const auto args = get_arguments(); - const auto& method = methods[name]; + const auto args = get_arguments(); + const auto& method = methods[name]; - try - { - const scripting::entity entity = game::Scr_GetEntityId( - game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0); + try + { + const scripting::entity entity = game::Scr_GetEntityId( + game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0); - std::vector args_{}; - args_.push_back(entity); - for (const auto& arg : args) - { - args_.push_back(arg); - } + std::vector args_{}; + args_.push_back(entity); + for (const auto& arg : args) + { + args_.push_back(arg); + } - const auto value = method(args_); - return_value(value); - } - catch (const std::exception& e) - { - game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false); - } - } + const auto value = method(args_); + return_value(value); + } + catch (const std::exception& e) + { + game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false); + } + } - void* wrap_function_call(const std::string& name) - { - const auto name_ = utils::memory::get_allocator()->duplicate_string(name); - return utils::hook::assemble([name_](utils::hook::assembler& a) - { - a.pushad(); - a.push(name_); - a.call(call_function); - a.add(esp, 0x4); - a.popad(); + void* wrap_function_call(const std::string& name) + { + const auto name_ = utils::memory::get_allocator()->duplicate_string(name); + return utils::hook::assemble([name_](utils::hook::assembler& a) + { + a.pushad(); + a.push(name_); + a.call(call_function); + a.add(esp, 0x4); + a.popad(); - a.ret(); - }); - } + a.ret(); + }); + } - void* wrap_method_call(const std::string& name) - { - const auto name_ = utils::memory::get_allocator()->duplicate_string(name); - return utils::hook::assemble([name_](utils::hook::assembler& a) - { - a.pushad(); - a.push(dword_ptr(esp, 0x24)); - a.push(name_); - a.call(call_method); - a.add(esp, 0x8); - a.popad(); + void* wrap_method_call(const std::string& name) + { + const auto name_ = utils::memory::get_allocator()->duplicate_string(name); + return utils::hook::assemble([name_](utils::hook::assembler& a) + { + a.pushad(); + a.push(dword_ptr(esp, 0x24)); + a.push(name_); + a.call(call_method); + a.add(esp, 0x8); + a.popad(); - a.ret(); - }); - } + a.ret(); + }); + } - script_function get_function_stub(const char** name, int* type) - { - if (function_wraps.find(*name) != function_wraps.end()) - { - return reinterpret_cast(function_wraps[*name]); - } + script_function get_function_stub(const char** name, int* type) + { + if (function_wraps.find(*name) != function_wraps.end()) + { + return reinterpret_cast(function_wraps[*name]); + } - return get_function_hook.invoke(name, type); - } + return get_function_hook.invoke(name, type); + } - script_function get_method_stub(const char** name, int* type) - { - if (method_wraps.find(*name) != method_wraps.end()) - { - return reinterpret_cast(method_wraps[*name]); - } + script_function get_method_stub(const char** name, int* type) + { + if (method_wraps.find(*name) != method_wraps.end()) + { + return reinterpret_cast(method_wraps[*name]); + } - return get_method_hook.invoke(name, type); - } + return get_method_hook.invoke(name, type); + } - void print(int, const char* fmt, ...) - { - char buffer[2048]; + void print(int, const char* fmt, ...) + { + char buffer[2048]; - va_list ap; - va_start(ap, fmt); + va_list ap; + va_start(ap, fmt); - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); - va_end(ap); + va_end(ap); - printf("%s", buffer); - } + printf("%s", buffer); + } - utils::hook::detour scr_settings_hook; - void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst) - { - scr_settings_hook.invoke(developer_script, developer_script, 0, inst); - } + utils::hook::detour scr_settings_hook; + void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst) + { + scr_settings_hook.invoke(developer_script, developer_script, 0, inst); + } - utils::hook::detour scr_get_builtin_hook; - unsigned int scr_get_builtin_stub(int inst, game::sval_u func_name) - { - const auto type = *reinterpret_cast(func_name.block); - if (type != 28) - { - return scr_get_builtin_hook.invoke(inst, func_name); - } + utils::hook::detour scr_get_builtin_hook; + unsigned int scr_get_builtin_stub(int inst, game::sval_u func_name) + { + const auto type = *reinterpret_cast(func_name.block); + if (type != 28) + { + return scr_get_builtin_hook.invoke(inst, func_name); + } - const auto func_namea = *reinterpret_cast(reinterpret_cast(func_name.block) + 4); - const auto typea = *reinterpret_cast(func_namea); - if (typea != 20) - { - return scr_get_builtin_hook.invoke(inst, func_name); - } + const auto func_namea = *reinterpret_cast(reinterpret_cast(func_name.block) + 4); + const auto typea = *reinterpret_cast(func_namea); + if (typea != 20) + { + return scr_get_builtin_hook.invoke(inst, func_name); + } - const auto func_nameb = *reinterpret_cast(reinterpret_cast(func_namea) + 4); - const auto typeb = *reinterpret_cast(func_nameb); + const auto func_nameb = *reinterpret_cast(reinterpret_cast(func_namea) + 4); + const auto typeb = *reinterpret_cast(func_nameb); - if (typeb == 23) // script::function type call - { - const auto namespace_ = game::SL_ConvertToString( - *reinterpret_cast(reinterpret_cast(func_nameb) + 4), game::SCRIPTINSTANCE_SERVER); - const auto name = game::SL_ConvertToString( - *reinterpret_cast(reinterpret_cast(func_nameb) + 8), game::SCRIPTINSTANCE_SERVER); + if (typeb == 23) // script::function type call + { + const auto namespace_ = game::SL_ConvertToString( + *reinterpret_cast(reinterpret_cast(func_nameb) + 4), game::SCRIPTINSTANCE_SERVER); + const auto name = game::SL_ConvertToString( + *reinterpret_cast(reinterpret_cast(func_nameb) + 8), game::SCRIPTINSTANCE_SERVER); - const auto full_name = utils::string::va("%s::%s", namespace_, name); - if (functions.find(full_name) != functions.end()) - { - return game::SL_GetString(full_name, 0, game::SCRIPTINSTANCE_SERVER); - } - } + const auto full_name = utils::string::va("%s::%s", namespace_, name); + if (functions.find(full_name) != functions.end()) + { + return game::SL_GetString(full_name, 0, game::SCRIPTINSTANCE_SERVER); + } + } - return scr_get_builtin_hook.invoke(inst, func_name); - } + return scr_get_builtin_hook.invoke(inst, func_name); + } - // Scr_NotifyId doesn't exist, Scr_NotifyNum_Internal calls FindVariableId to get the variable id from entnum, classnum & clientNum - // to not have to recreate Scr_NotifyId we simply make FindVariableId return `entnum` (which in this case will be the id) if `clientNum` == -1 - unsigned int find_variable_id_stub(int inst, int entnum, unsigned int classnum, int client_num) - { - if (client_num == -1) - { - return entnum; - } + // Scr_NotifyId doesn't exist, Scr_NotifyNum_Internal calls FindVariableId to get the variable id from entnum, classnum & clientNum + // to not have to recreate Scr_NotifyId we simply make FindVariableId return `entnum` (which in this case will be the id) if `clientNum` == -1 + unsigned int find_variable_id_stub(int inst, int entnum, unsigned int classnum, int client_num) + { + if (client_num == -1) + { + return entnum; + } - return utils::hook::invoke(SELECT_VALUE(0x5E96E0, 0x40BEF0), inst, entnum, classnum, client_num); - } - } + return utils::hook::invoke(SELECT_VALUE(0x5E96E0, 0x40BEF0), inst, entnum, classnum, client_num); + } + } - namespace function - { - void add_internal(const std::string& name, const function_t& function) - { - const auto name_ = utils::string::to_lower(name); - functions[name_] = function; - const auto call_wrap = wrap_function_call(name_); - function_wraps[name_] = call_wrap; - } - } + namespace function + { + void add_internal(const std::string& name, const function_t& function) + { + const auto name_ = utils::string::to_lower(name); + functions[name_] = function; + const auto call_wrap = wrap_function_call(name_); + function_wraps[name_] = call_wrap; + } + } - namespace method - { - void add_internal(const std::string& name, const function_t& method) - { - const auto name_ = utils::string::to_lower(name); - methods[name_] = method; - const auto call_wrap = wrap_method_call(name_); - method_wraps[name_] = call_wrap; - } - } + namespace method + { + void add_internal(const std::string& name, const function_t& method) + { + const auto name_ = utils::string::to_lower(name); + methods[name_] = method; + const auto call_wrap = wrap_method_call(name_); + method_wraps[name_] = call_wrap; + } + } - class component final : public component_interface - { - public: - void post_unpack() override - { - // Don't com_error on gsc errors - utils::hook::nop(SELECT_VALUE(0x5A17E1, 0x4D9BB1), 5); - utils::hook::jump(SELECT_VALUE(0x5DFC40, 0x568B90), print); + class component final : public component_interface + { + public: + void post_unpack() override + { + // Don't com_error on gsc errors + utils::hook::nop(SELECT_VALUE(0x5A17E1, 0x4D9BB1), 5); + utils::hook::jump(SELECT_VALUE(0x5DFC40, 0x568B90), print); - scr_settings_hook.create(SELECT_VALUE(0x4CEEA0, 0x55D010), scr_settings_stub); + scr_settings_hook.create(SELECT_VALUE(0x4CEEA0, 0x55D010), scr_settings_stub); - get_function_hook.create(utils::hook::extract(SELECT_VALUE(0x8A02FB, 0x8DE11B) + 1), get_function_stub); - get_method_hook.create(utils::hook::extract(SELECT_VALUE(0x8A052E, 0x8DE34E) + 1), get_method_stub); + get_function_hook.create(utils::hook::extract(SELECT_VALUE(0x8A02FB, 0x8DE11B) + 1), get_function_stub); + get_method_hook.create(utils::hook::extract(SELECT_VALUE(0x8A052E, 0x8DE34E) + 1), get_method_stub); - scr_get_builtin_hook.create(SELECT_VALUE(0x4ACAC0, 0x411490), scr_get_builtin_stub); + scr_get_builtin_hook.create(SELECT_VALUE(0x4ACAC0, 0x411490), scr_get_builtin_stub); - // \n******* script runtime error *******\n%s\n - utils::hook::set(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n'); - utils::hook::set(SELECT_VALUE(0x9FC5C0 + 41, 0xAABA68 + 41), '\0'); + // \n******* script runtime error *******\n%s\n + utils::hook::set(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n'); + utils::hook::set(SELECT_VALUE(0x9FC5C0 + 41, 0xAABA68 + 41), '\0'); - utils::hook::call(SELECT_VALUE(0x41D2B5, 0x416325), find_variable_id_stub); + utils::hook::call(SELECT_VALUE(0x41D2B5, 0x416325), find_variable_id_stub); - gsc::function::add("array", [](const scripting::variadic_args& va) - { - scripting::array array{}; + gsc::function::add("array", [](const scripting::variadic_args& va) + { + scripting::array array{}; - for (const auto& arg : va) - { - array.push(arg); - } + for (const auto& arg : va) + { + array.push(arg); + } - return array; - }); + return array; + }); - gsc::function::add_multiple([](const scripting::script_value& value) - { - return value.type_name(); - }, "typeof", "type"); + gsc::function::add_multiple([](const scripting::script_value& value) + { + return value.type_name(); + }, "typeof", "type"); - gsc::function::add("debug::get_var_count", []() - { - auto count = 0; + gsc::function::add("debug::get_var_count", []() + { + auto count = 0; - if (game::environment::is_sp()) - { - for (auto i = 1; i < 0x5FFE; i++) - { - const auto var = game::scr_VarGlob->variableList_mp[i]; - count += var.w.status != 0; - } - } - else - { - for (auto i = 1; i < 0x7FFE; i++) - { - const auto var = game::scr_VarGlob->variableList_mp[i]; - count += var.w.status != 0; - } - } + if (game::environment::is_sp()) + { + for (auto i = 1; i < 0x5FFE; i++) + { + const auto var = game::scr_VarGlob->variableList_mp[i]; + count += var.w.status != 0; + } + } + else + { + for (auto i = 1; i < 0x7FFE; i++) + { + const auto var = game::scr_VarGlob->variableList_mp[i]; + count += var.w.status != 0; + } + } - return count; - }); + return count; + }); - gsc::function::add("toint", [](const std::string& str, const scripting::variadic_args& va) - { - auto radix = 10; - if (va.size() > 0) - { - radix = va[0]; - } + gsc::function::add("toint", [](const std::string& str, const scripting::variadic_args& va) + { + auto radix = 10; + if (!va.empty()) + { + radix = va[0]; + } - return static_cast(std::strtoull(str.data(), nullptr, radix)); - }); + return static_cast(std::strtoull(str.data(), nullptr, radix)); + }); - gsc::function::add("os::date", [](const scripting::variadic_args& va) - { - std::string format = "%Y-%m-%dT%H:%M:%S%z"; - if (va.size() > 0) - { - format = va[0].as(); - } + gsc::function::add("os::date", [](const scripting::variadic_args& va) + { + std::string format = "%Y-%m-%dT%H:%M:%S%z"; + if (!va.empty()) + { + format = va[0].as(); + } - tm ltime{}; - char timestamp[MAX_PATH] = {0}; - const auto time = _time64(nullptr); + tm ltime{}; + char timestamp[MAX_PATH] = {0}; + const auto time = _time64(nullptr); - _localtime64_s(<ime, &time); - std::strftime(timestamp, sizeof(timestamp) - 1, format.data(), <ime); + _localtime64_s(<ime, &time); + std::strftime(timestamp, sizeof(timestamp) - 1, format.data(), <ime); - return std::string(timestamp); - }); - } - }; + return std::string(timestamp); + }); + } + }; } REGISTER_COMPONENT(gsc::component) diff --git a/src/component/http.cpp b/src/component/http.cpp index 633af9b..a22b6d7 100644 --- a/src/component/http.cpp +++ b/src/component/http.cpp @@ -13,165 +13,165 @@ namespace http { - std::unordered_map active_requests{}; - uint64_t request_id{}; + std::unordered_map active_requests{}; + uint64_t request_id{}; - class component final : public component_interface - { - public: - void post_unpack() override - { - scripting::on_shutdown([]() - { - active_requests.clear(); - }); + class component final : public component_interface + { + public: + void post_unpack() override + { + scripting::on_shutdown([]() + { + active_requests.clear(); + }); - gsc::function::add_multiple([](const std::string& url) - { - const auto id = request_id++; - active_requests[id] = true; + gsc::function::add_multiple([](const std::string& url) + { + const auto id = request_id++; + active_requests[id] = true; - const auto object = scripting::object{}; - const auto object_id = object.get_entity_id(); + const auto object = scripting::object{}; + const auto object_id = object.get_entity_id(); - scheduler::once([id, object_id, url]() - { - const auto data = utils::http::get_data(url); - scheduler::once([id, object_id, data]() - { - if (active_requests.find(id) == active_requests.end()) - { - return; - } + scheduler::once([id, object_id, url]() + { + const auto data = utils::http::get_data(url); + scheduler::once([id, object_id, data]() + { + if (active_requests.find(id) == active_requests.end()) + { + return; + } - if (!data.has_value()) - { - scripting::notify(object_id, "done", {{}, false, "Unknown error"}); - return; - } + if (!data.has_value()) + { + scripting::notify(object_id, "done", {{}, false, "Unknown error"}); + return; + } - const auto& result = data.value(); - const auto error = curl_easy_strerror(result.code); + const auto& result = data.value(); + const auto error = curl_easy_strerror(result.code); - if (result.code != CURLE_OK) - { - scripting::notify(object_id, "done", {{}, false, error}); - return; - } + if (result.code != CURLE_OK) + { + scripting::notify(object_id, "done", {{}, false, error}); + return; + } - if (result.buffer.size() >= 0x5000) - { - printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); - } + if (result.buffer.size() >= 0x5000) + { + printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); + } - scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); - }, scheduler::pipeline::server); - }, scheduler::pipeline::async); + scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); + }, scheduler::pipeline::server); + }, scheduler::pipeline::async); - return object; - }, "http::get", "httpget", "curl"); + return object; + }, "http::get", "httpget", "curl"); - gsc::function::add("http::request", [](const std::string& url, const scripting::variadic_args& va) - { - const auto id = request_id++; - active_requests[id] = true; + gsc::function::add("http::request", [](const std::string& url, const scripting::variadic_args& va) + { + const auto id = request_id++; + active_requests[id] = true; - const auto object = scripting::object{}; - const auto object_id = object.get_entity_id(); + const auto object = scripting::object{}; + const auto object_id = object.get_entity_id(); - std::string fields_string{}; - std::unordered_map headers_map{}; + std::string fields_string{}; + std::unordered_map headers_map{}; - if (va.size() > 0) - { - const auto options = va[0].as(); + if (va.size() > 0) + { + const auto options = va[0].as(); - const auto fields = options["parameters"]; - const auto body = options["body"]; - const auto headers = options["headers"]; + const auto fields = options["parameters"]; + const auto body = options["body"]; + const auto headers = options["headers"]; - if (fields.is()) - { - const auto fields_ = fields.as(); - const auto keys = fields_.get_keys(); + if (fields.is()) + { + const auto fields_ = fields.as(); + const auto keys = fields_.get_keys(); - for (const auto& key : keys) - { - if (!key.is()) - { - continue; - } + for (const auto& key : keys) + { + if (!key.is()) + { + continue; + } - const auto key_ = key.as(); - const auto value = fields_[key].to_string(); - fields_string += key_ + "=" + value + "&"; - } + const auto key_ = key.as(); + const auto value = fields_[key].to_string(); + fields_string += key_ + "=" + value + "&"; + } - } + } - if (body.is()) - { - fields_string = body.as(); - } + if (body.is()) + { + fields_string = body.as(); + } - if (headers.is()) - { - const auto headers_ = headers.as(); - const auto keys = headers_.get_keys(); + if (headers.is()) + { + const auto headers_ = headers.as(); + const auto keys = headers_.get_keys(); - for (const auto& key : keys) - { - if (!key.is()) - { - continue; - } + for (const auto& key : keys) + { + if (!key.is()) + { + continue; + } - const auto key_ = key.as(); - const auto value = headers_[key].to_string(); + const auto key_ = key.as(); + const auto value = headers_[key].to_string(); - headers_map[key_] = value; - } - } - } + headers_map[key_] = value; + } + } + } - scheduler::once([id, object_id, url, fields_string, headers_map]() - { - const auto data = utils::http::get_data(url, fields_string, headers_map); - scheduler::once([data, object_id, id] - { - if (active_requests.find(id) == active_requests.end()) - { - return; - } + scheduler::once([id, object_id, url, fields_string, headers_map]() + { + const auto data = utils::http::get_data(url, fields_string, headers_map); + scheduler::once([data, object_id, id] + { + if (active_requests.find(id) == active_requests.end()) + { + return; + } - if (!data.has_value()) - { - scripting::notify(object_id, "done", {{}, false, "Unknown error"}); - return; - } + if (!data.has_value()) + { + scripting::notify(object_id, "done", {{}, false, "Unknown error"}); + return; + } - const auto& result = data.value(); - const auto error = curl_easy_strerror(result.code); + const auto& result = data.value(); + const auto error = curl_easy_strerror(result.code); - if (result.code != CURLE_OK) - { - scripting::notify(object_id, "done", {{}, false, error}); - return; - } + if (result.code != CURLE_OK) + { + scripting::notify(object_id, "done", {{}, false, error}); + return; + } - if (result.buffer.size() >= 0x5000) - { - printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); - } + if (result.buffer.size() >= 0x5000) + { + printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); + } - scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); - }, scheduler::pipeline::server); - }, scheduler::pipeline::async); + scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); + }, scheduler::pipeline::server); + }, scheduler::pipeline::async); - return object; - }); - } - }; + return object; + }); + } + }; } REGISTER_COMPONENT(http::component) diff --git a/src/component/io.cpp b/src/component/io.cpp index 3bdbbba..01c5006 100644 --- a/src/component/io.cpp +++ b/src/component/io.cpp @@ -10,57 +10,57 @@ namespace io { - class component final : public component_interface - { - public: - void post_unpack() override - { - const auto fs_basegame = game::Dvar_FindVar("fs_basegame"); - std::filesystem::current_path(fs_basegame->current.string); + class component final : public component_interface + { + public: + void post_unpack() override + { + const auto fs_basegame = game::Dvar_FindVar("fs_basegame"); + std::filesystem::current_path(fs_basegame->current.string); - gsc::function::add_multiple([](const std::string& file, const std::string& data, - const scripting::variadic_args& va) - { - auto append = false; + gsc::function::add_multiple([](const std::string& file, const std::string& data, + const scripting::variadic_args& va) + { + auto append = false; - if (va.size() > 0) - { - append = va[0]; - } + if (va.size() > 0) + { + append = va[0]; + } - return utils::io::write_file(file, data, append); - }, "writefile", "io::write_file"); + return utils::io::write_file(file, data, append); + }, "writefile", "io::write_file"); - gsc::function::add_multiple([](const std::string& file, const std::string& data) - { - return utils::io::write_file(file, data, true); - }, "appendfile", "io::append_file"); + gsc::function::add_multiple([](const std::string& file, const std::string& data) + { + return utils::io::write_file(file, data, true); + }, "appendfile", "io::append_file"); - gsc::function::add_multiple(utils::io::file_exists, "fileexists", "io::file_exists"); - gsc::function::add_multiple(utils::io::move_file, "movefile", "io::move_file"); - gsc::function::add_multiple(utils::io::file_size, "filesize", "io::file_size"); - gsc::function::add_multiple(utils::io::create_directory, "createdirectory", "io::create_directory"); - gsc::function::add_multiple(utils::io::directory_exists, "directoryexists", "io::directory_exists"); - gsc::function::add_multiple(utils::io::directory_is_empty, "directoryisempty", "io::directory_is_empty"); - gsc::function::add_multiple(utils::io::list_files, "listfiles", "io::list_files"); - gsc::function::add_multiple(utils::io::remove_file, "removefile", "io::remove_file"); + gsc::function::add_multiple(utils::io::file_exists, "fileexists", "io::file_exists"); + gsc::function::add_multiple(utils::io::move_file, "movefile", "io::move_file"); + gsc::function::add_multiple(utils::io::file_size, "filesize", "io::file_size"); + gsc::function::add_multiple(utils::io::create_directory, "createdirectory", "io::create_directory"); + gsc::function::add_multiple(utils::io::directory_exists, "directoryexists", "io::directory_exists"); + gsc::function::add_multiple(utils::io::directory_is_empty, "directoryisempty", "io::directory_is_empty"); + gsc::function::add_multiple(utils::io::list_files, "listfiles", "io::list_files"); + gsc::function::add_multiple(utils::io::remove_file, "removefile", "io::remove_file"); - gsc::function::add_multiple([](const std::filesystem::path& src, const scripting::variadic_args& va) - { - bool recursive = false; - if (va.size() > 0) - { - recursive = va[0]; - } + gsc::function::add_multiple([](const std::filesystem::path& src, const scripting::variadic_args& va) + { + bool recursive = false; + if (va.size() > 0) + { + recursive = va[0]; + } - utils::io::remove_directory(src, recursive); - }, "removedirectory", "io::remove_directory"); + utils::io::remove_directory(src, recursive); + }, "removedirectory", "io::remove_directory"); - gsc::function::add_multiple(utils::io::copy_folder, "copyfolder", "io::copy_folder"); - gsc::function::add_multiple(utils::io::copy_folder, "copydirectory", "io::copy_directory"); - gsc::function::add_multiple(static_cast(utils::io::read_file), "readfile", "io::read_file"); - } - }; + gsc::function::add_multiple(utils::io::copy_folder, "copyfolder", "io::copy_folder"); + gsc::function::add_multiple(utils::io::copy_folder, "copydirectory", "io::copy_directory"); + gsc::function::add_multiple(static_cast(utils::io::read_file), "readfile", "io::read_file"); + } + }; } REGISTER_COMPONENT(io::component) diff --git a/src/component/string.cpp b/src/component/string.cpp index 2f049d8..ee3a084 100644 --- a/src/component/string.cpp +++ b/src/component/string.cpp @@ -19,205 +19,205 @@ namespace string { - namespace - { - // lua/lstrlib.c - const char* getformat(const char* strfrmt, char* form) - { - const auto len = std::strspn(strfrmt, L_FMTFLAGSF "123456789.") + 1; - if (len >= MAX_FORMAT - 10) - { - throw std::runtime_error("invalid format (too long)"); - } + namespace + { + // lua/lstrlib.c + const char* getformat(const char* strfrmt, char* form) + { + const auto len = std::strspn(strfrmt, L_FMTFLAGSF "123456789.") + 1; + if (len >= MAX_FORMAT - 10) + { + throw std::runtime_error("invalid format (too long)"); + } - *(form++) = '%'; - std::memcpy(form, strfrmt, len * sizeof(char)); - *(form + len) = '\0'; - return strfrmt + len - 1; - } + *(form++) = '%'; + std::memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; + } - // lua/lstrlib.c - const char* get_2_digits(const char* s) - { - if (isdigit(static_cast(*s))) - { - s++; - if (isdigit(static_cast(*s))) - { - s++; - } - } + // lua/lstrlib.c + const char* get_2_digits(const char* s) + { + if (isdigit(static_cast(*s))) + { + s++; + if (isdigit(static_cast(*s))) + { + s++; + } + } - return s; - } + return s; + } - // lua/lstrlib.c - void check_format(const char* form, const char* flags, int precision) - { - const char* spec = form + 1; - spec += std::strspn(spec, flags); - if (*spec != '0') - { - spec = get_2_digits(spec); - if (*spec == '.' && precision) - { - spec++; - spec = get_2_digits(spec); - } - } - if (!std::isalpha(static_cast(*spec))) - { - throw std::runtime_error(utils::string::va("invalid conversion specification: '%s'", form)); - } - } + // lua/lstrlib.c + void check_format(const char* form, const char* flags, int precision) + { + const char* spec = form + 1; + spec += std::strspn(spec, flags); + if (*spec != '0') + { + spec = get_2_digits(spec); + if (*spec == '.' && precision) + { + spec++; + spec = get_2_digits(spec); + } + } + if (!std::isalpha(static_cast(*spec))) + { + throw std::runtime_error(utils::string::va("invalid conversion specification: '%s'", form)); + } + } - // partially lua/lstrlib.c - std::string format_string(const std::string& fmt, const scripting::variadic_args& va) - { - std::string buffer{}; - size_t va_index{}; - const char* strfrmt = fmt.data(); - const char* strfrmt_end = strfrmt + fmt.size(); + // partially lua/lstrlib.c + std::string format_string(const std::string& fmt, const scripting::variadic_args& va) + { + std::string buffer{}; + size_t va_index{}; + const char* strfrmt = fmt.data(); + const char* strfrmt_end = strfrmt + fmt.size(); - while (strfrmt < strfrmt_end) - { - if (*strfrmt != '%') - { - buffer.push_back(*strfrmt++); - } - else if (*++strfrmt == '%') - { - buffer.push_back(*strfrmt++); - } - else - { - char form[MAX_FORMAT]{}; - const char* flags = ""; - strfrmt = getformat(strfrmt, form); + while (strfrmt < strfrmt_end) + { + if (*strfrmt != '%') + { + buffer.push_back(*strfrmt++); + } + else if (*++strfrmt == '%') + { + buffer.push_back(*strfrmt++); + } + else + { + char form[MAX_FORMAT]{}; + const char* flags = ""; + strfrmt = getformat(strfrmt, form); - switch (*strfrmt++) - { - case 'd': - case 'i': - flags = L_FMTFLAGSI; - goto intcase; - case 'u': - case 'p': - flags = L_FMTFLAGSU; - goto intcase; - case 'o': - case 'x': - case 'X': - flags = L_FMTFLAGSX; - intcase: - { - check_format(form, flags, 1); - const auto value = va[va_index].as(); - buffer.append(utils::string::va(form, value)); - va_index++; - break; - } - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - { - check_format(form, L_FMTFLAGSF, 1); - const auto value = va[va_index].as(); - buffer.append(utils::string::va(form, value)); - va_index++; - break; - } - case 'c': - { - const auto value = va[va_index].as(); - check_format(form, L_FMTFLAGSC, 0); - buffer.append(utils::string::va(form, static_cast(value))); - va_index++; - break; - } - case 's': - { - const auto str = va[va_index].as(); - buffer.append(str); - va_index++; - break; - } - default: - { - throw std::runtime_error(utils::string::va("invalid conversion '%s' to 'format'", form)); - } - } - } - } + switch (*strfrmt++) + { + case 'd': + case 'i': + flags = L_FMTFLAGSI; + goto intcase; + case 'u': + case 'p': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': + case 'x': + case 'X': + flags = L_FMTFLAGSX; + intcase: + { + check_format(form, flags, 1); + const auto value = va[va_index].as(); + buffer.append(utils::string::va(form, value)); + va_index++; + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + { + check_format(form, L_FMTFLAGSF, 1); + const auto value = va[va_index].as(); + buffer.append(utils::string::va(form, value)); + va_index++; + break; + } + case 'c': + { + const auto value = va[va_index].as(); + check_format(form, L_FMTFLAGSC, 0); + buffer.append(utils::string::va(form, static_cast(value))); + va_index++; + break; + } + case 's': + { + const auto str = va[va_index].as(); + buffer.append(str); + va_index++; + break; + } + default: + { + throw std::runtime_error(utils::string::va("invalid conversion '%s' to 'format'", form)); + } + } + } + } - return buffer; - } - } + return buffer; + } + } - class component final : public component_interface - { - public: - void post_unpack() override - { - gsc::function::add_multiple(format_string, "va", "string::va", - "formatstring", "string::format", "sprintf"); + class component final : public component_interface + { + public: + void post_unpack() override + { + gsc::function::add_multiple(format_string, "va", "string::va", + "formatstring", "string::format", "sprintf"); - gsc::function::add("printf", [](const std::string& fmt, const scripting::variadic_args& va) - { - printf("%s", format_string(fmt, va).data()); - }); + gsc::function::add("printf", [](const std::string& fmt, const scripting::variadic_args& va) + { + printf("%s", format_string(fmt, va).data()); + }); - gsc::function::add("print", [](const scripting::variadic_args& va) - { - std::string buffer{}; + gsc::function::add("print", [](const scripting::variadic_args& va) + { + std::string buffer{}; - for (const auto& arg : va) - { - buffer.append(utils::string::va("%s\t", arg.to_string().data())); - } + for (const auto& arg : va) + { + buffer.append(utils::string::va("%s\t", arg.to_string().data())); + } - printf("%s\n", buffer.data()); - }); + printf("%s\n", buffer.data()); + }); - gsc::function::add_multiple(utils::string::to_upper, "toupper", "string::to_upper"); - gsc::function::add_multiple(utils::string::to_lower, "tolower", "string::to_lower"); + gsc::function::add_multiple(utils::string::to_upper, "toupper", "string::to_upper"); + gsc::function::add_multiple(utils::string::to_lower, "tolower", "string::to_lower"); - gsc::function::add("string::is_numeric", utils::string::is_numeric); - gsc::function::add("string::starts_with", utils::string::starts_with); - gsc::function::add("string::ends_with", utils::string::ends_with); - gsc::function::add("string::replace", utils::string::replace); + gsc::function::add("string::is_numeric", utils::string::is_numeric); + gsc::function::add("string::starts_with", utils::string::starts_with); + gsc::function::add("string::ends_with", utils::string::ends_with); + gsc::function::add("string::replace", utils::string::replace); - gsc::function::add("string::regex_replace", [](const std::string& str, const std::regex& expr, - const std::string& with) - { - return std::regex_replace(str, expr, with); - }); + gsc::function::add("string::regex_replace", [](const std::string& str, const std::regex& expr, + const std::string& with) + { + return std::regex_replace(str, expr, with); + }); - gsc::function::add("string::regex_match", [](const std::string& str, const std::regex& expr) - { - scripting::array array_match{}; - std::smatch match{}; + gsc::function::add("string::regex_match", [](const std::string& str, const std::regex& expr) + { + scripting::array array_match{}; + std::smatch match{}; - if (std::regex_match(str, match, std::regex(expr))) - { - for (const auto& s : match) - { - array_match.push((s.str())); - } - } + if (std::regex_match(str, match, std::regex(expr))) + { + for (const auto& s : match) + { + array_match.push((s.str())); + } + } - return array_match; - }); + return array_match; + }); - gsc::function::add("string::regex_search", [](const std::string& str, const std::regex& expr) - { - return std::regex_search(str, expr); - }); - } - }; + gsc::function::add("string::regex_search", [](const std::string& str, const std::regex& expr) + { + return std::regex_search(str, expr); + }); + } + }; } REGISTER_COMPONENT(string::component) diff --git a/src/component/user_info.cpp b/src/component/user_info.cpp new file mode 100644 index 0000000..6d7b01a --- /dev/null +++ b/src/component/user_info.cpp @@ -0,0 +1,143 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include "gsc.hpp" + +#include +#include + +namespace user_info +{ + namespace + { + using user_info_map = std::unordered_map; + std::unordered_map user_info_overrides; + + utils::hook::detour scr_shutdown_system_hook; + + void clear_client_overrides(const int client_num) + { + user_info_overrides[client_num].clear(); + } + + void clear_all_overrides() + { + user_info_overrides.clear(); + } + + void client_disconnect_stub(const int client_num) + { + clear_client_overrides(client_num); + game::ClientDisconnect(client_num); + } + + void scr_shutdown_system_stub(const game::scriptInstance_t inst, const unsigned char sys, const int b_complete) + { + clear_all_overrides(); + scr_shutdown_system_hook.invoke(inst, sys, b_complete); + } + + void sv_get_user_info_stub(const int index, char* buffer, const int buffer_size) + { + game::SV_GetUserinfo(index, buffer, buffer_size); + + utils::info_string map(buffer); + + if (!user_info_overrides.contains(index)) + { + user_info_overrides[index] = {}; + } + + for (const auto& [key, val] : user_info_overrides[index]) + { + if (val.empty()) + { + map.remove(key); + } + else + { + map.set(key, val); + } + } + + const auto user_info = map.build(); + strncpy_s(buffer, buffer_size, user_info.data(), _TRUNCATE); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + utils::hook::call(SELECT_VALUE(0x5D38EB, 0x4A75E2), sv_get_user_info_stub); + utils::hook::call(SELECT_VALUE(0x67FFE9, 0x548DB0), sv_get_user_info_stub); + + utils::hook::call(SELECT_VALUE(0x4F3931, 0x5DC953), client_disconnect_stub); + + scr_shutdown_system_hook.create(SELECT_VALUE(0x596D40, 0x540780), scr_shutdown_system_stub); + + gsc::method::add_multiple([](const scripting::entity& player, const std::string& name) -> void + { + const auto entref = player.get_entity_reference(); + if (entref.classnum) + { + throw std::runtime_error("Not a player entity"); + } + + if (name.empty()) + { + throw std::runtime_error("set_name: Illegal parameter!"); + } + + user_info_overrides[entref.entnum]["name"] = name; + game::ClientUserinfoChanged(entref.entnum); + }, "user_info::set_name", "set_name"); + + gsc::method::add_multiple([](const scripting::entity& player) -> void + { + const auto entref = player.get_entity_reference(); + if (entref.classnum) + { + throw std::runtime_error("Not a player entity"); + } + + user_info_overrides[entref.entnum].erase("name"); + game::ClientUserinfoChanged(entref.entnum); + }, "user_info::reset_name", "reset_name"); + + gsc::method::add_multiple([](const scripting::entity& player, const std::string& tag) -> void + { + const auto entref = player.get_entity_reference(); + if (entref.classnum) + { + throw std::runtime_error("Not a player entity"); + } + + if (tag.empty()) + { + throw std::runtime_error("set_clantag: Illegal parameter!"); + } + + user_info_overrides[entref.entnum]["clanAbbrev"] = tag; + game::ClientUserinfoChanged(entref.entnum); + }, "user_info::set_clantag", "set_clantag"); + + gsc::method::add_multiple([](const scripting::entity& player) -> void + { + const auto entref = player.get_entity_reference(); + if (entref.classnum) + { + throw std::runtime_error("Not a player entity"); + } + + user_info_overrides[entref.entnum].erase("clanAbbrev"); + game::ClientUserinfoChanged(entref.entnum); + }, "user_info::reset_clantag", "reset_clantag"); + } + }; +} + +REGISTER_COMPONENT(user_info::component) diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index f13c6a3..822083c 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -17,8 +17,6 @@ namespace game WEAK symbol Cmd_Argv{0x0, 0x0}; WEAK symbol Cmd_RemoveCommand{0x5F1A90, 0x527EA0}; - WEAK symbol ClientUserInfoChanged{0x0, 0x0}; - WEAK symbol Com_Error{0x651D90, 0x627380}; WEAK symbol Com_Printf{0x43BF30, 0x4126C0}; WEAK symbol Com_Printf_NoFilter{0x566BC0, 0x64C260}; @@ -107,10 +105,14 @@ namespace game WEAK symbol VM_Execute{0x8ACE60, 0x8EADE0}; + WEAK symbol SV_GetUserinfo{0x50DCD0, 0x6916A0}; WEAK symbol SV_GameDropClient{0x0, 0x0}; WEAK symbol SV_IsTestClient{0x0, 0x0}; WEAK symbol SV_GameSendServerCommand{0x543CF0, 0x6B8730}; + WEAK symbol ClientDisconnect{0x4F4000, 0x66FA00}; + WEAK symbol ClientUserinfoChanged{0x67FFC0, 0x548D80}; + WEAK symbol Sys_GetValue{0x67D4F0, 0x529EB0}; WEAK symbol Sys_Milliseconds{0x0, 0x0}; diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 3e4464f..ba4f10f 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -11,24 +11,25 @@ #define WIN32_LEAN_AND_MEAN #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include + +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef max #undef max diff --git a/src/utils/http.cpp b/src/utils/http.cpp index 2daea66..ab566b1 100644 --- a/src/utils/http.cpp +++ b/src/utils/http.cpp @@ -101,12 +101,5 @@ namespace utils::http return result; } - - if (helper.exception) - { - std::rethrow_exception(helper.exception); - } - - return {}; } } \ No newline at end of file diff --git a/src/utils/info_string.cpp b/src/utils/info_string.cpp new file mode 100644 index 0000000..836ef07 --- /dev/null +++ b/src/utils/info_string.cpp @@ -0,0 +1,67 @@ +#include + +#include "info_string.hpp" +#include "string.hpp" + +namespace utils +{ + info_string::info_string(const std::string& buffer) + { + this->parse(buffer); + } + + void info_string::set(const std::string& key, const std::string& value) + { + this->key_value_pairs_[key] = value; + } + + void info_string::remove(const std::string& key) + { + this->key_value_pairs_.erase(key); + } + + std::string info_string::get(const std::string& key) const + { + const auto value = this->key_value_pairs_.find(key); + if (value != this->key_value_pairs_.end()) + { + return value->second; + } + + return {}; + } + + void info_string::parse(std::string buffer) + { + if (buffer[0] == '\\') + { + buffer = buffer.substr(1); + } + + const auto key_values = string::split(buffer, '\\'); + for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1); i += 2) + { + const auto& key = key_values[i]; + const auto& value = key_values[i + 1]; + + if (!this->key_value_pairs_.contains(key)) + { + this->key_value_pairs_[key] = value; + } + } + } + + std::string info_string::build() const + { + std::string info_string; + for (const auto& [key, val] : this->key_value_pairs_) + { + info_string.append("\\"); + info_string.append(key); + info_string.append("\\"); + info_string.append(val); + } + + return info_string; + } +} diff --git a/src/utils/info_string.hpp b/src/utils/info_string.hpp new file mode 100644 index 0000000..c1f09dd --- /dev/null +++ b/src/utils/info_string.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace utils +{ + class info_string + { + public: + info_string() = default; + info_string(const std::string& buffer); + + void set(const std::string& key, const std::string& value); + void remove(const std::string& key); + + [[nodiscard]] std::string get(const std::string& key) const; + [[nodiscard]] std::string build() const; + + private: + std::unordered_map key_value_pairs_{}; + + void parse(std::string buffer); + }; +} diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 8e1f53b..b40641e 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -35,20 +35,20 @@ namespace utils::string std::string to_lower(std::string text) { - std::transform(text.begin(), text.end(), text.begin(), [](const char input) - { + std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + { return static_cast(tolower(input)); - }); + }); return text; } std::string to_upper(std::string text) { - std::transform(text.begin(), text.end(), text.begin(), [](const char input) - { + std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + { return static_cast(toupper(input)); - }); + }); return text; } @@ -86,34 +86,23 @@ namespace utils::string return result; } - void strip(const char* in, char* out, int max) + std::string replace(std::string str, const std::string& from, const std::string& to) { - if (!in || !out) return; - - max--; - auto current = 0; - while (*in != 0 && current < max) + if (from.empty()) { - const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48); - - if (*in == '^' && (color_index != 7 || *(in + 1) == '7')) - { - ++in; - } - else - { - *out = *in; - ++out; - ++current; - } - - ++in; + return str; } - *out = '\0'; + + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + + return str; } -#pragma warning(push) -#pragma warning(disable: 4100) std::string convert(const std::wstring& wstr) { std::string result; @@ -139,24 +128,6 @@ namespace utils::string return result; } -#pragma warning(pop) - - std::string replace(std::string str, const std::string& from, const std::string& to) - { - if (from.empty()) - { - return str; - } - - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) - { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } - - return str; - } std::string get_timestamp() { diff --git a/src/utils/string.hpp b/src/utils/string.hpp index baa8da8..bcc743c 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -90,12 +90,10 @@ namespace utils::string std::string dump_hex(const std::string& data, const std::string& separator = " "); - void strip(const char* in, char* out, int max); + std::string replace(std::string str, const std::string& from, const std::string& to); std::string convert(const std::wstring& wstr); std::wstring convert(const std::string& str); - std::string replace(std::string str, const std::string& from, const std::string& to); - std::string get_timestamp(); } \ No newline at end of file diff --git a/tools/premake5.exe b/tools/premake5.exe index c73da1f..1a637aa 100644 Binary files a/tools/premake5.exe and b/tools/premake5.exe differ