mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-07-03 01:31:53 +00:00
Compare commits
11 Commits
0cd113b33f
...
main
Author | SHA1 | Date | |
---|---|---|---|
bf1632f4ec | |||
4bd1ff9a66 | |||
9b8680b7bd | |||
646b5a3264 | |||
3e9fcc5fda | |||
83ffa1c324 | |||
fd80b4c55a | |||
348bf99bd6 | |||
0c128ca259 | |||
232ce3bcaa | |||
483c7126c8 |
@ -3,15 +3,15 @@ A plugin that has code that hopefully compiles and the game will load it to do t
|
||||
|
||||
Requires Git (https://git-scm.com/), Premake5 (https://premake.github.io/), and MSVC 2022 (https://visualstudio.microsoft.com/vs/features/cplusplus/) to build.
|
||||
|
||||
# Installation
|
||||
Move the `t4sp-server-plugin.dll` to `%LOCALAPPDATA%\Plutonium\plugins\`, the plugin will be loaded when you start up a dedicated server for Plutonium T4SP.
|
||||
|
||||
# Features
|
||||
|
||||
Detours and reimplements the entire GSC VM + compiler.
|
||||
|
||||
Adds custom GSC functions.
|
||||
|
||||
# Installation
|
||||
Move the `t4sp-server-plugin.dll` to `%LOCALAPPDATA%\Plutonium\plugins\`, the plugin will be loaded when you start up a dedicated server for Plutonium T4SP.
|
||||
|
||||
## FileIO
|
||||
This plugin provides FileIO interface to GSC for reading and writing files, this is exact to [CoD4x's](https://github.com/callofduty4x/CoD4x_Server/blob/master/scriptdocumentation/script_functions_reference.md#file-operations) interface.
|
||||
|
||||
|
@ -108,13 +108,13 @@ workspace "t4sp-server-plugin"
|
||||
else
|
||||
filter "configurations:Release"
|
||||
postbuildcommands {
|
||||
"if \"%COMPUTERNAME%\" == \"NEW-BUILT\" ( copy /y \"$(TargetPath)\" \"$(CODWAW_PATH)\\t4\\plugins\\\" )"
|
||||
"if \"%COMPUTERNAME%\" == \"\" ( copy /y \"$(TargetPath)\" \"$(LOCALAPPDATA)\\Plutonium\\plugins\\\" )"
|
||||
}
|
||||
filter {}
|
||||
|
||||
filter "configurations:Debug"
|
||||
postbuildcommands {
|
||||
"if \"%COMPUTERNAME%\" == \"NEW-BUILT\" ( copy /y \"$(TargetPath)\" \"$(CODWAW_PATH)\\t4staging\\plugins\\\" )"
|
||||
"if \"%COMPUTERNAME%\" == \"\" ( copy /y \"$(TargetPath)\" \"$(LOCALAPPDATA)\\Plutonium-staging\\plugins\\\" )"
|
||||
}
|
||||
filter {}
|
||||
end
|
||||
|
@ -2011,14 +2011,6 @@ LABEL_17:
|
||||
|
||||
}
|
||||
|
||||
// our addition
|
||||
auto f = gsc::function::get(pName, type);
|
||||
if (f != nullptr)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
//
|
||||
|
||||
// pluto
|
||||
if (game::plutonium::scr_get_function_hook != nullptr)
|
||||
{
|
||||
@ -2154,14 +2146,6 @@ LABEL_17:
|
||||
}
|
||||
}
|
||||
|
||||
// our addition
|
||||
auto f = gsc::method::get(pName, type);
|
||||
if (f != nullptr)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
//
|
||||
|
||||
// pluto
|
||||
if (game::plutonium::scr_get_method_hook != nullptr)
|
||||
{
|
||||
|
@ -304,7 +304,6 @@ namespace codsrc
|
||||
{
|
||||
game::plutonium::script_preprocess(sourceBuffer, inst, &parseData); // the pluto hook will call ScriptParse, so we dont have to
|
||||
}
|
||||
|
||||
//
|
||||
else
|
||||
{
|
||||
|
@ -723,19 +723,20 @@ namespace codsrc
|
||||
{
|
||||
if (game::Scr_IsInOpcodeMemory(scriptInstance, codepos - 1))
|
||||
{
|
||||
// pluto
|
||||
const char* s;
|
||||
|
||||
// pluto
|
||||
if (game::plutonium::at_codepose_va != nullptr)
|
||||
{
|
||||
s = game::plutonium::at_codepose_va(scriptInstance, codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
||||
}
|
||||
//
|
||||
else
|
||||
{
|
||||
s = game::va("@ %d\n", codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
||||
}
|
||||
|
||||
game::Com_PrintMessage(channel, s, 0);
|
||||
//
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -4702,10 +4702,6 @@ namespace codsrc
|
||||
// Decomp Status: Tested, Completed
|
||||
void Scr_InitSystem(game::scriptInstance_t inst)
|
||||
{
|
||||
// our additions
|
||||
scheduler::exec_pre_scr_init_funcs(inst);
|
||||
//
|
||||
|
||||
assert(!game::gScrVarPub[inst].timeArrayId);
|
||||
|
||||
//assert(!game::gScrVarPub[inst].ext_threadcount);
|
||||
@ -4731,10 +4727,6 @@ namespace codsrc
|
||||
assert(!game::gScrVarPub[inst].freeEntList);
|
||||
|
||||
game::g_script_error_level[inst] = -1;
|
||||
|
||||
// our additions
|
||||
scheduler::exec_post_scr_init_funcs(inst);
|
||||
//
|
||||
}
|
||||
|
||||
//Restored function
|
||||
|
@ -87,7 +87,7 @@ namespace fileio
|
||||
void free_scr_fh(scr_fh_t& scr_fh)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("free_scr_fh: closing %s\n", scr_fh.base_path.c_str());
|
||||
plugin::get()->get_interface()->logging()->info(utils::string::va("free_scr_fh: closing %s\n", scr_fh.base_path.c_str()));
|
||||
#endif
|
||||
|
||||
scr_fh = {};
|
||||
@ -146,13 +146,8 @@ namespace fileio
|
||||
|
||||
void add_file_io()
|
||||
{
|
||||
scheduler::on_pre_scr_init_system([]([[maybe_unused]] game::scriptInstance_t inst)
|
||||
scheduler::on_scr_execute([]()
|
||||
{
|
||||
if (inst != game::SCRIPTINSTANCE_SERVER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
close_all_scr_fh();
|
||||
});
|
||||
|
||||
@ -256,7 +251,7 @@ namespace fileio
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("gscr_fs_fopen: opening %s, mode %s\n", fpath.c_str(), mode);
|
||||
plugin::get()->get_interface()->logging()->info(utils::string::va("gscr_fs_fopen: opening %s, mode %s\n", fpath.c_str(), mode));
|
||||
#endif
|
||||
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, i + 1);
|
||||
});
|
||||
|
@ -1,125 +1,14 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "gsc.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <json.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
std::unordered_map<std::string, game::BuiltinFunction> functions;
|
||||
std::unordered_map<std::string, game::BuiltinMethod> methods;
|
||||
|
||||
utils::hook::detour scr_getmethod_hook;
|
||||
void* scr_getfunction_stub_ret_loc;
|
||||
|
||||
namespace
|
||||
{
|
||||
game::BuiltinFunction scr_getfunction_call(const char** pName, int* pType)
|
||||
{
|
||||
auto itr = functions.find(*pName);
|
||||
|
||||
if (itr == functions.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*pType = 0;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
game::BuiltinFunction NAKED scr_getfunction_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax;
|
||||
pushad;
|
||||
|
||||
lea eax, [esp + 0x24 + 0x2C - 0x1C];
|
||||
push eax;
|
||||
push edx;
|
||||
call scr_getfunction_call;
|
||||
add esp, 8;
|
||||
mov [esp + 0x20], eax;
|
||||
|
||||
popad;
|
||||
pop eax;
|
||||
|
||||
test eax, eax;
|
||||
jnz just_ret;
|
||||
|
||||
// go do original code
|
||||
push scr_getfunction_stub_ret_loc;
|
||||
ret;
|
||||
|
||||
just_ret:
|
||||
add esp, 4;
|
||||
push 0x682DC8;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
game::BuiltinMethod scr_getmethod_call(const char** pName, int* pType)
|
||||
{
|
||||
auto itr = methods.find(*pName);
|
||||
|
||||
if (itr == methods.end())
|
||||
{
|
||||
// call og
|
||||
const auto og_addr = scr_getmethod_hook.get_original();
|
||||
game::BuiltinMethod answer;
|
||||
|
||||
__asm
|
||||
{
|
||||
mov edi, pType;
|
||||
mov esi, pName;
|
||||
call og_addr;
|
||||
mov answer, eax;
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
*pType = 0;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
game::BuiltinMethod NAKED scr_getmethod_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push edi;
|
||||
push esi;
|
||||
call scr_getmethod_call;
|
||||
add esp, 8;
|
||||
|
||||
ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace function
|
||||
{
|
||||
void add(const std::string& name, const game::BuiltinFunction function)
|
||||
{
|
||||
functions.insert_or_assign(name, function);
|
||||
}
|
||||
|
||||
game::BuiltinFunction get(const char** name, int* type)
|
||||
{
|
||||
auto got = functions.find(*name);
|
||||
|
||||
if (got == functions.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*type = 0;
|
||||
return got->second;
|
||||
plugin::get()->get_interface()->gsc()->register_function(name, function);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,46 +16,8 @@ namespace gsc
|
||||
{
|
||||
void add(const std::string& name, const game::BuiltinMethod method)
|
||||
{
|
||||
methods.insert_or_assign(name, method);
|
||||
}
|
||||
|
||||
game::BuiltinMethod get(const char** name, int* type)
|
||||
{
|
||||
auto got = methods.find(*name);
|
||||
|
||||
if (got == methods.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*type = 0;
|
||||
return got->second;
|
||||
plugin::get()->get_interface()->gsc()->register_method(name, (plutonium::sdk::v1::interfaces::gsc::method_callback)method);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// for when we dont use the decomp
|
||||
// custom gsc methods
|
||||
if (game::plutonium::scr_get_method_stub != nullptr)
|
||||
{
|
||||
scr_getmethod_hook.create(game::plutonium::scr_get_method_stub.get(), scr_getmethod_stub);
|
||||
}
|
||||
|
||||
// custom gsc funcs
|
||||
if (game::plutonium::scr_get_function_stub != nullptr)
|
||||
{
|
||||
scr_getfunction_stub_ret_loc = game::plutonium::scr_get_function_stub.get();
|
||||
utils::hook::jump(SELECT(0x0, 0x682D99), scr_getfunction_stub);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(gsc::component)
|
||||
|
||||
|
@ -5,12 +5,10 @@ namespace gsc
|
||||
namespace function
|
||||
{
|
||||
void add(const std::string& name, const game::BuiltinFunction function);
|
||||
game::BuiltinFunction get(const char** name, int* type);
|
||||
}
|
||||
|
||||
namespace method
|
||||
{
|
||||
void add(const std::string& name, const game::BuiltinMethod method);
|
||||
game::BuiltinMethod get(const char** name, int* type);
|
||||
}
|
||||
}
|
@ -1,266 +1,11 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace scheduler
|
||||
{
|
||||
namespace
|
||||
void on_scr_execute(void(*callback)())
|
||||
{
|
||||
struct task
|
||||
{
|
||||
std::function<bool()> handler{};
|
||||
std::chrono::milliseconds interval{};
|
||||
std::chrono::high_resolution_clock::time_point last_call{};
|
||||
};
|
||||
|
||||
using task_list = std::vector<task>;
|
||||
|
||||
class task_pipeline
|
||||
{
|
||||
public:
|
||||
void add(task&& task)
|
||||
{
|
||||
new_callbacks_.access([&task](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
this->merge_callbacks();
|
||||
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->last_call;
|
||||
|
||||
if (diff < i->interval)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_call = now;
|
||||
|
||||
const auto res = i->handler();
|
||||
if (res == cond_end)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
utils::concurrency::container<task_list> new_callbacks_;
|
||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||
|
||||
void merge_callbacks()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
std::thread thread;
|
||||
task_pipeline pipelines[pipeline::count];
|
||||
|
||||
void execute(const pipeline type)
|
||||
{
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
pipelines[type].execute();
|
||||
}
|
||||
|
||||
void execute_server()
|
||||
{
|
||||
execute(pipeline::server);
|
||||
}
|
||||
|
||||
void execute_main()
|
||||
{
|
||||
execute(pipeline::main);
|
||||
}
|
||||
|
||||
utils::hook::detour com_init_hook;
|
||||
utils::hook::detour gscr_postloadscripts_hook;
|
||||
|
||||
std::vector<std::function<void()>> post_init_funcs;
|
||||
bool com_inited = false;
|
||||
|
||||
void on_post_init_hook()
|
||||
{
|
||||
if (com_inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
com_inited = true;
|
||||
for (const auto& func : post_init_funcs)
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
post_init_funcs.clear();
|
||||
}
|
||||
|
||||
void com_init_stub()
|
||||
{
|
||||
com_init_hook.invoke<void>();
|
||||
on_post_init_hook();
|
||||
}
|
||||
|
||||
std::vector<std::function<void(game::scriptInstance_t)>> pre_scr_init_funcs;
|
||||
std::vector<std::function<void(game::scriptInstance_t)>> post_scr_init_funcs;
|
||||
|
||||
utils::hook::detour pre_scr_init_system_hook;
|
||||
utils::hook::detour post_scr_init_system_hook;
|
||||
|
||||
void* pre_scr_init_system_original;
|
||||
void* post_scr_init_system_original;
|
||||
|
||||
NAKED void pre_scr_init_system_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad;
|
||||
push eax;
|
||||
call exec_pre_scr_init_funcs;
|
||||
add esp, 4;
|
||||
popad;
|
||||
|
||||
push pre_scr_init_system_original;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
NAKED void post_scr_init_system_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad;
|
||||
push eax;
|
||||
call exec_post_scr_init_funcs;
|
||||
add esp, 4;
|
||||
popad;
|
||||
|
||||
push post_scr_init_system_original;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
plugin::get()->get_interface()->callbacks()->on_scripts_execute(callback);
|
||||
}
|
||||
|
||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
|
||||
task task;
|
||||
task.handler = callback;
|
||||
task.interval = delay;
|
||||
task.last_call = std::chrono::high_resolution_clock::now();
|
||||
|
||||
pipelines[type].add(std::move(task));
|
||||
}
|
||||
|
||||
void loop(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
{
|
||||
callback();
|
||||
return cond_continue;
|
||||
}, type, delay);
|
||||
}
|
||||
|
||||
void once(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
{
|
||||
callback();
|
||||
return cond_end;
|
||||
}, type, delay);
|
||||
}
|
||||
|
||||
void on_init(const std::function<void()>& callback)
|
||||
{
|
||||
if (com_inited)
|
||||
{
|
||||
once(callback, pipeline::main);
|
||||
}
|
||||
else
|
||||
{
|
||||
post_init_funcs.push_back(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void on_pre_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback)
|
||||
{
|
||||
pre_scr_init_funcs.push_back(callback);
|
||||
}
|
||||
|
||||
void on_post_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback)
|
||||
{
|
||||
post_scr_init_funcs.push_back(callback);
|
||||
}
|
||||
|
||||
void exec_pre_scr_init_funcs(game::scriptInstance_t inst)
|
||||
{
|
||||
for (const auto& func : pre_scr_init_funcs)
|
||||
{
|
||||
func(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void exec_post_scr_init_funcs(game::scriptInstance_t inst)
|
||||
{
|
||||
for (const auto& func : post_scr_init_funcs)
|
||||
{
|
||||
func(inst);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
thread = std::thread([]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
execute(pipeline::async);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
|
||||
com_init_hook.create(SELECT(0x0, 0x59D710), com_init_stub);
|
||||
utils::hook::call(SELECT(0x0, 0x503B5D), execute_server);
|
||||
utils::hook::call(SELECT(0x0, 0x59DCFD), execute_main);
|
||||
|
||||
// for when we dont use decomp
|
||||
pre_scr_init_system_hook.create(0x699865, pre_scr_init_system_stub);
|
||||
pre_scr_init_system_original = pre_scr_init_system_hook.get_original();
|
||||
post_scr_init_system_hook.create(0x699924, post_scr_init_system_stub);
|
||||
post_scr_init_system_original = post_scr_init_system_hook.get_original();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(scheduler::component)
|
||||
|
@ -2,28 +2,5 @@
|
||||
|
||||
namespace scheduler
|
||||
{
|
||||
enum pipeline
|
||||
{
|
||||
server,
|
||||
async,
|
||||
main,
|
||||
count,
|
||||
};
|
||||
|
||||
static const bool cond_continue = false;
|
||||
static const bool cond_end = true;
|
||||
|
||||
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::main,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void loop(const std::function<void()>& callback, pipeline type = pipeline::main,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void once(const std::function<void()>& callback, pipeline type = pipeline::main,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
|
||||
void on_init(const std::function<void()>& callback);
|
||||
|
||||
void on_pre_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback);
|
||||
void on_post_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback);
|
||||
void exec_pre_scr_init_funcs(game::scriptInstance_t inst);
|
||||
void exec_post_scr_init_funcs(game::scriptInstance_t inst);
|
||||
void on_scr_execute(void(*callback)());
|
||||
}
|
||||
|
@ -9,174 +9,32 @@
|
||||
|
||||
namespace signatures
|
||||
{
|
||||
std::string read_sigs_file()
|
||||
{
|
||||
return utils::compression::zlib::decompress(utils::cryptography::des::decrypt(utils::io::read_file("t4sp-server-plugin/sigs")));
|
||||
}
|
||||
|
||||
bool write_sigs_file(const std::string& f)
|
||||
{
|
||||
return utils::io::write_file("t4sp-server-plugin/sigs", utils::cryptography::des::encrypt(utils::compression::zlib::compress(f)));
|
||||
}
|
||||
|
||||
const char* get_current_version()
|
||||
{
|
||||
return *reinterpret_cast<const char**>(0x4FF72D + 4);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> get_cache_info_for_our_version()
|
||||
{
|
||||
std::unordered_map<std::string, std::string> answer;
|
||||
|
||||
auto* version = get_current_version();
|
||||
|
||||
nlohmann::json cache_json = nlohmann::json::parse(read_sigs_file(), nullptr, false, true);
|
||||
if (!cache_json.is_discarded() && cache_json.is_object())
|
||||
{
|
||||
for (const auto& [key, value] : cache_json.items())
|
||||
{
|
||||
if (key != version)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!value.is_object())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
answer = value.get<std::unordered_map<std::string, std::string>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool save_cache_info_for_our_version(const std::unordered_map<std::string, std::string>& cache_info)
|
||||
{
|
||||
auto* version = get_current_version();
|
||||
|
||||
nlohmann::json cache_json = nlohmann::json::parse(read_sigs_file(), nullptr, false, true);
|
||||
if (cache_json.is_discarded() || !cache_json.is_object())
|
||||
{
|
||||
cache_json = nlohmann::json::parse("{}", nullptr, false, true);
|
||||
|
||||
if (cache_json.is_discarded() || !cache_json.is_object())
|
||||
{
|
||||
return false; // can't happen?
|
||||
}
|
||||
}
|
||||
|
||||
cache_json[version] = cache_info;
|
||||
return write_sigs_file(cache_json.dump());
|
||||
}
|
||||
|
||||
size_t load_image_size()
|
||||
bool addr_is_in_image_space_of_pluto(size_t wheree)
|
||||
{
|
||||
MODULEINFO info{};
|
||||
GetModuleInformation(GetCurrentProcess(),
|
||||
GetModuleHandle("plutonium-bootstrapper-win32.exe"), &info, sizeof(MODULEINFO));
|
||||
return info.SizeOfImage;
|
||||
}
|
||||
|
||||
size_t get_image_size()
|
||||
{
|
||||
static const auto image_size = load_image_size();
|
||||
return image_size;
|
||||
}
|
||||
static const auto image_base = reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
||||
|
||||
size_t load_iamge_base()
|
||||
{
|
||||
return reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
||||
}
|
||||
|
||||
size_t get_image_base()
|
||||
{
|
||||
static const auto image_base = load_iamge_base();
|
||||
return image_base;
|
||||
}
|
||||
|
||||
bool addr_is_in_image_space(size_t wheree)
|
||||
{
|
||||
static const auto image_base = load_iamge_base();
|
||||
|
||||
return wheree >= image_base && wheree < image_base + get_image_size();
|
||||
}
|
||||
|
||||
size_t find_string_ptr(const std::string& string)
|
||||
{
|
||||
const char* string_ptr = nullptr;
|
||||
std::string mask(string.size(), 'x');
|
||||
const auto base = get_image_base();
|
||||
utils::hook::signature signature(base, get_image_size() - base);
|
||||
|
||||
signature.add({
|
||||
string,
|
||||
mask,
|
||||
[&](char* address)
|
||||
{
|
||||
string_ptr = address;
|
||||
}
|
||||
});
|
||||
|
||||
signature.process();
|
||||
return reinterpret_cast<size_t>(string_ptr);
|
||||
}
|
||||
|
||||
size_t find_string_ref(const std::string& string)
|
||||
{
|
||||
char bytes[4] = {0};
|
||||
const auto string_ptr = find_string_ptr(string);
|
||||
if (!string_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(bytes, &string_ptr, sizeof(bytes));
|
||||
return find_string_ptr({bytes, 4});
|
||||
return wheree >= image_base && wheree < image_base + info.SizeOfImage;
|
||||
}
|
||||
|
||||
std::string err_reason;
|
||||
|
||||
const std::string& get_err_reason()
|
||||
{
|
||||
return err_reason;
|
||||
}
|
||||
|
||||
bool process_printf(std::unordered_map<std::string, std::string> &cache_info)
|
||||
{
|
||||
if (cache_info.contains("printf"))
|
||||
{
|
||||
game::plutonium::printf.set(std::atoi(cache_info.at("printf").c_str()));
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto string_ref = find_string_ref("A critical exception occured!\n");
|
||||
if (!string_ref)
|
||||
{
|
||||
err_reason = "printf";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto offset = *reinterpret_cast<size_t*>(string_ref + 5);
|
||||
game::plutonium::printf.set(string_ref + 4 + 5 + offset);
|
||||
|
||||
cache_info.insert_or_assign("printf", std::to_string(string_ref + 4 + 5 + offset));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define SAFE_SET_PLUTO_SYMBOL_DOUBLE(name, addr, off) \
|
||||
addr2 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
||||
if (!addr_is_in_image_space(addr2)) \
|
||||
if (!addr_is_in_image_space_of_pluto(addr2)) \
|
||||
{ \
|
||||
err_reason = #name " 1"; \
|
||||
return false; \
|
||||
} \
|
||||
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr2 + off)); \
|
||||
if (!addr_is_in_image_space(addr1)) \
|
||||
if (!addr_is_in_image_space_of_pluto(addr1)) \
|
||||
{ \
|
||||
err_reason = #name " 2"; \
|
||||
return false; \
|
||||
@ -186,7 +44,7 @@ namespace signatures
|
||||
|
||||
#define SAFE_SET_PLUTO_SYMBOL(name, addr) \
|
||||
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
||||
if (!addr_is_in_image_space(addr1)) \
|
||||
if (!addr_is_in_image_space_of_pluto(addr1)) \
|
||||
{ \
|
||||
err_reason = #name; \
|
||||
return false; \
|
||||
@ -198,12 +56,6 @@ namespace signatures
|
||||
{
|
||||
size_t addr1;
|
||||
size_t addr2;
|
||||
auto cache_info = get_cache_info_for_our_version();
|
||||
|
||||
if (!process_printf(cache_info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(load_custom_script_func, 0x689C80, 0x6);
|
||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(script_preprocess, 0x689BCF, 0x2);
|
||||
@ -216,21 +68,15 @@ namespace signatures
|
||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(store_func_codepos, 0x688909, 0x3);
|
||||
|
||||
SAFE_SET_PLUTO_SYMBOL(cscr_get_function_hook, 0x682DC0);
|
||||
SAFE_SET_PLUTO_SYMBOL(scr_get_function_stub, 0x682D99);
|
||||
SAFE_SET_PLUTO_SYMBOL(scr_get_method_stub, 0x683043);
|
||||
SAFE_SET_PLUTO_SYMBOL(cscr_get_method_hook, 0x68305C);
|
||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_method_hook, 0x683043, 0x4);
|
||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_function_hook, 0x682D99, 0x8);
|
||||
|
||||
save_cache_info_for_our_version(cache_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process()
|
||||
{
|
||||
utils::cryptography::des::set_key("694201337");
|
||||
|
||||
return handle_funcs();
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize()
|
||||
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_*/)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
@ -96,8 +96,6 @@ namespace game
|
||||
|
||||
namespace plutonium
|
||||
{
|
||||
WEAK symbol<int(const char* fmt, ...)> printf{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(scriptInstance_t)> load_custom_script_func{0x0, 0x0};
|
||||
WEAK symbol<void(char*, game::scriptInstance_t, sval_u*)> script_preprocess{0x0, 0x0};
|
||||
WEAK symbol<void(game::scriptInstance_t)> vm_execute_update_codepos{0x0, 0x0};
|
||||
@ -107,9 +105,6 @@ namespace game
|
||||
|
||||
WEAK symbol<const char*(game::scriptInstance_t, unsigned int)> at_codepose_va{ 0x0, 0x0 };
|
||||
|
||||
WEAK symbol<void()> scr_get_method_stub{ 0x0, 0x0 };
|
||||
WEAK symbol<void()> scr_get_function_stub{ 0x0, 0x0 };
|
||||
|
||||
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> scr_get_method_hook{ 0x0, 0x0 };
|
||||
WEAK symbol<game::BuiltinFunction(const char** name, int* type)> scr_get_function_hook{ 0x0, 0x0 };
|
||||
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> cscr_get_method_hook{ 0x0, 0x0 };
|
||||
|
@ -43,11 +43,6 @@ namespace plugin
|
||||
return;
|
||||
}
|
||||
|
||||
if (game::plutonium::printf.get() != nullptr)
|
||||
{
|
||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), game::plutonium::printf);
|
||||
}
|
||||
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user