mirror of
https://github.com/alicealys/t5-gsc-utils.git
synced 2025-03-23 07:46:57 +00:00
feature(user_info): allow GSC to edit name/clantag
This commit is contained in:
parent
7d5060c8bc
commit
1aed1787ba
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@ -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: |
|
||||
|
2
deps/curl
vendored
2
deps/curl
vendored
@ -1 +1 @@
|
||||
Subproject commit 134963a5efdc3906257c88ce62dba8d46c292908
|
||||
Subproject commit ab885eccd6c82a949e21cfab9af778c0fd44d0ae
|
@ -92,7 +92,6 @@ workspace "t5-gsc-utils"
|
||||
|
||||
pchheader "stdinc.hpp"
|
||||
pchsource "src/stdinc.cpp"
|
||||
buildoptions { "/Zm100 -Zm100" }
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace command
|
||||
|
||||
game::CmdArgs* get_cmd_args()
|
||||
{
|
||||
return reinterpret_cast<game::CmdArgs*>(game::Sys_GetValue(4));
|
||||
return static_cast<game::CmdArgs*>(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");
|
||||
}
|
||||
|
@ -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<size_t>(stub);
|
||||
}
|
||||
return reinterpret_cast<size_t>(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<LPOSVERSIONINFOA>(&version_info));
|
||||
OSVERSIONINFOEXA version_info;
|
||||
ZeroMemory(&version_info, sizeof(version_info));
|
||||
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
||||
GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&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<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
||||
SetUnhandledExceptionFilter(exception_filter);
|
||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(exception::component)
|
||||
|
@ -6,336 +6,333 @@
|
||||
|
||||
#include "gsc.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
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<std::string, function_t> functions;
|
||||
std::unordered_map<std::string, function_t> methods;
|
||||
std::unordered_map<std::string, function_t> functions;
|
||||
std::unordered_map<std::string, function_t> methods;
|
||||
|
||||
std::unordered_map<std::string, void*> function_wraps;
|
||||
std::unordered_map<std::string, void*> method_wraps;
|
||||
std::unordered_map<std::string, void*> function_wraps;
|
||||
std::unordered_map<std::string, void*> method_wraps;
|
||||
|
||||
std::vector<scripting::script_value> get_arguments()
|
||||
{
|
||||
std::vector<scripting::script_value> args;
|
||||
std::vector<scripting::script_value> get_arguments()
|
||||
{
|
||||
std::vector<scripting::script_value> args;
|
||||
|
||||
for (auto i = 0; static_cast<unsigned int>(i) < game::scr_VmPub->outparamcount; i++)
|
||||
{
|
||||
const auto value = game::scr_VmPub->top[-i];
|
||||
args.push_back(value);
|
||||
}
|
||||
for (auto i = 0; static_cast<unsigned int>(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<scripting::script_value> args_{};
|
||||
args_.push_back(entity);
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
args_.push_back(arg);
|
||||
}
|
||||
std::vector<scripting::script_value> 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<script_function>(function_wraps[*name]);
|
||||
}
|
||||
script_function get_function_stub(const char** name, int* type)
|
||||
{
|
||||
if (function_wraps.find(*name) != function_wraps.end())
|
||||
{
|
||||
return reinterpret_cast<script_function>(function_wraps[*name]);
|
||||
}
|
||||
|
||||
return get_function_hook.invoke<script_function>(name, type);
|
||||
}
|
||||
return get_function_hook.invoke<script_function>(name, type);
|
||||
}
|
||||
|
||||
script_function get_method_stub(const char** name, int* type)
|
||||
{
|
||||
if (method_wraps.find(*name) != method_wraps.end())
|
||||
{
|
||||
return reinterpret_cast<script_function>(method_wraps[*name]);
|
||||
}
|
||||
script_function get_method_stub(const char** name, int* type)
|
||||
{
|
||||
if (method_wraps.find(*name) != method_wraps.end())
|
||||
{
|
||||
return reinterpret_cast<script_function>(method_wraps[*name]);
|
||||
}
|
||||
|
||||
return get_method_hook.invoke<script_function>(name, type);
|
||||
}
|
||||
return get_method_hook.invoke<script_function>(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<void>(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<void>(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<uint8_t*>(func_name.block);
|
||||
if (type != 28)
|
||||
{
|
||||
return scr_get_builtin_hook.invoke<unsigned int>(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<uint8_t*>(func_name.block);
|
||||
if (type != 28)
|
||||
{
|
||||
return scr_get_builtin_hook.invoke<unsigned int>(inst, func_name);
|
||||
}
|
||||
|
||||
const auto func_namea = *reinterpret_cast<void**>(reinterpret_cast<size_t>(func_name.block) + 4);
|
||||
const auto typea = *reinterpret_cast<uint8_t*>(func_namea);
|
||||
if (typea != 20)
|
||||
{
|
||||
return scr_get_builtin_hook.invoke<unsigned int>(inst, func_name);
|
||||
}
|
||||
const auto func_namea = *reinterpret_cast<void**>(reinterpret_cast<size_t>(func_name.block) + 4);
|
||||
const auto typea = *reinterpret_cast<uint8_t*>(func_namea);
|
||||
if (typea != 20)
|
||||
{
|
||||
return scr_get_builtin_hook.invoke<unsigned int>(inst, func_name);
|
||||
}
|
||||
|
||||
const auto func_nameb = *reinterpret_cast<void**>(reinterpret_cast<size_t>(func_namea) + 4);
|
||||
const auto typeb = *reinterpret_cast<uint8_t*>(func_nameb);
|
||||
const auto func_nameb = *reinterpret_cast<void**>(reinterpret_cast<size_t>(func_namea) + 4);
|
||||
const auto typeb = *reinterpret_cast<uint8_t*>(func_nameb);
|
||||
|
||||
if (typeb == 23) // script::function type call
|
||||
{
|
||||
const auto namespace_ = game::SL_ConvertToString(
|
||||
*reinterpret_cast<unsigned int*>(reinterpret_cast<size_t>(func_nameb) + 4), game::SCRIPTINSTANCE_SERVER);
|
||||
const auto name = game::SL_ConvertToString(
|
||||
*reinterpret_cast<unsigned int*>(reinterpret_cast<size_t>(func_nameb) + 8), game::SCRIPTINSTANCE_SERVER);
|
||||
if (typeb == 23) // script::function type call
|
||||
{
|
||||
const auto namespace_ = game::SL_ConvertToString(
|
||||
*reinterpret_cast<unsigned int*>(reinterpret_cast<size_t>(func_nameb) + 4), game::SCRIPTINSTANCE_SERVER);
|
||||
const auto name = game::SL_ConvertToString(
|
||||
*reinterpret_cast<unsigned int*>(reinterpret_cast<size_t>(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<unsigned int>(inst, func_name);
|
||||
}
|
||||
return scr_get_builtin_hook.invoke<unsigned int>(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<unsigned int>(SELECT_VALUE(0x5E96E0, 0x40BEF0), inst, entnum, classnum, client_num);
|
||||
}
|
||||
}
|
||||
return utils::hook::invoke<unsigned int>(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<size_t>(SELECT_VALUE(0x8A02FB, 0x8DE11B) + 1), get_function_stub);
|
||||
get_method_hook.create(utils::hook::extract<size_t>(SELECT_VALUE(0x8A052E, 0x8DE34E) + 1), get_method_stub);
|
||||
get_function_hook.create(utils::hook::extract<size_t>(SELECT_VALUE(0x8A02FB, 0x8DE11B) + 1), get_function_stub);
|
||||
get_method_hook.create(utils::hook::extract<size_t>(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<char>(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n');
|
||||
utils::hook::set<char>(SELECT_VALUE(0x9FC5C0 + 41, 0xAABA68 + 41), '\0');
|
||||
// \n******* script runtime error *******\n%s\n
|
||||
utils::hook::set<char>(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n');
|
||||
utils::hook::set<char>(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<int>(std::strtoull(str.data(), nullptr, radix));
|
||||
});
|
||||
return static_cast<int>(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<std::string>();
|
||||
}
|
||||
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<std::string>();
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -13,165 +13,165 @@
|
||||
|
||||
namespace http
|
||||
{
|
||||
std::unordered_map<uint64_t, bool> active_requests{};
|
||||
uint64_t request_id{};
|
||||
std::unordered_map<uint64_t, bool> 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<int>(result.buffer.size()));
|
||||
}
|
||||
if (result.buffer.size() >= 0x5000)
|
||||
{
|
||||
printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(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<std::string, std::string> headers_map{};
|
||||
std::string fields_string{};
|
||||
std::unordered_map<std::string, std::string> headers_map{};
|
||||
|
||||
if (va.size() > 0)
|
||||
{
|
||||
const auto options = va[0].as<scripting::array>();
|
||||
if (va.size() > 0)
|
||||
{
|
||||
const auto options = va[0].as<scripting::array>();
|
||||
|
||||
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<scripting::array>())
|
||||
{
|
||||
const auto fields_ = fields.as<scripting::array>();
|
||||
const auto keys = fields_.get_keys();
|
||||
if (fields.is<scripting::array>())
|
||||
{
|
||||
const auto fields_ = fields.as<scripting::array>();
|
||||
const auto keys = fields_.get_keys();
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto key_ = key.as<std::string>();
|
||||
const auto value = fields_[key].to_string();
|
||||
fields_string += key_ + "=" + value + "&";
|
||||
}
|
||||
const auto key_ = key.as<std::string>();
|
||||
const auto value = fields_[key].to_string();
|
||||
fields_string += key_ + "=" + value + "&";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (body.is<std::string>())
|
||||
{
|
||||
fields_string = body.as<std::string>();
|
||||
}
|
||||
if (body.is<std::string>())
|
||||
{
|
||||
fields_string = body.as<std::string>();
|
||||
}
|
||||
|
||||
if (headers.is<scripting::array>())
|
||||
{
|
||||
const auto headers_ = headers.as<scripting::array>();
|
||||
const auto keys = headers_.get_keys();
|
||||
if (headers.is<scripting::array>())
|
||||
{
|
||||
const auto headers_ = headers.as<scripting::array>();
|
||||
const auto keys = headers_.get_keys();
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto key_ = key.as<std::string>();
|
||||
const auto value = headers_[key].to_string();
|
||||
const auto key_ = key.as<std::string>();
|
||||
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<int>(result.buffer.size()));
|
||||
}
|
||||
if (result.buffer.size() >= 0x5000)
|
||||
{
|
||||
printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(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)
|
||||
|
@ -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<std::string(*)(const std::string&)>(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<std::string(*)(const std::string&)>(utils::io::read_file), "readfile", "io::read_file");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(io::component)
|
||||
|
@ -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<unsigned char>(*s)))
|
||||
{
|
||||
s++;
|
||||
if (isdigit(static_cast<unsigned char>(*s)))
|
||||
{
|
||||
s++;
|
||||
}
|
||||
}
|
||||
// lua/lstrlib.c
|
||||
const char* get_2_digits(const char* s)
|
||||
{
|
||||
if (isdigit(static_cast<unsigned char>(*s)))
|
||||
{
|
||||
s++;
|
||||
if (isdigit(static_cast<unsigned char>(*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<unsigned char>(*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<unsigned char>(*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<int>();
|
||||
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<float>();
|
||||
buffer.append(utils::string::va(form, value));
|
||||
va_index++;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
const auto value = va[va_index].as<int>();
|
||||
check_format(form, L_FMTFLAGSC, 0);
|
||||
buffer.append(utils::string::va(form, static_cast<char>(value)));
|
||||
va_index++;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
const auto str = va[va_index].as<std::string>();
|
||||
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<int>();
|
||||
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<float>();
|
||||
buffer.append(utils::string::va(form, value));
|
||||
va_index++;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
const auto value = va[va_index].as<int>();
|
||||
check_format(form, L_FMTFLAGSC, 0);
|
||||
buffer.append(utils::string::va(form, static_cast<char>(value)));
|
||||
va_index++;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
const auto str = va[va_index].as<std::string>();
|
||||
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)
|
||||
|
143
src/component/user_info.cpp
Normal file
143
src/component/user_info.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "gsc.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/info_string.hpp>
|
||||
|
||||
namespace user_info
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using user_info_map = std::unordered_map<std::string, std::string>;
|
||||
std::unordered_map<int, user_info_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<void>(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)
|
@ -17,8 +17,6 @@ namespace game
|
||||
WEAK symbol<const char*(int index)> Cmd_Argv{0x0, 0x0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x5F1A90, 0x527EA0};
|
||||
|
||||
WEAK symbol<void(int clientNum)> ClientUserInfoChanged{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(errorParm_t code, const char* fmt, ...)> Com_Error{0x651D90, 0x627380};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_Printf{0x43BF30, 0x4126C0};
|
||||
WEAK symbol<void(const char* fmt, ...)> Com_Printf_NoFilter{0x566BC0, 0x64C260};
|
||||
@ -107,10 +105,14 @@ namespace game
|
||||
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x8ACE60, 0x8EADE0};
|
||||
|
||||
WEAK symbol<void(int index, char* buffer, int bufferSize)> SV_GetUserinfo{0x50DCD0, 0x6916A0};
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_GameDropClient{0x0, 0x0};
|
||||
WEAK symbol<bool(int clientNum)> SV_IsTestClient{0x0, 0x0};
|
||||
WEAK symbol<void(int clientNum, int type, const char* command)> SV_GameSendServerCommand{0x543CF0, 0x6B8730};
|
||||
|
||||
WEAK symbol<void(int clientNum)> ClientDisconnect{0x4F4000, 0x66FA00};
|
||||
WEAK symbol<void(int clientNum)> ClientUserinfoChanged{0x67FFC0, 0x548D80};
|
||||
|
||||
WEAK symbol<void*(int valueIndex)> Sys_GetValue{0x67D4F0, 0x529EB0};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x0, 0x0};
|
||||
|
||||
|
@ -11,24 +11,25 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <csetjmp>
|
||||
#include <atlcomcli.h>
|
||||
#include <variant>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
|
@ -101,12 +101,5 @@ namespace utils::http
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (helper.exception)
|
||||
{
|
||||
std::rethrow_exception(helper.exception);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
67
src/utils/info_string.cpp
Normal file
67
src/utils/info_string.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
25
src/utils/info_string.hpp
Normal file
25
src/utils/info_string.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
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<std::string, std::string> key_value_pairs_{};
|
||||
|
||||
void parse(std::string buffer);
|
||||
};
|
||||
}
|
@ -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<char>(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<char>(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()
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user