mirror of
https://github.com/alicealys/t5-gsc-utils.git
synced 2025-04-19 04:22:54 +00:00
commit
589c5b5a48
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:PlatformTarget=x86 build/t5-gsc-utils.sln
|
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:PlatformTarget=x86 build/t5-gsc-utils.sln
|
||||||
|
|
||||||
- name: Upload ${{matrix.configuration}} binaries
|
- name: Upload ${{matrix.configuration}} binaries
|
||||||
uses: actions/upload-artifact@v3.1.3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{matrix.configuration}} binaries
|
name: ${{matrix.configuration}} binaries
|
||||||
path: |
|
path: |
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -28,3 +28,6 @@
|
|||||||
[submodule "deps/curl"]
|
[submodule "deps/curl"]
|
||||||
path = deps/curl
|
path = deps/curl
|
||||||
url = https://github.com/curl/curl.git
|
url = https://github.com/curl/curl.git
|
||||||
|
[submodule "deps/plutonium-sdk"]
|
||||||
|
path = deps/plutonium-sdk
|
||||||
|
url = https://github.com/plutoniummod/plutonium-sdk.git
|
||||||
|
1
deps/plutonium-sdk
vendored
Submodule
1
deps/plutonium-sdk
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 17e9a0a4d5e1133b50f879e3db07e97d1bf92e10
|
18
deps/premake/plutonium-sdk.lua
vendored
Normal file
18
deps/premake/plutonium-sdk.lua
vendored
Normal file
@ -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)
|
@ -74,9 +74,14 @@ namespace command
|
|||||||
void client_command_stub(const int client_num)
|
void client_command_stub(const int client_num)
|
||||||
{
|
{
|
||||||
params_sv params = {};
|
params_sv params = {};
|
||||||
|
if (params.size() < 1)
|
||||||
|
{
|
||||||
|
client_command_hook.invoke<void>(client_num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto command = utils::string::to_lower(params[0]);
|
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))
|
handle_chat_command(client_num, params))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -262,7 +267,7 @@ namespace command
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
scripting::on_shutdown(clear);
|
scripting::on_shutdown(clear);
|
||||||
client_command_hook.create(SELECT_VALUE(0x4AF770, 0x63DB70), client_command_stub);
|
client_command_hook.create(SELECT_VALUE(0x4AF770, 0x63DB70), client_command_stub);
|
||||||
|
@ -134,7 +134,7 @@ namespace exception
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
SetUnhandledExceptionFilter(exception_filter);
|
SetUnhandledExceptionFilter(exception_filter);
|
||||||
@ -144,4 +144,4 @@ namespace exception
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_COMPONENT(exception::component)
|
//REGISTER_COMPONENT(exception::component)
|
||||||
|
@ -180,7 +180,7 @@ namespace gsc
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
// Don't com_error on gsc errors
|
// Don't com_error on gsc errors
|
||||||
utils::hook::nop(SELECT_VALUE(0x5A17E1, 0x4D9BB1), 5);
|
utils::hook::nop(SELECT_VALUE(0x5A17E1, 0x4D9BB1), 5);
|
||||||
|
@ -19,7 +19,7 @@ namespace http
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
scripting::on_shutdown([]()
|
scripting::on_shutdown([]()
|
||||||
{
|
{
|
||||||
|
@ -13,10 +13,11 @@ namespace io
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
const auto fs_basegame = game::Dvar_FindVar("fs_basegame");
|
// TODO: fix this, or is it because pluto does it already?
|
||||||
std::filesystem::current_path(fs_basegame->current.string);
|
//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,
|
gsc::function::add_multiple([](const std::string& file, const std::string& data,
|
||||||
const scripting::variadic_args& va)
|
const scripting::variadic_args& va)
|
||||||
|
@ -204,7 +204,7 @@ namespace json
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
gsc::function::add_multiple([](const scripting::variadic_args& va)
|
gsc::function::add_multiple([](const scripting::variadic_args& va)
|
||||||
{
|
{
|
||||||
|
@ -140,7 +140,7 @@ namespace scheduler
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
thread = std::thread([]()
|
thread = std::thread([]()
|
||||||
{
|
{
|
||||||
@ -154,7 +154,7 @@ namespace scheduler
|
|||||||
server_frame_hook.create(SELECT_VALUE(0x43E340, 0x46B680), server_frame_stub);
|
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;
|
kill_thread = true;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ namespace scripting
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
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);
|
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);
|
sl_get_canonical_string_hook.create(SELECT_VALUE(0x5F3F40, 0x622530), sl_transfer_canonical_string_stub);
|
||||||
|
@ -160,7 +160,7 @@ namespace string
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
gsc::function::add_multiple(format_string, "va", "string::va",
|
gsc::function::add_multiple(format_string, "va", "string::va",
|
||||||
"formatstring", "string::format", "sprintf");
|
"formatstring", "string::format", "sprintf");
|
||||||
|
@ -70,7 +70,7 @@ namespace user_info
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
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(0x5D38EB, 0x4A75E2), sv_get_user_info_stub);
|
||||||
utils::hook::call(SELECT_VALUE(0x67FFE9, 0x548DB0), sv_get_user_info_stub);
|
utils::hook::call(SELECT_VALUE(0x67FFE9, 0x548DB0), sv_get_user_info_stub);
|
||||||
|
@ -1,38 +1,28 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/nt.hpp>
|
||||||
|
|
||||||
namespace
|
PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize()
|
||||||
{
|
{
|
||||||
void printf_stub(const char* fmt, ...)
|
return plugin::get();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HMODULE /*module*/, DWORD ul_reason_for_call, LPVOID /*reserved*/)
|
BOOL APIENTRY DllMain(HMODULE module, DWORD ul_reason_for_call, LPVOID /*reserved*/)
|
||||||
{
|
{
|
||||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
{
|
{
|
||||||
utils::hook::jump(reinterpret_cast<size_t>(&printf), printf_stub);
|
utils::nt::library::set_current_handle(module);
|
||||||
component_loader::post_unpack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
||||||
{
|
{
|
||||||
component_loader::pre_destroy();
|
component_loader::on_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "../plugin.hpp"
|
||||||
|
|
||||||
class component_interface
|
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()
|
virtual bool is_supported()
|
||||||
|
@ -1,87 +1,48 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "component_loader.hpp"
|
#include "component_loader.hpp"
|
||||||
|
|
||||||
void component_loader::register_component(std::unique_ptr<component_interface>&& component_)
|
void component_loader::register_component(const std::string& name, std::unique_ptr<component_interface>&& component_)
|
||||||
{
|
{
|
||||||
get_components().push_back(std::move(component_));
|
get_components().push_back(std::make_pair(name, std::move(component_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool component_loader::post_start()
|
#define ON_CALLBACK(__name__) \
|
||||||
{
|
void component_loader::__name__() \
|
||||||
static auto handled = false;
|
{ \
|
||||||
if (handled) return true;
|
static auto handled = false; \
|
||||||
handled = true;
|
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
|
ON_CALLBACK(on_startup);
|
||||||
{
|
ON_CALLBACK(on_dvar_init);
|
||||||
for (const auto& component_ : get_components())
|
ON_CALLBACK(on_after_dvar_init);
|
||||||
{
|
ON_CALLBACK(on_shutdown);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void component_loader::clean()
|
void component_loader::clean()
|
||||||
{
|
{
|
||||||
auto& components = get_components();
|
auto& components = get_components();
|
||||||
for (auto i = components.begin(); i != components.end();)
|
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);
|
i = components.erase(i);
|
||||||
}
|
}
|
||||||
else
|
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()
|
void component_loader::trigger_premature_shutdown()
|
||||||
{
|
{
|
||||||
throw premature_shutdown_trigger();
|
throw premature_shutdown_trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<component_interface>>& component_loader::get_components()
|
std::vector<std::pair<std::string, std::unique_ptr<component_interface>>>& component_loader::get_components()
|
||||||
{
|
{
|
||||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
using component_vector = std::vector<std::pair<std::string, std::unique_ptr<component_interface>>>;
|
||||||
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||||
|
|
||||||
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
||||||
{
|
{
|
||||||
pre_destroy();
|
on_shutdown();
|
||||||
delete component_vector;
|
delete component_vector;
|
||||||
});
|
});
|
||||||
|
|
||||||
return *components;
|
return *components;
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ public:
|
|||||||
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
installer()
|
installer(const std::string& name)
|
||||||
{
|
{
|
||||||
register_component(std::make_unique<T>());
|
register_component(name, std::make_unique<T>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,24 +38,22 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void register_component(std::unique_ptr<component_interface>&& component);
|
static void register_component(const std::string& name, std::unique_ptr<component_interface>&& component);
|
||||||
|
|
||||||
static bool post_start();
|
static void on_startup();
|
||||||
static bool post_load();
|
static void on_dvar_init();
|
||||||
static void post_unpack();
|
static void on_after_dvar_init();
|
||||||
static void pre_destroy();
|
static void on_shutdown();
|
||||||
static void clean();
|
static void clean();
|
||||||
|
|
||||||
static void* load_import(const std::string& library, const std::string& function);
|
|
||||||
|
|
||||||
static void trigger_premature_shutdown();
|
static void trigger_premature_shutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
static std::vector<std::pair<std::string, std::unique_ptr<component_interface>>>& get_components();
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REGISTER_COMPONENT(name) \
|
#define REGISTER_COMPONENT(name) \
|
||||||
namespace \
|
namespace \
|
||||||
{ \
|
{ \
|
||||||
static component_loader::installer<name> __component; \
|
static component_loader::installer<name> __component(#name); \
|
||||||
}
|
}
|
||||||
|
75
src/plugin.cpp
Normal file
75
src/plugin.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
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<uintptr_t>(&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;
|
||||||
|
}
|
||||||
|
}
|
30
src/plugin.hpp
Normal file
30
src/plugin.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <plutonium_sdk.hpp>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
250
src/utils/nt.cpp
Normal file
250
src/utils/nt.cpp
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#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<LPCSTR>(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<PIMAGE_NT_HEADERS>(this->get_ptr() + this->get_dos_header()->e_lfanew);
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_DOS_HEADER library::get_dos_header() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<PIMAGE_DOS_HEADER>(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<PIMAGE_SECTION_HEADER> library::get_section_headers() const
|
||||||
|
{
|
||||||
|
std::vector<PIMAGE_SECTION_HEADER> 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<std::uint8_t*>(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<void*>(proc_name);
|
||||||
|
if (!target_function) return nullptr;
|
||||||
|
|
||||||
|
auto* header = this->get_optional_header();
|
||||||
|
if (!header) return nullptr;
|
||||||
|
|
||||||
|
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory
|
||||||
|
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||||
|
|
||||||
|
while (import_descriptor->Name)
|
||||||
|
{
|
||||||
|
if (!_stricmp(reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name), module_name.data()))
|
||||||
|
{
|
||||||
|
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->
|
||||||
|
OriginalFirstThunk + this->get_ptr());
|
||||||
|
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(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<char*>(ordinal_number)) ==
|
||||||
|
target_function)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<void**>(&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<void>("RtlAdjustPrivilege", 19, true, false, &data);
|
||||||
|
ntdll.invoke_pascal<void>("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};
|
||||||
|
}
|
||||||
|
}
|
112
src/utils/nt.hpp
Normal file
112
src/utils/nt.hpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
// min and max is required by gdi, therefore NOMINMAX won't work
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
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 <typename T>
|
||||||
|
T get_proc(const std::string& process) const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) T{};
|
||||||
|
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::function<T> get(const std::string& process) const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return std::function<T>();
|
||||||
|
return static_cast<T*>(this->get_proc<void*>(process));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke(const std::string& process, Args ... args) const
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__cdecl)(Args ...)>(process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke_pascal(const std::string& process, Args ... args) const
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__stdcall)(Args ...)>(process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke_this(const std::string& process, void* this_ptr, Args ... args) const
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__thiscall)(void*, Args ...)>(this_ptr, process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PIMAGE_SECTION_HEADER> 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);
|
||||||
|
}
|
@ -140,4 +140,18 @@ namespace utils::string
|
|||||||
|
|
||||||
return timestamp;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -96,4 +96,6 @@ namespace utils::string
|
|||||||
std::wstring convert(const std::string& str);
|
std::wstring convert(const std::string& str);
|
||||||
|
|
||||||
std::string get_timestamp();
|
std::string get_timestamp();
|
||||||
|
|
||||||
|
std::string trim(const std::string& str, const std::string& whitespace = " \t\n\r\f\v");
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user