From 3020770cb346cb8f68f9f0f698799e8eecb6980e Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Feb 2025 14:44:37 -0600 Subject: [PATCH 1/5] fix bad params crashing --- src/component/command.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/component/command.cpp b/src/component/command.cpp index fbde68e..76fd338 100644 --- a/src/component/command.cpp +++ b/src/component/command.cpp @@ -74,6 +74,11 @@ namespace command void client_command_stub(const int client_num) { params_sv params = {}; + if (params.size() <= 1 || params[1] == "") + { + client_command_hook.invoke(client_num); + return; + } const auto command = utils::string::to_lower(params[0]); if ((command == "say" || command == "say_team") && From 27ae961fd851a35499cfaf702e7cbb4076fbd624 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Feb 2025 14:44:43 -0600 Subject: [PATCH 2/5] plutonium sdk support --- .gitmodules | 3 + deps/premake/plutonium-sdk.lua | 18 +++ src/component/command.cpp | 2 +- src/component/exception.cpp | 4 +- src/component/gsc.cpp | 2 +- src/component/http.cpp | 2 +- src/component/io.cpp | 7 +- src/component/json.cpp | 2 +- src/component/scheduler.cpp | 4 +- src/component/scripting.cpp | 2 +- src/component/string.cpp | 2 +- src/component/user_info.cpp | 2 +- src/dllmain.cpp | 26 +-- src/loader/component_interface.hpp | 12 +- src/loader/component_loader.cpp | 129 +++++---------- src/loader/component_loader.hpp | 22 ++- src/plugin.cpp | 75 +++++++++ src/plugin.hpp | 30 ++++ src/utils/nt.cpp | 250 +++++++++++++++++++++++++++++ src/utils/nt.hpp | 112 +++++++++++++ src/utils/string.cpp | 14 ++ src/utils/string.hpp | 4 +- 22 files changed, 581 insertions(+), 143 deletions(-) create mode 100644 deps/premake/plutonium-sdk.lua create mode 100644 src/plugin.cpp create mode 100644 src/plugin.hpp create mode 100644 src/utils/nt.cpp create mode 100644 src/utils/nt.hpp diff --git a/.gitmodules b/.gitmodules index 466a220..1112a30 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "deps/curl"] path = deps/curl url = https://github.com/curl/curl.git +[submodule "deps/plutonium-sdk"] + path = deps/plutonium-sdk + url = https://github.com/plutoniummod/plutonium-sdk.git diff --git a/deps/premake/plutonium-sdk.lua b/deps/premake/plutonium-sdk.lua new file mode 100644 index 0000000..22d8ab7 --- /dev/null +++ b/deps/premake/plutonium-sdk.lua @@ -0,0 +1,18 @@ +plutonium_sdk = { + source = path.join(dependencies.basePath, "plutonium-sdk"), +} + +function plutonium_sdk.import() + plutonium_sdk.includes() +end + +function plutonium_sdk.includes() + includedirs { + plutonium_sdk.source, + } +end + +function plutonium_sdk.project() +end + +table.insert(dependencies, plutonium_sdk) diff --git a/src/component/command.cpp b/src/component/command.cpp index 76fd338..c87f9f9 100644 --- a/src/component/command.cpp +++ b/src/component/command.cpp @@ -267,7 +267,7 @@ namespace command class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { scripting::on_shutdown(clear); client_command_hook.create(SELECT_VALUE(0x4AF770, 0x63DB70), client_command_stub); diff --git a/src/component/exception.cpp b/src/component/exception.cpp index c6acf94..ab69d07 100644 --- a/src/component/exception.cpp +++ b/src/component/exception.cpp @@ -134,7 +134,7 @@ namespace exception class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { #ifdef DEBUG SetUnhandledExceptionFilter(exception_filter); @@ -144,4 +144,4 @@ namespace exception }; } -REGISTER_COMPONENT(exception::component) +//REGISTER_COMPONENT(exception::component) diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index 116c2ad..d344fbb 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -180,7 +180,7 @@ namespace gsc class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { // Don't com_error on gsc errors utils::hook::nop(SELECT_VALUE(0x5A17E1, 0x4D9BB1), 5); diff --git a/src/component/http.cpp b/src/component/http.cpp index a22b6d7..c2e4615 100644 --- a/src/component/http.cpp +++ b/src/component/http.cpp @@ -19,7 +19,7 @@ namespace http class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { scripting::on_shutdown([]() { diff --git a/src/component/io.cpp b/src/component/io.cpp index 01c5006..4ca4ae8 100644 --- a/src/component/io.cpp +++ b/src/component/io.cpp @@ -13,10 +13,11 @@ namespace io class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { - const auto fs_basegame = game::Dvar_FindVar("fs_basegame"); - std::filesystem::current_path(fs_basegame->current.string); + // TODO: fix this, or is it because pluto does it already? + //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) diff --git a/src/component/json.cpp b/src/component/json.cpp index 59adf5f..004bd5e 100644 --- a/src/component/json.cpp +++ b/src/component/json.cpp @@ -204,7 +204,7 @@ namespace json class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { gsc::function::add_multiple([](const scripting::variadic_args& va) { diff --git a/src/component/scheduler.cpp b/src/component/scheduler.cpp index 77e4b93..4b68b73 100644 --- a/src/component/scheduler.cpp +++ b/src/component/scheduler.cpp @@ -140,7 +140,7 @@ namespace scheduler class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { thread = std::thread([]() { @@ -154,7 +154,7 @@ namespace scheduler server_frame_hook.create(SELECT_VALUE(0x43E340, 0x46B680), server_frame_stub); } - void pre_destroy() override + void on_shutdown([[maybe_unused]] plugin::plugin* plugin) override { kill_thread = true; diff --git a/src/component/scripting.cpp b/src/component/scripting.cpp index 5f953f8..3c133bd 100644 --- a/src/component/scripting.cpp +++ b/src/component/scripting.cpp @@ -67,7 +67,7 @@ namespace scripting class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { g_shutdown_game_hook.create(SELECT_VALUE(0x607700, 0x540950), g_shutdown_game_stub); sl_get_canonical_string_hook.create(SELECT_VALUE(0x5F3F40, 0x622530), sl_transfer_canonical_string_stub); diff --git a/src/component/string.cpp b/src/component/string.cpp index ee3a084..63cd927 100644 --- a/src/component/string.cpp +++ b/src/component/string.cpp @@ -160,7 +160,7 @@ namespace string class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) override { gsc::function::add_multiple(format_string, "va", "string::va", "formatstring", "string::format", "sprintf"); diff --git a/src/component/user_info.cpp b/src/component/user_info.cpp index 6d7b01a..abfd107 100644 --- a/src/component/user_info.cpp +++ b/src/component/user_info.cpp @@ -70,7 +70,7 @@ namespace user_info class component final : public component_interface { public: - void post_unpack() override + void on_startup([[maybe_unused]] plugin::plugin* plugin) 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); diff --git a/src/dllmain.cpp b/src/dllmain.cpp index e086911..92c6b99 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -1,38 +1,28 @@ #include #include "loader/component_loader.hpp" +#include "plugin.hpp" + #include "game/game.hpp" #include +#include -namespace +PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize() { - void printf_stub(const char* fmt, ...) - { - char buffer[2048]{}; - - va_list ap; - va_start(ap, fmt); - - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); - - va_end(ap); - - game::Com_Printf(0, "%s", buffer); - } + return plugin::get(); } -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) { - utils::hook::jump(reinterpret_cast(&printf), printf_stub); - component_loader::post_unpack(); + utils::nt::library::set_current_handle(module); } if (ul_reason_for_call == DLL_PROCESS_DETACH) { - component_loader::pre_destroy(); + component_loader::on_shutdown(); } return TRUE; diff --git a/src/loader/component_interface.hpp b/src/loader/component_interface.hpp index e1ee433..2f5b7c6 100644 --- a/src/loader/component_interface.hpp +++ b/src/loader/component_interface.hpp @@ -1,4 +1,5 @@ #pragma once +#include "../plugin.hpp" class component_interface { @@ -7,25 +8,24 @@ public: { } - virtual void post_start() + virtual void on_startup([[maybe_unused]] plugin::plugin* plugin) { } - virtual void post_load() + virtual void on_dvar_init([[maybe_unused]] plugin::plugin* plugin) { } - virtual void pre_destroy() + virtual void on_after_dvar_init([[maybe_unused]] plugin::plugin* plugin) { } - virtual void post_unpack() + virtual void on_game_init([[maybe_unused]] plugin::plugin* plugin) { } - virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function) + virtual void on_shutdown([[maybe_unused]] plugin::plugin* plugin) { - return nullptr; } virtual bool is_supported() diff --git a/src/loader/component_loader.cpp b/src/loader/component_loader.cpp index d6a0090..0b4c7ce 100644 --- a/src/loader/component_loader.cpp +++ b/src/loader/component_loader.cpp @@ -1,87 +1,48 @@ #include #include "component_loader.hpp" -void component_loader::register_component(std::unique_ptr&& component_) +void component_loader::register_component(const std::string& name, std::unique_ptr&& component_) { - get_components().push_back(std::move(component_)); + get_components().push_back(std::make_pair(name, std::move(component_))); } -bool component_loader::post_start() -{ - static auto handled = false; - if (handled) return true; - handled = true; +#define ON_CALLBACK(__name__) \ + void component_loader::__name__() \ + { \ + static auto handled = false; \ + if (handled) \ + { \ + return; \ + } \ + \ + handled = true; \ + \ + for (const auto& component_ : get_components()) \ + { \ + try \ + { \ + component_.second->__name__(plugin::get()); \ + } \ + catch (const std::exception& e) \ + { \ + printf("error executing component \"%s\" callback \"%s\": %s\n", component_.first.data(), #__name__, e.what()); \ + } \ + } \ + } \ - try - { - for (const auto& component_ : get_components()) - { - component_->post_start(); - } - } - catch (premature_shutdown_trigger&) - { - return false; - } - - return true; -} - -bool component_loader::post_load() -{ - static auto handled = false; - if (handled) return true; - handled = true; - - clean(); - - try - { - for (const auto& component_ : get_components()) - { - component_->post_load(); - } - } - catch (premature_shutdown_trigger&) - { - return false; - } - - return true; -} - -void component_loader::post_unpack() -{ - static auto handled = false; - if (handled) return; - handled = true; - - for (const auto& component_ : get_components()) - { - component_->post_unpack(); - } -} - -void component_loader::pre_destroy() -{ - static auto handled = false; - if (handled) return; - handled = true; - - for (const auto& component_ : get_components()) - { - component_->pre_destroy(); - } -} +ON_CALLBACK(on_startup); +ON_CALLBACK(on_dvar_init); +ON_CALLBACK(on_after_dvar_init); +ON_CALLBACK(on_shutdown); void component_loader::clean() { auto& components = get_components(); for (auto i = components.begin(); i != components.end();) { - if (!(*i)->is_supported()) + if (!(*i).second->is_supported()) { - (*i)->pre_destroy(); + (*i).second->on_shutdown(plugin::get()); i = components.erase(i); } else @@ -91,37 +52,21 @@ void component_loader::clean() } } -void* component_loader::load_import(const std::string& library, const std::string& function) -{ - void* function_ptr = nullptr; - - for (const auto& component_ : get_components()) - { - auto* const component_function_ptr = component_->load_import(library, function); - if (component_function_ptr) - { - function_ptr = component_function_ptr; - } - } - - return function_ptr; -} - void component_loader::trigger_premature_shutdown() { throw premature_shutdown_trigger(); } -std::vector>& component_loader::get_components() +std::vector>>& component_loader::get_components() { - using component_vector = std::vector>; + using component_vector = std::vector>>; using component_vector_container = std::unique_ptr>; static component_vector_container components(new component_vector, [](component_vector* component_vector) - { - pre_destroy(); - delete component_vector; - }); + { + on_shutdown(); + delete component_vector; + }); return *components; } diff --git a/src/loader/component_loader.hpp b/src/loader/component_loader.hpp index 1f6b4d1..0444283 100644 --- a/src/loader/component_loader.hpp +++ b/src/loader/component_loader.hpp @@ -18,9 +18,9 @@ public: static_assert(std::is_base_of::value, "component has invalid base class"); public: - installer() + installer(const std::string& name) { - register_component(std::make_unique()); + register_component(name, std::make_unique()); } }; @@ -38,24 +38,22 @@ public: return nullptr; } - static void register_component(std::unique_ptr&& component); + static void register_component(const std::string& name, std::unique_ptr&& component); - static bool post_start(); - static bool post_load(); - static void post_unpack(); - static void pre_destroy(); + static void on_startup(); + static void on_dvar_init(); + static void on_after_dvar_init(); + static void on_shutdown(); static void clean(); - static void* load_import(const std::string& library, const std::string& function); - static void trigger_premature_shutdown(); private: - static std::vector>& get_components(); + static std::vector>>& get_components(); }; -#define REGISTER_COMPONENT(name) \ +#define REGISTER_COMPONENT(name) \ namespace \ { \ - static component_loader::installer __component; \ + static component_loader::installer __component(#name); \ } diff --git a/src/plugin.cpp b/src/plugin.cpp new file mode 100644 index 0000000..790977d --- /dev/null +++ b/src/plugin.cpp @@ -0,0 +1,75 @@ +#include +#include "loader/component_loader.hpp" + +#include "plugin.hpp" + +#include +#include + +namespace plugin +{ + namespace + { + void printf_stub(const char* fmt, ...) + { + char buffer[0x2000] = {}; + + va_list ap; + va_start(ap, fmt); + + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); + + va_end(ap); + + const auto trimmed = utils::string::trim(buffer); + get()->get_interface()->logging()->info(trimmed); + } + } + + std::uint32_t plugin::plugin_version() + { + return 1; + } + + const char* plugin::plugin_name() + { + return "t5-gsc-utils"; + } + + bool plugin::is_game_supported([[maybe_unused]] plutonium::sdk::game game) + { + return game == plutonium::sdk::game::t5; + } + + void plugin::on_startup(plutonium::sdk::iinterface* interface_ptr, plutonium::sdk::game game) + { + this->interface_ = interface_ptr; + this->game_ = game; + //utils::hook::jump(reinterpret_cast(&printf), printf_stub); + + component_loader::on_startup(); + //interface_ptr->callbacks()->on_dvar_init(&component_loader::on_dvar_init); + //interface_ptr->callbacks()->on_after_dvar_init(&component_loader::on_after_dvar_init); + } + + void plugin::on_shutdown() + { + component_loader::on_shutdown(); + } + + plutonium::sdk::iinterface* plugin::get_interface() + { + return this->interface_; + } + + plutonium::sdk::game plugin::get_game() + { + return this->game_; + } + + plugin* get() + { + static plugin instance; + return &instance; + } +} diff --git a/src/plugin.hpp b/src/plugin.hpp new file mode 100644 index 0000000..3d18d09 --- /dev/null +++ b/src/plugin.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace plugin +{ + class plugin : public plutonium::sdk::plugin + { + public: + ~plugin() = default; + + std::uint32_t plugin_version() override; + const char* plugin_name() override; + + bool is_game_supported([[maybe_unused]] plutonium::sdk::game game) override; + + void on_startup(plutonium::sdk::iinterface* interface_ptr, plutonium::sdk::game game) override; + void on_shutdown() override; + + plutonium::sdk::iinterface* get_interface(); + plutonium::sdk::game get_game(); + + private: + plutonium::sdk::iinterface* interface_{}; + plutonium::sdk::game game_{}; + + }; + + plugin* get(); +} diff --git a/src/utils/nt.cpp b/src/utils/nt.cpp new file mode 100644 index 0000000..ebcd6b8 --- /dev/null +++ b/src/utils/nt.cpp @@ -0,0 +1,250 @@ +#include +#include "nt.hpp" +#include "string.hpp" + +namespace utils::nt +{ + HMODULE library::current_handle_; + + library library::load(const std::string& name) + { + return library(LoadLibraryA(name.data())); + } + + library library::load(const std::filesystem::path& path) + { + return library::load(path.generic_string()); + } + + library library::get_by_address(void* address) + { + HMODULE handle = nullptr; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast(address), &handle); + return library(handle); + } + + void library::set_current_handle(HMODULE handle) + { + current_handle_ = handle; + } + + HMODULE library::get_current_handle() + { + return current_handle_; + } + + library::library() + { + this->module_ = GetModuleHandleA(nullptr); + } + + library::library(const std::string& name) + { + this->module_ = GetModuleHandleA(name.data()); + } + + library::library(const HMODULE handle) + { + this->module_ = handle; + } + + bool library::operator==(const library& obj) const + { + return this->module_ == obj.module_; + } + + library::operator bool() const + { + return this->is_valid(); + } + + library::operator HMODULE() const + { + return this->get_handle(); + } + + PIMAGE_NT_HEADERS library::get_nt_headers() const + { + if (!this->is_valid()) return nullptr; + return reinterpret_cast(this->get_ptr() + this->get_dos_header()->e_lfanew); + } + + PIMAGE_DOS_HEADER library::get_dos_header() const + { + return reinterpret_cast(this->get_ptr()); + } + + PIMAGE_OPTIONAL_HEADER library::get_optional_header() const + { + if (!this->is_valid()) return nullptr; + return &this->get_nt_headers()->OptionalHeader; + } + + std::vector library::get_section_headers() const + { + std::vector headers; + + auto nt_headers = this->get_nt_headers(); + auto section = IMAGE_FIRST_SECTION(nt_headers); + + for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) + { + if (section) headers.push_back(section); + else OutputDebugStringA("There was an invalid section :O"); + } + + return headers; + } + + std::uint8_t* library::get_ptr() const + { + return reinterpret_cast(this->module_); + } + + void library::unprotect() const + { + if (!this->is_valid()) return; + + DWORD protection; + VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, + &protection); + } + + size_t library::get_relative_entry_point() const + { + if (!this->is_valid()) return 0; + return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint; + } + + void* library::get_entry_point() const + { + if (!this->is_valid()) return nullptr; + return this->get_ptr() + this->get_relative_entry_point(); + } + + bool library::is_valid() const + { + return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE; + } + + std::string library::get_name() const + { + if (!this->is_valid()) return ""; + + auto path = this->get_path(); + const auto pos = path.find_last_of("/\\"); + if (pos == std::string::npos) return path; + + return path.substr(pos + 1); + } + + std::string library::get_path() const + { + if (!this->is_valid()) return ""; + + char name[MAX_PATH] = {0}; + GetModuleFileNameA(this->module_, name, sizeof name); + + return name; + } + + std::string library::get_folder() const + { + if (!this->is_valid()) return ""; + + const auto path = std::filesystem::path(this->get_path()); + return path.parent_path().generic_string(); + } + + void library::free() + { + if (this->is_valid()) + { + FreeLibrary(this->module_); + this->module_ = nullptr; + } + } + + HMODULE library::get_handle() const + { + return this->module_; + } + + void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const + { + if (!this->is_valid()) return nullptr; + + const library other_module(module_name); + if (!other_module.is_valid()) return nullptr; + + auto* const target_function = other_module.get_proc(proc_name); + if (!target_function) return nullptr; + + auto* header = this->get_optional_header(); + if (!header) return nullptr; + + auto* import_descriptor = reinterpret_cast(this->get_ptr() + header->DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + while (import_descriptor->Name) + { + if (!_stricmp(reinterpret_cast(this->get_ptr() + import_descriptor->Name), module_name.data())) + { + auto* original_thunk_data = reinterpret_cast(import_descriptor-> + OriginalFirstThunk + this->get_ptr()); + auto* thunk_data = reinterpret_cast(import_descriptor->FirstThunk + this-> + get_ptr()); + + while (original_thunk_data->u1.AddressOfData) + { + const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF; + + if (ordinal_number > 0xFFFF) continue; + + if (GetProcAddress(other_module.module_, reinterpret_cast(ordinal_number)) == + target_function) + { + return reinterpret_cast(&thunk_data->u1.Function); + } + + ++original_thunk_data; + ++thunk_data; + } + + //break; + } + + ++import_descriptor; + } + + return nullptr; + } + + void raise_hard_exception() + { + int data = false; + const library ntdll("ntdll.dll"); + ntdll.invoke_pascal("RtlAdjustPrivilege", 19, true, false, &data); + ntdll.invoke_pascal("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data); + } + + std::string load_resource(const int id) + { + const auto self_handle = library::get_current_handle(); + const auto res = FindResource(self_handle, MAKEINTRESOURCE(id), RT_RCDATA); + if (res == nullptr) + { + return {}; + } + + const auto handle = LoadResource(self_handle, res); + if (handle == nullptr) + { + return {}; + } + + const auto str = LPSTR(LockResource(handle)); + const auto size = SizeofResource(self_handle, res); + return std::string{str, size}; + } +} diff --git a/src/utils/nt.hpp b/src/utils/nt.hpp new file mode 100644 index 0000000..4ba32c7 --- /dev/null +++ b/src/utils/nt.hpp @@ -0,0 +1,112 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +// min and max is required by gdi, therefore NOMINMAX won't work +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#include +#include +#include + +namespace utils::nt +{ + class library final + { + public: + static library load(const std::string& name); + static library load(const std::filesystem::path& path); + static library get_by_address(void* address); + + static void set_current_handle(HMODULE handle); + static HMODULE get_current_handle(); + + library(); + explicit library(const std::string& name); + explicit library(HMODULE handle); + + library(const library& a) : module_(a.module_) + { + } + + bool operator!=(const library& obj) const { return !(*this == obj); }; + bool operator==(const library& obj) const; + + operator bool() const; + operator HMODULE() const; + + void unprotect() const; + void* get_entry_point() const; + size_t get_relative_entry_point() const; + + bool is_valid() const; + std::string get_name() const; + std::string get_path() const; + std::string get_folder() const; + std::uint8_t* get_ptr() const; + void free(); + + HMODULE get_handle() const; + + template + T get_proc(const std::string& process) const + { + if (!this->is_valid()) T{}; + return reinterpret_cast(GetProcAddress(this->module_, process.data())); + } + + template + std::function get(const std::string& process) const + { + if (!this->is_valid()) return std::function(); + return static_cast(this->get_proc(process)); + } + + template + T invoke(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_pascal(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_this(const std::string& process, void* this_ptr, Args ... args) const + { + auto method = this->get(this_ptr, process); + if (method) return method(args...); + return T(); + } + + std::vector get_section_headers() const; + + PIMAGE_NT_HEADERS get_nt_headers() const; + PIMAGE_DOS_HEADER get_dos_header() const; + PIMAGE_OPTIONAL_HEADER get_optional_header() const; + + void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const; + + private: + HMODULE module_; + static HMODULE current_handle_; + + }; + + __declspec(noreturn) void raise_hard_exception(); + std::string load_resource(int id); +} diff --git a/src/utils/string.cpp b/src/utils/string.cpp index b40641e..b75990d 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -140,4 +140,18 @@ namespace utils::string return timestamp; } + + std::string trim(const std::string& str, const std::string& whitespace) + { + const auto first = str.find_first_not_of(whitespace); + if (first == std::string::npos) + { + return {}; + } + + const auto last = str.find_last_not_of(whitespace); + const auto range = last - first + 1; + + return str.substr(first, range); + } } \ No newline at end of file diff --git a/src/utils/string.hpp b/src/utils/string.hpp index bcc743c..d0cb4dc 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -96,4 +96,6 @@ namespace utils::string std::wstring convert(const std::string& str); std::string get_timestamp(); -} \ No newline at end of file + + std::string trim(const std::string& str, const std::string& whitespace = " \t\n\r\f\v"); +} From 936c3a5548cde3f2e5a238cee2b650ecfcc479c1 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Feb 2025 14:47:36 -0600 Subject: [PATCH 3/5] sdk dep --- deps/plutonium-sdk | 1 + 1 file changed, 1 insertion(+) create mode 160000 deps/plutonium-sdk diff --git a/deps/plutonium-sdk b/deps/plutonium-sdk new file mode 160000 index 0000000..17e9a0a --- /dev/null +++ b/deps/plutonium-sdk @@ -0,0 +1 @@ +Subproject commit 17e9a0a4d5e1133b50f879e3db07e97d1bf92e10 From 9adb001d081e40250c3957a1f5039d3a3a625d6a Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Feb 2025 14:53:10 -0600 Subject: [PATCH 4/5] better params check --- src/component/command.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/command.cpp b/src/component/command.cpp index c87f9f9..0c64fd7 100644 --- a/src/component/command.cpp +++ b/src/component/command.cpp @@ -74,14 +74,14 @@ namespace command void client_command_stub(const int client_num) { params_sv params = {}; - if (params.size() <= 1 || params[1] == "") + if (params.size() < 1) { client_command_hook.invoke(client_num); return; } const auto command = utils::string::to_lower(params[0]); - if ((command == "say" || command == "say_team") && + if (params.size() > 1 && (command == "say" || command == "say_team") && handle_chat_command(client_num, params)) { return; From 7c0c9b0c3664a9a6677debe856e66ed451618189 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Feb 2025 14:58:02 -0600 Subject: [PATCH 5/5] fix action? --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e60acb2..6b89eb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,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@v3.1.3 + uses: actions/upload-artifact@v4 with: name: ${{matrix.configuration}} binaries path: |