Merge pull request #96 from mjkzy/gsc

GSC, Anti-anti debug, Arxan
This commit is contained in:
m 2024-01-20 20:02:44 -06:00 committed by GitHub
commit 81c1b8fd99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 8602 additions and 2038 deletions

4
.gitmodules vendored
View File

@ -31,3 +31,7 @@
[submodule "deps/rapidjson"] [submodule "deps/rapidjson"]
path = deps/rapidjson path = deps/rapidjson
url = https://github.com/Tencent/rapidjson.git url = https://github.com/Tencent/rapidjson.git
[submodule "deps/gsc-tool"]
path = deps/gsc-tool
url = https://github.com/Joelrau/gsc-tool.git
branch = iw7

1
deps/gsc-tool vendored Submodule

@ -0,0 +1 @@
Subproject commit 4761bfbadeba5f4fd04714b837aef93b85977ec2

62
deps/premake/gsc-tool.lua vendored Normal file
View File

@ -0,0 +1,62 @@
gsc_tool = {
source = path.join(dependencies.basePath, "gsc-tool")
}
function gsc_tool.import()
links {"xsk-gsc-iw7", "xsk-gsc-utils"}
gsc_tool.includes()
end
function gsc_tool.includes()
includedirs {
path.join(gsc_tool.source, "include")
}
end
function gsc_tool.project()
project "xsk-gsc-utils"
kind "StaticLib"
language "C++"
warnings "Off"
files {
path.join(gsc_tool.source, "include/xsk/utils/*.hpp"),
path.join(gsc_tool.source, "src/utils/*.cpp")
}
includedirs {
path.join(gsc_tool.source, "include")
}
zlib.includes()
project "xsk-gsc-iw7"
kind "StaticLib"
language "C++"
warnings "Off"
filter "action:vs*"
buildoptions "/Zc:__cplusplus"
filter {}
files {
path.join(gsc_tool.source, "include/xsk/stdinc.hpp"),
path.join(gsc_tool.source, "include/xsk/gsc/engine/iw7.hpp"),
path.join(gsc_tool.source, "src/gsc/engine/iw7.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/iw7_code.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/iw7_func.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/iw7_meth.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/iw7_token.cpp"), path.join(gsc_tool.source, "src/gsc/*.cpp"),
path.join(gsc_tool.source, "src/gsc/common/*.cpp"),
path.join(gsc_tool.source, "include/xsk/gsc/common/*.hpp")
}
includedirs {
path.join(gsc_tool.source, "include")
}
end
table.insert(dependencies, gsc_tool)

View File

@ -322,6 +322,15 @@ links {"common"}
prebuildcommands {"pushd %{_MAIN_SCRIPT_DIR}", "tools\\premake5 generate-buildinfo", "popd"} prebuildcommands {"pushd %{_MAIN_SCRIPT_DIR}", "tools\\premake5 generate-buildinfo", "popd"}
COMPUTER_NAME = os.getenv('COMPUTERNAME')
if COMPUTER_NAME == "JOEL-PC" then
debugdir "D:\\Games\\PC\\IW7"
debugcommand "D:\\Games\\PC\\IW7\\$(TargetName)$(TargetExt)"
postbuildcommands {
"copy /y \"$(OutDir)$(TargetName)$(TargetExt)\" \"D:\\Games\\PC\\IW7\\$(TargetName)$(TargetExt)\"",
}
end
if _OPTIONS["copy-to"] then if _OPTIONS["copy-to"] then
postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""} postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""}
end end

View File

@ -10,8 +10,12 @@
#include <utils/string.hpp> #include <utils/string.hpp>
#include "integrity.hpp" #include "integrity.hpp"
#include "breakpoints.hpp"
#include "illegal_instructions.hpp"
#define PRECOMPUTED_INTEGRITY_CHECKS #define PRECOMPUTED_INTEGRITY_CHECKS
#define PRECOMPUTED_BREAKPOINTS
#define PRECOMPUTED_ILLEGAL_INSTRUCTIONS
#define ProcessDebugPort 7 #define ProcessDebugPort 7
#define ProcessDebugObjectHandle 30 #define ProcessDebugObjectHandle 30
@ -19,85 +23,8 @@
namespace arxan namespace arxan
{ {
namespace namespace integrity
{ {
utils::hook::detour nt_close_hook;
utils::hook::detour nt_query_information_process_hook;
HANDLE process_id_to_handle(const DWORD pid)
{
return reinterpret_cast<HANDLE>(static_cast<DWORD64>(pid));
}
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
const PVOID info,
const ULONG info_length, const PULONG ret_length)
{
auto* orig = static_cast<decltype(NtQueryInformationProcess)*>(nt_query_information_process_hook.get_original());
const auto status = orig(handle, info_class, info, info_length, ret_length);
if (NT_SUCCESS(status))
{
if (info_class == ProcessBasicInformation)
{
static DWORD explorer_pid = 0;
if (!explorer_pid)
{
auto* const shell_window = GetShellWindow();
GetWindowThreadProcessId(shell_window, &explorer_pid);
}
// InheritedFromUniqueProcessId
static_cast<PPROCESS_BASIC_INFORMATION>(info)->Reserved3 = PVOID(DWORD64(explorer_pid));
}
else if (info_class == ProcessDebugObjectHandle)
{
*static_cast<HANDLE*>(info) = nullptr;
return 0xC0000353;
}
else if (info_class == ProcessDebugPort)
{
*static_cast<HANDLE*>(info) = nullptr;
}
else if (info_class == ProcessDebugFlags)
{
*static_cast<ULONG*>(info) = 1;
}
}
return status;
}
NTSTATUS NTAPI nt_close_stub(const HANDLE handle)
{
char info[16];
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345)
{
auto* orig = static_cast<decltype(NtClose)*>(nt_close_hook.get_original());
return orig(handle);
}
return STATUS_INVALID_HANDLE;
}
void hide_being_debugged()
{
auto* const peb = PPEB(__readgsqword(0x60));
peb->BeingDebugged = false;
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70;
}
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
const std::vector<std::pair<uint8_t*, size_t>>& get_text_sections() const std::vector<std::pair<uint8_t*, size_t>>& get_text_sections()
{ {
static const std::vector<std::pair<uint8_t*, size_t>> text = [] static const std::vector<std::pair<uint8_t*, size_t>> text = []
@ -179,7 +106,7 @@ namespace arxan
uint32_t adjust_integrity_checksum(const uint64_t return_address, uint8_t* stack_frame, uint32_t adjust_integrity_checksum(const uint64_t return_address, uint8_t* stack_frame,
const uint32_t current_checksum) const uint32_t current_checksum)
{ {
[[maybe_unused]]const auto handler_address = return_address - 5; [[maybe_unused]] const auto handler_address = return_address - 5;
const auto* context = search_handler_context(stack_frame, current_checksum); const auto* context = search_handler_context(stack_frame, current_checksum);
if (!context) if (!context)
@ -320,9 +247,6 @@ namespace arxan
assert(game::base_address == 0x140000000); assert(game::base_address == 0x140000000);
search_and_patch_integrity_checks_precomputed(); search_and_patch_integrity_checks_precomputed();
#else #else
// There seem to be 670 results.
// Searching them is quite slow.
// Maybe precomputing that might be better?
const auto intact_results = "89 04 8A 83 45 ? FF"_sig; const auto intact_results = "89 04 8A 83 45 ? FF"_sig;
const auto split_results = "89 04 8A E9"_sig; const auto split_results = "89 04 8A E9"_sig;
@ -338,14 +262,405 @@ namespace arxan
#endif #endif
} }
} }
using namespace integrity;
namespace anti_debug
{
utils::hook::detour nt_close_hook;
utils::hook::detour nt_query_information_process_hook;
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
const PVOID info,
const ULONG info_length, const PULONG ret_length)
{
auto* orig = static_cast<decltype(NtQueryInformationProcess)*>(nt_query_information_process_hook.get_original());
auto status = orig(handle, info_class, info, info_length, ret_length);
if (NT_SUCCESS(status))
{
if (info_class == ProcessDebugObjectHandle)
{
*static_cast<HANDLE*>(info) = nullptr;
return static_cast<LONG>(0xC0000353);
}
else if (info_class == ProcessDebugPort)
{
*static_cast<HANDLE*>(info) = nullptr;
}
else if (info_class == ProcessDebugFlags)
{
*static_cast<ULONG*>(info) = 1;
}
}
return status;
}
NTSTATUS NTAPI nt_close_stub(const HANDLE handle)
{
char info[16];
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345)
{
auto* orig = static_cast<decltype(NtClose)*>(nt_close_hook.get_original());
return orig(handle);
}
return STATUS_INVALID_HANDLE;
}
void hide_being_debugged()
{
auto* const peb = PPEB(__readgsqword(0x60));
peb->BeingDebugged = false;
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70; // NtGlobalFlag
}
void remove_hardware_breakpoints()
{
CONTEXT context;
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
auto* const thread = GetCurrentThread();
GetThreadContext(thread, &context);
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr6 = 0;
context.Dr7 = 0;
SetThreadContext(thread, &context);
}
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{
if (context->ContextFlags == CONTEXT_DEBUG_REGISTERS)
{
return TRUE;
}
return SetThreadContext(thread, context);
}
enum dbg_funcs_e
{
DbgBreakPoint,
DbgUserBreakPoint,
DbgUiConnectToDbg,
DbgUiContinue,
DbgUiConvertStateChangeStructure,
DbgUiDebugActiveProcess,
DbgUiGetThreadDebugObject,
DbgUiIssueRemoteBreakin,
DbgUiRemoteBreakin,
DbgUiSetThreadDebugObject,
DbgUiStopDebugging,
DbgUiWaitStateChange,
DbgPrintReturnControlC,
DbgPrompt,
DBG_FUNCS_COUNT,
};
const char* dbg_funcs_names[] =
{
"DbgBreakPoint",
"DbgUserBreakPoint",
"DbgUiConnectToDbg",
"DbgUiContinue",
"DbgUiConvertStateChangeStructure",
"DbgUiDebugActiveProcess",
"DbgUiGetThreadDebugObject",
"DbgUiIssueRemoteBreakin",
"DbgUiRemoteBreakin",
"DbgUiSetThreadDebugObject",
"DbgUiStopDebugging",
"DbgUiWaitStateChange",
"DbgPrintReturnControlC",
"DbgPrompt",
};
struct dbg_func_bytes_s
{
std::uint8_t buffer[15];
};
dbg_func_bytes_s dbg_func_bytes[DBG_FUNCS_COUNT];
void* dbg_func_procs[DBG_FUNCS_COUNT]{};
void store_debug_functions()
{
const utils::nt::library ntdll("ntdll.dll");
for (auto i = 0; i < DBG_FUNCS_COUNT; i++)
{
dbg_func_procs[i] = ntdll.get_proc<void*>(dbg_funcs_names[i]);
memcpy(dbg_func_bytes[i].buffer, dbg_func_procs[i], sizeof(dbg_func_bytes[i].buffer));
}
}
void restore_debug_functions()
{
for (auto i = 0; i < DBG_FUNCS_COUNT; i++)
{
utils::hook::copy(dbg_func_procs[i], dbg_func_bytes[i].buffer, sizeof(dbg_func_bytes[i].buffer));
}
}
namespace exceptions
{
std::unordered_map<PVOID, void*> handle_handler;
void fake_exception(void* address, _CONTEXT* fake_context, DWORD exception)
{
_EXCEPTION_POINTERS fake_info{};
_EXCEPTION_RECORD fake_record{};
fake_info.ExceptionRecord = &fake_record;
fake_info.ContextRecord = fake_context;
fake_record.ExceptionAddress = reinterpret_cast<void*>(reinterpret_cast<std::uint64_t>(address) + 3);
fake_record.ExceptionCode = exception;
for (auto handler : handle_handler)
{
if (handler.second)
{
auto result = utils::hook::invoke<LONG>(handler.second, &fake_info);
if (result)
{
memset(fake_context, 0, sizeof(_CONTEXT));
break;
}
}
}
}
void patch_int2d_trap(void* address)
{
const auto game_address = reinterpret_cast<std::uint64_t>(address);
const auto jump_target = utils::hook::extract<void*>(reinterpret_cast<void*>(game_address + 3));
_CONTEXT* fake_context = new _CONTEXT{};
const auto stub = utils::hook::assemble([address, jump_target, fake_context](utils::hook::assembler& a)
{
a.push(rcx);
a.mov(rcx, fake_context);
a.call_aligned(RtlCaptureContext);
a.pop(rcx);
a.pushad64();
a.mov(rcx, address);
a.mov(rdx, fake_context);
a.mov(r8, EXCEPTION_BREAKPOINT);
a.call_aligned(fake_exception);
a.popad64();
a.jmp(jump_target);
});
utils::hook::nop(game_address, 7);
utils::hook::jump(game_address, stub, false);
}
#ifdef PRECOMPUTED_BREAKPOINTS
void patch_breakpoints_precomputed()
{
for (const auto i : int2d_breakpoint_addresses)
{
patch_int2d_trap(reinterpret_cast<void*>(i));
}
}
#endif
void patch_breakpoints()
{
static bool once = false;
if (once)
{
return;
}
once = true;
#ifdef PRECOMPUTED_BREAKPOINTS
assert(game::base_address == 0x140000000);
patch_breakpoints_precomputed();
#else
const auto int2d_results = utils::hook::signature("CD 2D E9 ? ? ? ?", game_module::get_game_module()).process();
for (auto* i : int2d_results)
{
patch_int2d_trap(i);
}
#endif
}
void patch_illegal_instruction_intact(void* address)
{
const auto game_address = reinterpret_cast<std::uint64_t>(address);
const auto jump_target = game_address + 6;
const auto a1 = *reinterpret_cast<std::uint8_t*>(game_address + 3);
_CONTEXT* fake_context = new _CONTEXT{};
const auto stub = utils::hook::assemble([address, jump_target, fake_context, a1](utils::hook::assembler& a)
{
a.lea(rax, ptr(rbp, a1));
a.push(rcx);
a.mov(rcx, fake_context);
a.call_aligned(RtlCaptureContext);
a.pop(rcx);
a.pushad64();
a.mov(rcx, address);
a.mov(rdx, fake_context);
a.mov(r8, EXCEPTION_ILLEGAL_INSTRUCTION);
a.call_aligned(fake_exception);
a.popad64();
a.jmp(jump_target);
});
utils::hook::nop(game_address, 6);
utils::hook::jump(game_address, stub, false);
}
void patch_illegal_instruction_split(void* address)
{
const auto game_address = reinterpret_cast<std::uint64_t>(address);
const auto jump_target = utils::hook::extract<void*>(reinterpret_cast<void*>(game_address + 5));
if (*reinterpret_cast<std::uint16_t*>(jump_target) != 0x0B0F) // illegal instruction
{
return; // false positive
}
const auto a1 = *reinterpret_cast<std::uint8_t*>(game_address + 3);
_CONTEXT* fake_context = new _CONTEXT{};
const auto stub = utils::hook::assemble([address, jump_target, fake_context, a1](utils::hook::assembler& a)
{
a.lea(rax, ptr(rbp, a1));
a.push(rcx);
a.mov(rcx, fake_context);
a.call_aligned(RtlCaptureContext);
a.pop(rcx);
a.pushad64();
a.mov(rcx, address);
a.mov(rdx, fake_context);
a.mov(r8, EXCEPTION_ILLEGAL_INSTRUCTION);
a.call_aligned(fake_exception);
a.popad64();
a.jmp(jump_target);
});
utils::hook::nop(game_address, 9);
utils::hook::nop(jump_target, 2);
utils::hook::jump(game_address, stub, false);
}
#ifdef PRECOMPUTED_ILLEGAL_INSTRUCTIONS
void patch_illegal_instructions_precomputed()
{
for (const auto i : illegal_instructions_intact)
{
patch_illegal_instruction_intact(reinterpret_cast<void*>(i));
}
for (const auto i : illegal_instructions_split)
{
patch_illegal_instruction_split(reinterpret_cast<void*>(i));
}
}
#endif
void patch_illegal_instructions()
{
static bool once = false;
if (once)
{
return;
}
once = true;
#ifdef PRECOMPUTED_ILLEGAL_INSTRUCTIONS
assert(game::base_address == 0x140000000);
patch_illegal_instructions_precomputed();
#else
const auto intact_results = utils::hook::signature("48 8D 45 ? 0F 0B", game_module::get_game_module()).process();
for (auto* i : intact_results)
{
patch_illegal_instruction_intact(i);
}
const auto split_results = utils::hook::signature("48 8D 45 ? E9 ? ? ?", game_module::get_game_module()).process();
for (auto* i : split_results)
{
patch_illegal_instruction_split(i);
}
#endif
}
PVOID WINAPI add_vectored_exception_handler_stub(ULONG first, PVECTORED_EXCEPTION_HANDLER handler)
{
exceptions::patch_breakpoints();
exceptions::patch_illegal_instructions();
auto handle = AddVectoredExceptionHandler(first, handler);
handle_handler[handle] = handler;
return handle;
}
ULONG WINAPI remove_vectored_exception_handler_stub(PVOID handle)
{
handle_handler[handle] = nullptr;
return RemoveVectoredExceptionHandler(handle);
}
}
}
using namespace anti_debug;
class component final : public component_interface class component final : public component_interface
{ {
public: public:
void* load_import(const std::string& library, const std::string& function) override
{
if (function == "SetThreadContext")
{
return set_thread_context_stub;
}
else if (function == "AddVectoredExceptionHandler")
{
return exceptions::add_vectored_exception_handler_stub;
}
else if (function == "RemoveVectoredExceptionHandler")
{
return exceptions::remove_vectored_exception_handler_stub;
}
return nullptr;
}
void post_load() override void post_load() override
{ {
remove_hardware_breakpoints();
hide_being_debugged(); hide_being_debugged();
scheduler::loop(hide_being_debugged, scheduler::pipeline::async); scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
store_debug_functions();
const utils::nt::library ntdll("ntdll.dll"); const utils::nt::library ntdll("ntdll.dll");
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub); nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
@ -359,7 +674,9 @@ namespace arxan
void post_unpack() override void post_unpack() override
{ {
remove_hardware_breakpoints();
search_and_patch_integrity_checks(); search_and_patch_integrity_checks();
restore_debug_functions();
} }
component_priority priority() override component_priority priority() override

View File

@ -0,0 +1,594 @@
#pragma once
#include <cstdint>
constexpr uint64_t int2d_breakpoint_addresses[] =
{
0x14004E569,
0x140069F29,
0x1400EFEA9,
0x1400F0FC9,
0x1403CE6D9,
0x1404ECC09,
0x1404F2539,
0x140524519,
0x140A50D69,
0x140BFA4D9,
0x140C1E049,
0x140C29999,
0x140C92BBE,
0x140CC0EF9,
0x140CEFFA9,
0x140CF0D79,
0x140CF241F,
0x140CF6A91,
0x140DB85A9,
0x140F138C9,
0x140F238E9,
0x140F24A89,
0x140F31AA9,
0x140F3D1A9,
0x140F58709,
0x1411049E9,
0x14117ADD0,
0x141216534,
0x14123991E,
0x14123B17E,
0x14124DA95,
0x14125842F,
0x1412632D5,
0x141267CBF,
0x1412697E4,
0x141286872,
0x141293C7F,
0x1412990AB,
0x14129A49E,
0x141303D54,
0x14131B345,
0x1413204BE,
0x141320571,
0x14132211F,
0x141379479,
0x141398E09,
0x1413B20E9,
0x14B2E246A,
0x14B2E5CA6,
0x14B2E6C7D,
0x14B2EAF29,
0x14B2F7DA3,
0x14B2F9D2E,
0x14B34FE91,
0x14B361E9D,
0x14B36397C,
0x14B36A32D,
0x14B378CA7,
0x14B37B194,
0x14B382645,
0x14B3888A3,
0x14B39618C,
0x14B39CD0C,
0x14B3A1ECE,
0x14B3A994B,
0x14B3AC9FF,
0x14B3AE79D,
0x14B3AF313,
0x14B3B16CF,
0x14B3B65C1,
0x14B3DA767,
0x14B3DC6B1,
0x14B3DD7E8,
0x14B3E340F,
0x14B3E8680,
0x14B3ED8F6,
0x14B3F590B,
0x14B3F5A29,
0x14B3F68EB,
0x14B3FCB09,
0x14B400C56,
0x14B412247,
0x14B4170F2,
0x14B4222E3,
0x14B426354,
0x14B434EB1,
0x14B438BC3,
0x14B439E43,
0x14B43BABC,
0x14B443781,
0x14B452BE4,
0x14B455B40,
0x14B45AAF3,
0x14B45CFC6,
0x14B46708E,
0x14B46B471,
0x14B46DFE6,
0x14B47218E,
0x14B47502B,
0x14B47FF6B,
0x14B489B5D,
0x14B4901F5,
0x14B493ED2,
0x14B497074,
0x14B499ADC,
0x14B49BB2A,
0x14B49F189,
0x14B49F2AE,
0x14B49FBC0,
0x14B4A5193,
0x14B4A9530,
0x14B4AAA35,
0x14B4AABC4,
0x14B4AD6E8,
0x14B4B3B31,
0x14B4B7B3D,
0x14B4B845B,
0x14B4BB7E6,
0x14B4BCCD6,
0x14B4C1AF6,
0x14B4C4AEA,
0x14B4C5B91,
0x14B4CCA65,
0x14B4D0F9C,
0x14B4D44D0,
0x14B4D7423,
0x14B5262AD,
0x14B52655E,
0x14B528DF2,
0x14B52BC8B,
0x14B53142D,
0x14B538901,
0x14B54520F,
0x14B5462B2,
0x14B5466A6,
0x14B54CF35,
0x14B54D44B,
0x14B550353,
0x14B555F0C,
0x14B55678C,
0x14B55F494,
0x14B563618,
0x14B564B29,
0x14B565CB9,
0x14B566DF3,
0x14B58085C,
0x14B58FA48,
0x14B59229B,
0x14B5944E4,
0x14B5A2E29,
0x14B5A3269,
0x14B5B05BF,
0x14B5B3D1B,
0x14B5BE2D9,
0x14B5C0A85,
0x14B5C32CF,
0x14B5C50F2,
0x14B5C67CD,
0x14B5C92EB,
0x14B5C9717,
0x14B5CE3A6,
0x14B5CEEE3,
0x14B626123,
0x14B626553,
0x14B633A3E,
0x14B63DF91,
0x14B648C4A,
0x14B64D55C,
0x14B64E48A,
0x14B6539B4,
0x14B666AC3,
0x14B66AD46,
0x14B66C669,
0x14B66CE05,
0x14B672AD5,
0x14B67EDC1,
0x14B68048C,
0x14B681119,
0x14B683347,
0x14B68691B,
0x14B6888BA,
0x14B691973,
0x14B6945F4,
0x14B7348B6,
0x14B73666A,
0x14B7398B2,
0x14B73E42F,
0x14B741BA5,
0x14B742DE8,
0x14B745CFB,
0x14B74827A,
0x14B74A230,
0x14B75124E,
0x14B751691,
0x14B760F53,
0x14B77032D,
0x14B7706AE,
0x14B778A13,
0x14B7794AF,
0x14B77BBFB,
0x14B785745,
0x14B788D32,
0x14B78DE47,
0x14B7910C9,
0x14B795294,
0x14B7A0DAB,
0x14B7A3B42,
0x14B7AC060,
0x14B7B5DA8,
0x14B7B64E5,
0x14B7BC9BF,
0x14B7BDB7E,
0x14B7BE77E,
0x14B7C5313,
0x14B7C8071,
0x14B7CA6E4,
0x14B7D3385,
0x14B7D43DD,
0x14B7D4C57,
0x14B7DD2AF,
0x14B7DE8C4,
0x14B7DED66,
0x14B7EE457,
0x14B7F5322,
0x14B7F6837,
0x14B7FDD66,
0x14B7FDFEE,
0x14B805299,
0x14B8052A0,
0x14B807827,
0x14B80A30B,
0x14B810D85,
0x14B81282F,
0x14B818622,
0x14B81C166,
0x14B820555,
0x14B8288BE,
0x14B86E751,
0x14B8760A6,
0x14B87D6FD,
0x14B8857AA,
0x14B889598,
0x14B894D05,
0x14B895BB9,
0x14B895BDA,
0x14B8967F2,
0x14B8E5641,
0x14B8E61D8,
0x14B8E6D08,
0x14B8E9516,
0x14B8E9784,
0x14B900442,
0x14B903DA2,
0x14B9518C3,
0x14B959CA1,
0x14B95DE5B,
0x14B9609C3,
0x14B963A1C,
0x14B9B86AE,
0x14B9C0892,
0x14B9C94CA,
0x14B9CAAB3,
0x14B9CD2F7,
0x14B9CE417,
0x14B9CFD20,
0x14B9DD84E,
0x14B9E31E7,
0x14B9E57B1,
0x14B9F1CB9,
0x14B9FFF1C,
0x14BA001F0,
0x14BA037EA,
0x14BA0787E,
0x14BA08886,
0x14BA10AEA,
0x14BA18A56,
0x14BA2368D,
0x14BA2454B,
0x14BA27998,
0x14BA281CE,
0x14BA2A34D,
0x14BA2A5FA,
0x14BA394BE,
0x14BA402BC,
0x14BA46451,
0x14BA4F657,
0x14BA50C3C,
0x14BA57A6A,
0x14BA5D5A0,
0x14BA6A7B2,
0x14BA76FA1,
0x14BA77D4E,
0x14BA7AA42,
0x14BA7AA8A,
0x14BA8640B,
0x14BA8E903,
0x14BA97CE7,
0x14BA98908,
0x14BAA1B64,
0x14BAA3F78,
0x14BAA4F8A,
0x14BAA7DC2,
0x14BAB299E,
0x14BAB3DCD,
0x14BAC35D6,
0x14BACBEB4,
0x14BACDE69,
0x14BAD7967,
0x14BADA669,
0x14BAF6257,
0x14BAFA589,
0x14BAFB6BF,
0x14BAFF6C5,
0x14BB007C0,
0x14BB039F2,
0x14BB04882,
0x14BB09346,
0x14BB15868,
0x14BB1CA96,
0x14BB1E48A,
0x14BB1FC5A,
0x14BB21979,
0x14BB21BDE,
0x14BB242AF,
0x14BB24927,
0x14BB2C7B0,
0x14BB30EF4,
0x14BB37799,
0x14BB3AA05,
0x14BB423B2,
0x14BB42F13,
0x14BB44F93,
0x14BB45129,
0x14BB48C26,
0x14BB4BD8D,
0x14BB4C498,
0x14BB4D00D,
0x14BB4FC00,
0x14BB50E8D,
0x14BB57FFB,
0x14BB60E63,
0x14BB64D83,
0x14BB67396,
0x14BB69F17,
0x14BB6ACCF,
0x14BB70B4D,
0x14BB714FE,
0x14BB7859D,
0x14BB82EFF,
0x14BB86019,
0x14BB88749,
0x14BB89919,
0x14BB8D7CF,
0x14BC0B6BF,
0x14BC0D671,
0x14BC34710,
0x14BC35765,
0x14BC38700,
0x14BC3941F,
0x14BC3A326,
0x14BC3B873,
0x14BC3CD6F,
0x14BC3CE22,
0x14BC3F705,
0x14BC4C130,
0x14BC4CDE6,
0x14BC54F57,
0x14BC56C46,
0x14BC5CB4A,
0x14BC656DB,
0x14BC657BA,
0x14BC68F18,
0x14BCB6BFA,
0x14BCBC601,
0x14BCBCBCA,
0x14BCBD596,
0x14BCC86C1,
0x14BCCC17D,
0x14BCD2059,
0x14BCD6A36,
0x14BCD9291,
0x14BCDDFD8,
0x14BCE5FC2,
0x14BCE7E55,
0x14BCE8BE4,
0x14BCE9ACF,
0x14BCF105A,
0x14BCF12D1,
0x14BCFA39E,
0x14BCFAEB5,
0x14BCFB339,
0x14BCFB394,
0x14BD04A53,
0x14BD0C09B,
0x14BD0F968,
0x14BD7184A,
0x14BDB8CD4,
0x14BDBB690,
0x14BDBC17B,
0x14BDC1732,
0x14BDC27BF,
0x14BDC3213,
0x14BDC3A6A,
0x14BDD1829,
0x14BDD3732,
0x14BDD9F03,
0x14BDDA3E0,
0x14BDDD6C6,
0x14BDE6A8D,
0x14BDE7DD3,
0x14BDE92BA,
0x14BDEA25C,
0x14BDF0034,
0x14BDFAEED,
0x14BDFC26C,
0x14BE00996,
0x14BE00F6C,
0x14BE02A7C,
0x14BE083B2,
0x14BE11768,
0x14BE18E54,
0x14BE1F482,
0x14BE21D97,
0x14BE3098D,
0x14BE5EB08,
0x14BE64E8C,
0x14BE65AF8,
0x14BE68B8D,
0x14BE6EB30,
0x14BE7045A,
0x14BE7433D,
0x14BE7AF38,
0x14BE7F377,
0x14BE89D93,
0x14BE9BC71,
0x14BE9E2B4,
0x14BEA5374,
0x14BEA6F65,
0x14BEAE8B0,
0x14BEB8C2A,
0x14BEB9450,
0x14BEC31BD,
0x14BEC58BE,
0x14BECAC7E,
0x14BED17B6,
0x14BED1BFB,
0x14BED54A0,
0x14BEDBC7B,
0x14BEE5679,
0x14BEE9824,
0x14BEEAA42,
0x14BEEACC5,
0x14BEEC39B,
0x14BEEC3E0,
0x14BEF214D,
0x14BEF5CEC,
0x14BF000AB,
0x14BF08BE3,
0x14BF19973,
0x14BF19BA9,
0x14BF2A59B,
0x14BF2F52A,
0x14BF30011,
0x14BF32866,
0x14BF35803,
0x14BF39362,
0x14BF3F92F,
0x14BF438DD,
0x14BF43DB9,
0x14BF49549,
0x14BF4BB7C,
0x14BF4C8A8,
0x14BF5287A,
0x14BF5A0A4,
0x14BF5B25B,
0x14BF5EF0E,
0x14BF73CDB,
0x14BF784AA,
0x14BF7C581,
0x14BF7DB1A,
0x14BF82743,
0x14BF84963,
0x14BF8B19D,
0x14BF8D2CF,
0x14BF91994,
0x14BF950CE,
0x14BFA5FA8,
0x14BFA68E6,
0x14BFB30E3,
0x14BFB4F69,
0x14BFB553D,
0x14BFB840E,
0x14BFB9D7E,
0x14BFBEB33,
0x14BFC5E0B,
0x14BFC6017,
0x14BFC70DE,
0x14BFC8FE9,
0x14BFD4EDD,
0x14BFD8230,
0x14BFDCEE5,
0x14BFDE795,
0x14BFDEB06,
0x14BFDF4FE,
0x14C006DA8,
0x14C00990A,
0x14C016FA5,
0x14C01980C,
0x14C0698CB,
0x14C071FFA,
0x14C078EC8,
0x14C07BBC7,
0x14C07D3EB,
0x14C07E695,
0x14C081960,
0x14C089ED2,
0x14C09F709,
0x14C0A80E7,
0x14C0AA53F,
0x14C0AB4AC,
0x14C0AE170,
0x14C0AEB55,
0x14C0B0093,
0x14C0B09AC,
0x14C0BB106,
0x14C0BE1CC,
0x14C0C5EE4,
0x14C0C63E8,
0x14C0D2894,
0x14C0D85DD,
0x14C0D926E,
0x14C0E82C0,
0x14C0EDDD2,
0x14C0F7CB3,
0x14C0F934E,
0x14C0FA838,
0x14C0FAF00,
0x14C0FD876,
0x14C101924,
0x14C104713,
0x14C1051C2,
0x14C1074D9,
0x14C10863A,
0x14C10A51B,
0x14C11092F,
0x14C170C7A,
0x14C1737F8,
0x14C17A37B,
0x14C18332C,
0x14C1E0E9E,
0x14C1E2A39,
0x14C1E3E7C,
0x14C1E67C0,
0x14C1E9AA8,
0x14C1EBA01,
0x14C1EE31F,
0x14C1F18E4,
0x14C1F3B80,
0x14C1F5E6E,
0x14C1F5F75,
0x14C1FAE4D,
0x14C1FC26A,
0x14C1FC625,
0x14C1FD553,
0x14C200AE5,
0x14C202042,
0x14C20701C,
0x14C20E0CF,
0x14C20F131,
0x14C231030,
0x14C23C17C,
0x14C23E7D8,
0x14C23FBBD,
0x14C2415E9,
0x14C2440CC,
0x14C24AFA0,
0x14C24B0B1,
0x14C24B99B,
0x14C250711,
0x14C252ACF,
0x14C25C0D5,
0x14C265061,
0x14C26A289,
0x14C27695B,
0x14C277EBA,
0x14C27B2F5,
0x14C27B803,
};

View File

@ -0,0 +1,597 @@
#pragma once
#include <cstdint>
constexpr uint64_t illegal_instructions_intact[] =
{
0x14123DEDC,
0x141261C7F,
0x141277D40,
0x1414020BD,
0x14140F61E,
0x14B2B6065,
0x14B2B9225,
0x14B2C38FC,
0x14B2C5FA2,
0x14B2CA78B,
0x14B2CF482,
0x14B2D653C,
0x14B2D8CF0,
0x14B2DA6D2,
0x14B2EB8FB,
0x14B2ECBEE,
0x14B2F376B,
0x14B2F6306,
0x14B2F88D3,
0x14B2FDF3A,
0x14B3508DC,
0x14B351B0F,
0x14B356014,
0x14B3573EF,
0x14B35CACF,
0x14B35E76E,
0x14B36757B,
0x14B36AEA0,
0x14B36E087,
0x14B372670,
0x14B372744,
0x14B375729,
0x14B3790EE,
0x14B379F34,
0x14B37CBC3,
0x14B37E46C,
0x14B3832F7,
0x14B389DE5,
0x14B390B46,
0x14B39A032,
0x14B39C874,
0x14B3A7879,
0x14B3A9BCC,
0x14B3AC660,
0x14B3AE8A8,
0x14B3AF005,
0x14B3C02E1,
0x14B3C2DC7,
0x14B3DAD67,
0x14B3DD92D,
0x14B3E49E1,
0x14B3EBB18,
0x14B3F156E,
0x14B3F3B2E,
0x14B3F6A1F,
0x14B3FC8EE,
0x14B3FE5C0,
0x14B3FFE0C,
0x14B405116,
0x14B40CD15,
0x14B40F8BA,
0x14B4136C4,
0x14B41539C,
0x14B41B882,
0x14B41C7D5,
0x14B424303,
0x14B428436,
0x14B42B8DC,
0x14B42DD0B,
0x14B42E1CA,
0x14B43A6D8,
0x14B43CB43,
0x14B4439FA,
0x14B44510E,
0x14B44528D,
0x14B44E9E5,
0x14B454C97,
0x14B455538,
0x14B475359,
0x14B47C92C,
0x14B488696,
0x14B489298,
0x14B489F2E,
0x14B48AF9D,
0x14B48B5B8,
0x14B48F441,
0x14B48FC17,
0x14B493289,
0x14B49581A,
0x14B4991B9,
0x14B4A0737,
0x14B4A6FBB,
0x14B4A7476,
0x14B4ACE09,
0x14B4B1A6A,
0x14B4C0500,
0x14B4C3A73,
0x14B4C8DD0,
0x14B4D9639,
0x14B529163,
0x14B52EB48,
0x14B52EF82,
0x14B53B5E3,
0x14B53C40E,
0x14B54466A,
0x14B554BBD,
0x14B583C3A,
0x14B587749,
0x14B587E86,
0x14B588BFD,
0x14B58EE98,
0x14B58FCC1,
0x14B59023F,
0x14B5924FB,
0x14B595DFC,
0x14B596B2A,
0x14B596E49,
0x14B5A0B4A,
0x14B5A235A,
0x14B5A5D52,
0x14B5AF2AA,
0x14B5B0446,
0x14B5C8698,
0x14B5CD955,
0x14B62639F,
0x14B62F321,
0x14B6339FC,
0x14B63D66C,
0x14B641A18,
0x14B642607,
0x14B64D09C,
0x14B655A50,
0x14B65A426,
0x14B65D7D2,
0x14B6666C4,
0x14B66757B,
0x14B66C177,
0x14B676E80,
0x14B6779A8,
0x14B67DEA9,
0x14B68C499,
0x14B68F544,
0x14B6E8817,
0x14B7373C5,
0x14B75DEB9,
0x14B7630CC,
0x14B769313,
0x14B772EBD,
0x14B77A2FE,
0x14B7858E4,
0x14B7878C2,
0x14B787DD7,
0x14B791100,
0x14B791552,
0x14B792E99,
0x14B7A0DEF,
0x14B7B8930,
0x14B7CA91A,
0x14B7CAAD8,
0x14B7D1BBA,
0x14B7DE6FD,
0x14B7E06B3,
0x14B7E0F97,
0x14B7E19D9,
0x14B7E7A48,
0x14B7E8BC1,
0x14B7E9741,
0x14B7EBD12,
0x14B807B6B,
0x14B80C398,
0x14B80C3C1,
0x14B80F98A,
0x14B8104BA,
0x14B81287C,
0x14B818115,
0x14B818710,
0x14B81DFBE,
0x14B81E350,
0x14B81F381,
0x14B825255,
0x14B829DD8,
0x14B8754DF,
0x14B885BE9,
0x14B885F36,
0x14B8896FC,
0x14B88E976,
0x14B8958FA,
0x14B8EAF6C,
0x14B8FEB01,
0x14B9007C0,
0x14B95AACD,
0x14B95E17B,
0x14B95EC92,
0x14B960817,
0x14B9636B4,
0x14B9696F0,
0x14B9B4551,
0x14B9B82BE,
0x14B9B8F4D,
0x14B9BB089,
0x14B9CB1DB,
0x14B9D5DED,
0x14B9DB649,
0x14B9E60CD,
0x14B9E6C9B,
0x14B9E9928,
0x14B9EFD44,
0x14BA019AD,
0x14BA01D70,
0x14BA058BC,
0x14BA09EC5,
0x14BA11B00,
0x14BA17BDC,
0x14BA196A3,
0x14BA1988A,
0x14BA35064,
0x14BA35B31,
0x14BA36204,
0x14BA3F03C,
0x14BA47016,
0x14BA4F49B,
0x14BA50611,
0x14BA64BB9,
0x14BA64F6A,
0x14BA6A610,
0x14BA6A814,
0x14BA6C8D3,
0x14BA72980,
0x14BA73097,
0x14BA74956,
0x14BA7B5F3,
0x14BA7DE77,
0x14BA81C1F,
0x14BA85CC8,
0x14BA91111,
0x14BA941CF,
0x14BA9B7EF,
0x14BAB13A3,
0x14BAB660C,
0x14BAC4E16,
0x14BAC662B,
0x14BACCD50,
0x14BAD3D3B,
0x14BAD6528,
0x14BAE6240,
0x14BAE6C5C,
0x14BAE782D,
0x14BAE8DB7,
0x14BAECE69,
0x14BAEFD3C,
0x14BAF3A44,
0x14BB017BF,
0x14BB06F08,
0x14BB18982,
0x14BB1A7EE,
0x14BB1B1C7,
0x14BB1F475,
0x14BB25D17,
0x14BB27982,
0x14BB38329,
0x14BB3FA72,
0x14BB43CBB,
0x14BB46A23,
0x14BB5391B,
0x14BB581D4,
0x14BB5FBBB,
0x14BB61611,
0x14BB63931,
0x14BB700CE,
0x14BB82A27,
0x14BB82C0A,
0x14BB89003,
0x14BB893CD,
0x14BC03B2D,
0x14BC041E8,
0x14BC05287,
0x14BC086B4,
0x14BC15749,
0x14BC1E58A,
0x14BC3EC23,
0x14BC436EF,
0x14BC48195,
0x14BC56497,
0x14BC5755D,
0x14BC57903,
0x14BC5A62E,
0x14BC5C62F,
0x14BC66A22,
0x14BCB7AE9,
0x14BCBF793,
0x14BCCAD84,
0x14BCDA738,
0x14BCEB884,
0x14BCEF45F,
0x14BCF3834,
0x14BD016D1,
0x14BD057AC,
0x14BD05859,
0x14BDB7EB7,
0x14BDBA1DB,
0x14BDBBA36,
0x14BDBE93F,
0x14BDC19E4,
0x14BDC3E21,
0x14BDC7BBF,
0x14BDCBE79,
0x14BDCF71D,
0x14BDD3CBD,
0x14BDD5AF5,
0x14BDDAA78,
0x14BDDB1F5,
0x14BDE0244,
0x14BDE6255,
0x14BDF1556,
0x14BDF4A3D,
0x14BDF608A,
0x14BDFB68F,
0x14BDFFE41,
0x14BE07690,
0x14BE0B269,
0x14BE0D169,
0x14BE1193F,
0x14BE11F1D,
0x14BE18C23,
0x14BE24E2A,
0x14BE2896A,
0x14BE2A7CF,
0x14BE2B1CB,
0x14BE2E4F7,
0x14BE32D39,
0x14BE351F2,
0x14BE60CDD,
0x14BE63E75,
0x14BE6EF30,
0x14BE71DB2,
0x14BE7AE7A,
0x14BE7E465,
0x14BE8024B,
0x14BE84254,
0x14BE8D471,
0x14BE90644,
0x14BE94ACA,
0x14BE95A75,
0x14BEA14E0,
0x14BEA5728,
0x14BEA5792,
0x14BEAAAAF,
0x14BEAD940,
0x14BEB407B,
0x14BEB71BF,
0x14BEC88F7,
0x14BECF014,
0x14BED66AB,
0x14BEE4E49,
0x14BEE712B,
0x14BEE8115,
0x14BEECA23,
0x14BEF822E,
0x14BEFE92E,
0x14BF032D0,
0x14BF086A4,
0x14BF0D790,
0x14BF1084D,
0x14BF27B4B,
0x14BF2BE6F,
0x14BF2ECCD,
0x14BF33217,
0x14BF3C027,
0x14BF3E0CA,
0x14BF3F3C5,
0x14BF40872,
0x14BF40EA6,
0x14BF411F3,
0x14BF51243,
0x14BF541A5,
0x14BF58A0A,
0x14BF5A721,
0x14BF5F9E0,
0x14BF7AC17,
0x14BF7BDBB,
0x14BF7DBB4,
0x14BF806B0,
0x14BF86481,
0x14BF8B563,
0x14BF8CE7E,
0x14BF98757,
0x14BFA3C60,
0x14BFAEEBA,
0x14BFB0E9D,
0x14BFB59DF,
0x14BFBDB2F,
0x14BFBF43D,
0x14BFC2764,
0x14BFD674C,
0x14BFE320E,
0x14BFE4D2B,
0x14BFE9D7F,
0x14C006433,
0x14C007B55,
0x14C008A99,
0x14C0092EC,
0x14C00943D,
0x14C00B792,
0x14C012837,
0x14C012AC3,
0x14C01675E,
0x14C0683E1,
0x14C06AEED,
0x14C07BED8,
0x14C07FE7D,
0x14C084D2D,
0x14C088436,
0x14C09466C,
0x14C0AE879,
0x14C0B4C36,
0x14C0BC243,
0x14C0C18D6,
0x14C0CC8FC,
0x14C0D4EF1,
0x14C0EC01A,
0x14C0ED2AD,
0x14C0EDE92,
0x14C0EE6A3,
0x14C0F5DA6,
0x14C0F654C,
0x14C1030BF,
0x14C17320D,
0x14C177DC3,
0x14C1850A3,
0x14C185BA9,
0x14C186C27,
0x14C188C99,
0x14C18CE23,
0x14C1D51B5,
0x14C1D6246,
0x14C1DCB9A,
0x14C1E3B52,
0x14C1E9C40,
0x14C1ED862,
0x14C1F4B92,
0x14C1F6575,
0x14C20357C,
0x14C20B745,
0x14C216876,
0x14C21ACF7,
0x14C21EF99,
0x14C2368F0,
0x14C237C1B,
0x14C2395B1,
0x14C23ECD9,
0x14C23EEEA,
0x14C24F487,
0x14C25172C,
0x14C255A80,
0x14C25E1BB,
0x14C25FA75,
0x14C263109,
0x14C265E7E,
0x14C269700,
0x14C2723B5,
0x14C275977,
0x14C279104,
0x14C27964C,
};
constexpr uint64_t illegal_instructions_split[] =
{
0x1412522EB,
0x14B2C77C4,
0x14B2D62AD,
0x14B2E71D4,
0x14B2E7713,
0x14B2F5C52,
0x14B35F6C1,
0x14B361A7F,
0x14B367251,
0x14B39AB42,
0x14B3ACABD,
0x14B3F7EAF,
0x14B406441,
0x14B4196E5,
0x14B41D19F,
0x14B4322A5,
0x14B43ACFB,
0x14B441015,
0x14B445DFF,
0x14B462EFE,
0x14B47FA01,
0x14B49BF69,
0x14B49D7D3,
0x14B4B31CE,
0x14B4C8EB8,
0x14B4CC306,
0x14B543258,
0x14B57B9A3,
0x14B597BED,
0x14B5A4691,
0x14B5C07B5,
0x14B5D51CC,
0x14B642ADA,
0x14B651DE6,
0x14B656F57,
0x14B68A4D4,
0x14B6E48EA,
0x14B73FCF4,
0x14B747FEC,
0x14B767C87,
0x14B76C67B,
0x14B7718EB,
0x14B792301,
0x14B7B8912,
0x14B7BF5EE,
0x14B7E6951,
0x14B7F603C,
0x14B804F0C,
0x14B818EE5,
0x14B86D440,
0x14B87F411,
0x14B884B33,
0x14B8F01F1,
0x14B90065F,
0x14B961290,
0x14B9B1CCE,
0x14B9C8637,
0x14B9D5D04,
0x14BA08255,
0x14BA1C344,
0x14BA2C491,
0x14BA628A5,
0x14BAA45B9,
0x14BAAFFC1,
0x14BAC036C,
0x14BAC09AB,
0x14BAC7C55,
0x14BAD7547,
0x14BAF3B1E,
0x14BB692E3,
0x14BB7AB76,
0x14BB800A1,
0x14BC0930A,
0x14BC34164,
0x14BC5965F,
0x14BC6152C,
0x14BCBD74A,
0x14BCC4BCB,
0x14BCC7D79,
0x14BCC7D8B,
0x14BCD6F46,
0x14BCF3B77,
0x14BCF49BF,
0x14BD05004,
0x14BDC7020,
0x14BDC7C68,
0x14BDD4DF5,
0x14BE09E85,
0x14BE17031,
0x14BE2C8A7,
0x14BE530BB,
0x14BE806DF,
0x14BE8D36C,
0x14BEBDBB6,
0x14BECB9D6,
0x14BED4F0F,
0x14BEEC558,
0x14BEFB5D8,
0x14BF2FC57,
0x14BF56768,
0x14BF58CCA,
0x14BF7E2ED,
0x14BF88EC2,
0x14BFBECE9,
0x14BFC9530,
0x14BFCBF54,
0x14C098550,
0x14C098F31,
0x14C0A22BD,
0x14C0BCDBD,
0x14C0C80D2,
0x14C173C79,
0x14C1D498D,
0x14C1F19C9,
0x14C1F3A1F,
0x14C1F6D2B,
0x14C1F7093,
0x14C235640,
0x14C241065,
0x14C244C7B,
0x14C24EDC6,
0x14C2615B0,
0x14C262DC0,
0x14C26EA5A,
};

View File

@ -4,6 +4,7 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/scripting/execution.hpp"
#include "console/console.hpp" #include "console/console.hpp"
#include "game_console.hpp" #include "game_console.hpp"
@ -194,34 +195,105 @@ namespace command
return 0; return 0;
} }
void client_println(int client_num, const std::string& text) void cmd_give(const int client_num, const std::vector<std::string>& params)
{ {
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP) if (params.size() < 2)
{ {
game::CG_Utils_GameMessage(client_num, text.data(), 0); // why is nothing printed? game::shared::client_println(client_num, "You did not specify a weapon name");
return;
} }
else
try
{
const auto& arg = params[1];
const auto player = scripting::entity({ static_cast<uint16_t>(client_num), 0 });
if (arg == "ammo")
{
const auto weapon = player.call("getcurrentweapon").as<std::string>();
player.call("givemaxammo", { weapon });
}
else if (arg == "allammo")
{
const auto weapons = player.call("getweaponslistall").as<scripting::array>();
for (auto i = 0; i < weapons.size(); i++)
{
player.call("givemaxammo", { weapons[i] });
}
}
else if (arg == "health")
{
if (params.size() > 2)
{
const auto amount = atoi(params[2].data());
const auto health = player.get("health").as<int>();
player.set("health", { health + amount });
}
else
{
const auto amount = atoi(game::Dvar_FindVar("scr_player_maxhealth")->current.string);
player.set("health", { amount });
}
}
else if (arg == "all")
{
const auto type = game::XAssetType::ASSET_TYPE_WEAPON;
game::DB_EnumXAssets(type, [&player, type](const game::XAssetHeader header)
{
const auto asset = game::XAsset{ type, header };
const auto asset_name = game::DB_GetXAssetName(&asset);
player.call("giveweapon", { asset_name });
});
}
else
{
player.call("giveweapon", { arg });
}
}
catch (...)
{ {
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"%s\"", text.data()));
} }
} }
bool cheats_ok(int client_num) void cmd_drop_weapon(int client_num)
{ {
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP) try
{ {
return true; const auto player = scripting::entity({ static_cast<uint16_t>(client_num), 0 });
const auto weapon = player.call("getcurrentweapon");
player.call("dropitem", { weapon });
}
catch (...)
{
}
}
void cmd_take(int client_num, const std::vector<std::string>& params)
{
if (params.size() < 2)
{
game::shared::client_println(client_num, "You did not specify a weapon name");
return;
} }
const auto sv_cheats = game::Dvar_FindVar("sv_cheats"); const auto& weapon = params[1];
if (!sv_cheats || !sv_cheats->current.enabled)
{
client_println(client_num, "GAME_CHEATSNOTENABLED");
return false;
}
return true; try
{
const auto player = scripting::entity({ static_cast<uint16_t>(client_num), 0 });
if (weapon == "all"s)
{
player.call("takeallweapons");
}
else
{
player.call("takeweapon", { weapon });
}
}
catch (...)
{
}
} }
} }
@ -416,13 +488,13 @@ namespace command
add_sv("god", [](const int client_num, const params_sv&) add_sv("god", [](const int client_num, const params_sv&)
{ {
if (!cheats_ok(client_num)) if (!game::shared::cheats_ok(client_num, true))
{ {
return; return;
} }
game::g_entities[client_num].flags ^= 1; game::g_entities[client_num].flags ^= 1;
client_println(client_num, game::shared::client_println(client_num,
game::g_entities[client_num].flags & 1 game::g_entities[client_num].flags & 1
? "GAME_GODMODE_ON" ? "GAME_GODMODE_ON"
: "GAME_GODMODE_OFF"); : "GAME_GODMODE_OFF");
@ -430,13 +502,13 @@ namespace command
add_sv("demigod", [](const int client_num, const params_sv&) add_sv("demigod", [](const int client_num, const params_sv&)
{ {
if (!cheats_ok(client_num)) if (!game::shared::cheats_ok(client_num, true))
{ {
return; return;
} }
game::g_entities[client_num].flags ^= 2; game::g_entities[client_num].flags ^= 2;
client_println(client_num, game::shared::client_println(client_num,
game::g_entities[client_num].flags & 2 game::g_entities[client_num].flags & 2
? "GAME_DEMI_GODMODE_ON" ? "GAME_DEMI_GODMODE_ON"
: "GAME_DEMI_GODMODE_OFF"); : "GAME_DEMI_GODMODE_OFF");
@ -444,13 +516,13 @@ namespace command
add_sv("notarget", [](const int client_num, const params_sv&) add_sv("notarget", [](const int client_num, const params_sv&)
{ {
if (!cheats_ok(client_num)) if (!game::shared::cheats_ok(client_num, true))
{ {
return; return;
} }
game::g_entities[client_num].flags ^= 4; game::g_entities[client_num].flags ^= 4;
client_println(client_num, game::shared::client_println(client_num,
game::g_entities[client_num].flags & 4 game::g_entities[client_num].flags & 4
? "GAME_NOTARGETON" ? "GAME_NOTARGETON"
: "GAME_NOTARGETOFF"); : "GAME_NOTARGETOFF");
@ -458,13 +530,13 @@ namespace command
add_sv("noclip", [](const int client_num, const params_sv&) add_sv("noclip", [](const int client_num, const params_sv&)
{ {
if (!cheats_ok(client_num)) if (!game::shared::cheats_ok(client_num, true))
{ {
return; return;
} }
game::g_entities[client_num].client->flags ^= 1; game::g_entities[client_num].client->flags ^= 1;
client_println(client_num, game::shared::client_println(client_num,
game::g_entities[client_num].client->flags & 1 game::g_entities[client_num].client->flags & 1
? "GAME_NOCLIPON" ? "GAME_NOCLIPON"
: "GAME_NOCLIPOFF"); : "GAME_NOCLIPOFF");
@ -472,17 +544,47 @@ namespace command
add_sv("ufo", [](const int client_num, const params_sv&) add_sv("ufo", [](const int client_num, const params_sv&)
{ {
if (!cheats_ok(client_num)) if (!game::shared::cheats_ok(client_num, true))
{ {
return; return;
} }
game::g_entities[client_num].client->flags ^= 2; game::g_entities[client_num].client->flags ^= 2;
client_println(client_num, game::shared::client_println(client_num,
game::g_entities[client_num].client->flags & 2 game::g_entities[client_num].client->flags & 2
? "GAME_UFOON" ? "GAME_UFOON"
: "GAME_UFOOFF"); : "GAME_UFOOFF");
}); });
add_sv("give", [](const int client_num, const params_sv& params)
{
if (!game::shared::cheats_ok(client_num, true))
{
return;
}
cmd_give(client_num, params.get_all());
});
add_sv("dropweapon", [](const int client_num, const params_sv& params)
{
if (!game::shared::cheats_ok(client_num, true))
{
return;
}
cmd_drop_weapon(client_num);
});
add_sv("take", [](const int client_num, const params_sv& params)
{
if (!game::shared::cheats_ok(client_num, true))
{
return;
}
cmd_take(client_num, params.get_all());
});
} }
}; };
} }

View File

@ -341,7 +341,8 @@ namespace terminal
public: public:
component() component()
{ {
ShowWindow(GetConsoleWindow(), SW_HIDE); if(!game::environment::is_dedi())
ShowWindow(GetConsoleWindow(), SW_HIDE);
} }
void post_unpack() override void post_unpack() override

View File

@ -8,6 +8,7 @@
#include "command.hpp" #include "command.hpp"
#include "console/console.hpp" #include "console/console.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "filesystem.hpp"
#include <utils/json.hpp> #include <utils/json.hpp>
@ -111,8 +112,6 @@ namespace dedicated
} }
}; };
command::execute("onlinegame 1", true);
command::execute("xblive_privatematch 1", true);
initialize_gamemode(); initialize_gamemode();
} }
@ -121,7 +120,7 @@ namespace dedicated
nlohmann::json get_snd_alias_length_data(const char* mapname, const std::string& game_mode = "") nlohmann::json get_snd_alias_length_data(const char* mapname, const std::string& game_mode = "")
{ {
const auto path = "sounddata/"s + game_mode + "/"s + mapname + ".json"s; const auto path = "sounddata/"s + game_mode + "/"s + mapname + ".json"s;
const auto buffer = utils::io::read_file(path); const auto buffer = filesystem::read_file(path);
if (!buffer.empty()) if (!buffer.empty())
{ {
try try
@ -153,6 +152,7 @@ namespace dedicated
} }
else else
{ {
//console::error("[SND]: failed to find sound length soundalias \"%s\"\n", alias);
return 0; return 0;
} }
} }
@ -233,8 +233,6 @@ namespace dedicated
utils::hook::nop(0x34296F_b, 2); // ^ utils::hook::nop(0x34296F_b, 2); // ^
utils::hook::set<uint8_t>(0xE08360_b, 0xC3); // don't shutdown renderer utils::hook::set<uint8_t>(0xE08360_b, 0xC3); // don't shutdown renderer
utils::hook::set<uint8_t>(0xC5A200_b, 0xC3); // disable host migration
// SOUND patches // SOUND patches
//utils::hook::nop(0xC93213_b, 5); // snd stream thread //utils::hook::nop(0xC93213_b, 5); // snd stream thread
//utils::hook::set<uint8_t>(0xC93206_b, 0); // snd_active //utils::hook::set<uint8_t>(0xC93206_b, 0); // snd_active

View File

@ -69,8 +69,7 @@ namespace dvar_cheats
return false; return false;
} }
const auto sv_cheats = game::Dvar_FindVar("sv_cheats"); if ((dvar->flags & game::DvarFlags::DVAR_FLAG_CHEAT) && (!game::shared::cheats_ok() && *game::isCheatOverride))
if ((dvar->flags & game::DvarFlags::DVAR_FLAG_CHEAT) && ((sv_cheats && !sv_cheats->current.enabled) && *game::isCheatOverride))
{ {
//#ifdef DEBUG //#ifdef DEBUG
console::error("%s is cheat protected\n", dvars::dvar_get_name(dvar).data()); console::error("%s is cheat protected\n", dvars::dvar_get_name(dvar).data());

View File

@ -10,6 +10,9 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include <utils/io.hpp>
#include <zlib.h>
//#define XFILE_DEBUG //#define XFILE_DEBUG
@ -33,6 +36,7 @@ namespace fastfiles
utils::hook::detour db_init_load_x_file_hook; utils::hook::detour db_init_load_x_file_hook;
utils::hook::detour db_load_x_zone_hook; utils::hook::detour db_load_x_zone_hook;
utils::hook::detour db_find_xasset_header_hook; utils::hook::detour db_find_xasset_header_hook;
utils::hook::detour db_add_xasset_hook;
void db_try_load_x_file_internal_stub(const char* zone_name, const unsigned int zone_flags, void db_try_load_x_file_internal_stub(const char* zone_name, const unsigned int zone_flags,
const bool is_base_map, const bool was_paused, const int failure_mode) const bool is_base_map, const bool was_paused, const int failure_mode)
@ -56,6 +60,28 @@ namespace fastfiles
return db_load_x_zone_hook.invoke<void>(parent_name, zone_flags, is_base_map, failure_mode); return db_load_x_zone_hook.invoke<void>(parent_name, zone_flags, is_base_map, failure_mode);
} }
game::dvar_t* g_dump_scripts;
void dump_gsc_script(const std::string& name, game::XAssetHeader header)
{
if (!g_dump_scripts->current.enabled)
{
return;
}
std::string buffer;
buffer.append(header.scriptfile->name, strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->len), 4);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->bytecodeLen), 4);
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(header.scriptfile->bytecode, header.scriptfile->bytecodeLen);
const auto out_name = utils::string::va("gsc_dump/%s.gscbin", name.data());
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name);
}
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, const int allow_create_default) game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, const int allow_create_default)
{ {
auto result = db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default); auto result = db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
@ -65,10 +91,76 @@ namespace fastfiles
game::g_assetNames[static_cast<unsigned int>(type)], game::g_assetNames[static_cast<unsigned int>(type)],
name); name);
} }
return result;
}
game::XAssetHeader db_add_xasset_stub(game::XAssetType type, game::XAssetHeader* header_ptr)
{
auto header = *header_ptr;
if (type == game::ASSET_TYPE_SCRIPTFILE && header.scriptfile)
{
dump_gsc_script(header.scriptfile->name ? header.scriptfile->name : "__unnamed__", header);
}
auto result = db_add_xasset_hook.invoke<game::XAssetHeader>(type, header_ptr);
return result; return result;
} }
} }
namespace zone_loading
{
utils::hook::detour db_is_patch_hook;
bool check_missing_content_func(const char* zone_name)
{
const char* lang_code = game::SEH_GetCurrentLanguageCode();
char buffer[0x100]{ 0 };
const auto len = sprintf_s(buffer, "%s_", lang_code);
if (!strncmp(zone_name, buffer, len))
{
printf("Tried to load missing language zone: %s\n", zone_name);
return true;
}
return false;
}
bool db_is_patch_stub(const char* name)
{
if (db_is_patch_hook.invoke<bool>(name)) return true;
if (check_missing_content_func(name)) return true;
return false;
}
void skip_extra_zones_stub(utils::hook::assembler& a)
{
const auto skip = a.newLabel();
const auto original = a.newLabel();
//a.pushad64();
a.test(edi, game::DB_ZONE_CUSTOM); // allocFlags
a.jnz(skip);
a.bind(original);
//a.popad64();
a.call(0x3BC450_b); // strnicmp_ffotd
a.mov(r12d, edi);
a.mov(rdx, 0x1467970_b); // "patch_"
a.jmp(0x3BA9C0_b);
a.bind(skip);
//a.popad64();
a.mov(r12d, game::DB_ZONE_CUSTOM);
a.not_(r12d);
a.and_(edi, r12d);
a.jmp(0x3BAC06_b);
}
}
using namespace zone_loading;
bool exists(const std::string& zone) bool exists(const std::string& zone)
{ {
const auto is_localized = game::DB_IsLocalized(zone.data()); const auto is_localized = game::DB_IsLocalized(zone.data());
@ -97,6 +189,45 @@ namespace fastfiles
#endif #endif
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
db_add_xasset_hook.create(0xA76520_b, db_add_xasset_stub);
g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
// Don't fatal on certain missing zones
db_is_patch_hook.create(0x3BC580_b, db_is_patch_stub);
// Don't load extra zones with loadzone
utils::hook::nop(0x3BA9B1_b, 15);
utils::hook::jump(0x3BA9B1_b, utils::hook::assemble(skip_extra_zones_stub), true);
// Allow loading of unsigned fastfiles
utils::hook::set<uint8_t>(0x9E8CAE_b, 0xEB); // DB_InflateInit
// Skip signature validation
utils::hook::set(0x9E6390_b, 0xC301B0); // signature
command::add("loadzone", [](const command::params& params)
{
if (params.size() < 2)
{
console::info("usage: loadzone <zone>\n");
return;
}
const char* name = params.get(1);
if (!fastfiles::exists(name))
{
console::warn("loadzone: zone %s could not be found!\n", name);
return;
}
game::XZoneInfo info{};
info.name = name;
info.allocFlags = game::DB_ZONE_GAME;
info.allocFlags |= game::DB_ZONE_CUSTOM; // skip extra zones with this flag
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_ASYNC);
});
command::add("listassetpool", [](const command::params& params) command::add("listassetpool", [](const command::params& params)
{ {

View File

@ -2,42 +2,225 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "game/game.hpp" #include "component/dvars.hpp"
#include "component/console/console.hpp"
#include "dvars.hpp" #include "game/game.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/io.hpp>
namespace game::filesystem namespace filesystem
{ {
file::file(std::string name) namespace
: name_(std::move(name))
{ {
char* buffer{}; utils::hook::detour fs_startup_hook;
const auto size = game::FS_ReadFile(this->name_.data(), &buffer);
if (size >= 0 && buffer) bool initialized = false;
std::deque<std::filesystem::path>& get_search_paths_internal()
{ {
this->valid_ = true; static std::deque<std::filesystem::path> search_paths{};
this->buffer_.append(buffer, size); return search_paths;
game::FS_FreeFile(buffer); }
void fs_display_path()
{
console::info("Current language: %s\n", game::SEH_GetLanguageName(*reinterpret_cast<int*>(0x74C6420_b)));
console::info("Current search paths:\n");
if (game::fs_searchpaths.get())
{
for (auto i = game::fs_searchpaths.get()->next; i; i = i->next)
{
console::info("%s/%s\n", i->dir->path, i->dir->gamedir);
}
}
for (auto path : filesystem::get_search_paths())
{
console::info("%s\n", path.data());
}
}
void fs_startup_stub(const char* name)
{
console::info("----- FS_Startup -----\n");
initialized = true;
filesystem::register_path(L".");
filesystem::register_path(L"iw7-mod");
filesystem::register_path(L"devraw_shared");
filesystem::register_path(L"devraw");
filesystem::register_path(L"raw_shared");
filesystem::register_path(L"raw");
filesystem::register_path(L"main_shared");
filesystem::register_path(L"main");
fs_startup_hook.invoke<void>(name);
fs_display_path();
console::info("----------------------\n");
}
std::vector<std::filesystem::path> get_paths(const std::filesystem::path& path)
{
std::vector<std::filesystem::path> paths{};
paths.push_back(path);
return paths;
}
bool can_insert_path(const std::filesystem::path& path)
{
for (const auto& path_ : get_search_paths_internal())
{
if (path_ == path)
{
return false;
}
}
return true;
}
const char* sys_default_install_path_stub()
{
static auto current_path = std::filesystem::current_path().string();
return current_path.data();
} }
} }
bool file::exists() const std::string read_file(const std::string& path)
{ {
return this->valid_; for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
return utils::io::read_file(path_.generic_string());
}
}
return {};
} }
const std::string& file::get_buffer() const bool read_file(const std::string& path, std::string* data, std::string* real_path)
{ {
return this->buffer_; for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::read_file(path_.generic_string(), data))
{
if (real_path != nullptr)
{
*real_path = path_.generic_string();
}
return true;
}
}
return false;
} }
const std::string& file::get_name() const bool find_file(const std::string& path, std::string* real_path)
{ {
return this->name_; for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
*real_path = path_.generic_string();
return true;
}
}
return false;
}
bool exists(const std::string& path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
return true;
}
}
return false;
}
void register_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
if (can_insert_path(path_))
{
console::debug("[FS] Registering path '%s'\n", path_.generic_string().data());
get_search_paths_internal().push_front(path_);
}
}
}
void unregister_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.begin(); i != search_paths.end();)
{
if (*i == path_)
{
console::debug("[FS] Unregistering path '%s'\n", path_.generic_string().data());
i = search_paths.erase(i);
}
else
{
++i;
}
}
}
}
std::vector<std::string> get_search_paths()
{
std::vector<std::string> paths{};
for (const auto& path : get_search_paths_internal())
{
paths.push_back(path.generic_string());
}
return paths;
}
std::vector<std::string> get_search_paths_rev()
{
std::vector<std::string> paths{};
const auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.rbegin(); i != search_paths.rend(); ++i)
{
paths.push_back(i->generic_string());
}
return paths;
} }
class component final : public component_interface class component final : public component_interface
@ -45,9 +228,14 @@ namespace game::filesystem
public: public:
void post_unpack() override void post_unpack() override
{ {
dvars::override::register_string("fs_basegame", "iw7-mod", 2048); fs_startup_hook.create(0xCDD800_b, fs_startup_stub);
utils::hook::jump(0xCFE5E0_b, sys_default_install_path_stub);
// fs_game flags
utils::hook::set<uint32_t>(0xCDD415_b, 0);
} }
}; };
} }
REGISTER_COMPONENT(game::filesystem::component) REGISTER_COMPONENT(filesystem::component)

View File

@ -1,19 +1,15 @@
#pragma once #pragma once
namespace game::filesystem namespace filesystem
{ {
class file std::string read_file(const std::string& path);
{ bool read_file(const std::string& path, std::string* data, std::string* real_path = nullptr);
public: bool find_file(const std::string& path, std::string* real_path);
file(std::string name); bool exists(const std::string& path);
[[nodiscard]] bool exists() const; void register_path(const std::filesystem::path& path);
[[nodiscard]] const std::string& get_buffer() const; void unregister_path(const std::filesystem::path& path);
[[nodiscard]] const std::string& get_name() const;
private: std::vector<std::string> get_search_paths();
bool valid_ = false; std::vector<std::string> get_search_paths_rev();
std::string name_; }
std::string buffer_;
};
}

View File

@ -0,0 +1,328 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "script_extension.hpp"
#include "script_error.hpp"
#include "component/scripting.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
using namespace utils::string;
namespace gsc
{
namespace
{
utils::hook::detour scr_emit_function_hook;
std::uint32_t current_filename = 0;
std::string unknown_function_error;
std::array<const char*, 27> var_typename =
{
"undefined",
"object",
"string",
"localized string",
"vector",
"float",
"int",
"codepos",
"precodepos",
"function",
"builtin function",
"builtin method",
"stack",
"animation",
"pre animation",
"thread",
"thread",
"thread",
"thread",
"struct",
"removed entity",
"entity",
"array",
"removed thread",
"<free>",
"thread list",
"endon list",
};
void scr_emit_function_stub(std::uint32_t filename, std::uint32_t thread_name, char* code_pos)
{
current_filename = filename;
scr_emit_function_hook.invoke<void>(filename, thread_name, code_pos);
}
std::string get_filename_name()
{
const auto filename_str = game::SL_ConvertToString(static_cast<game::scr_string_t>(current_filename));
const auto id = std::atoi(filename_str);
if (!id)
{
return filename_str;
}
return scripting::get_token(id);
}
void get_unknown_function_error(const char* code_pos)
{
const auto function = find_function(code_pos);
if (function.has_value())
{
const auto& pos = function.value();
unknown_function_error = std::format(
"while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file
);
}
else
{
unknown_function_error = std::format("unknown script '{}'", scripting::current_file);
}
}
void get_unknown_function_error(std::uint32_t thread_name)
{
const auto filename = get_filename_name();
const auto name = scripting::get_token(thread_name);
unknown_function_error = std::format(
"while processing script '{}':\nunknown function '{}::{}'", scripting::current_file, filename, name
);
}
void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg)
{
get_unknown_function_error(code_pos);
game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data());
}
std::uint32_t find_variable_stub(std::uint32_t parent_id, std::uint32_t thread_name)
{
const auto res = game::FindVariable(parent_id, thread_name);
if (!res)
{
get_unknown_function_error(thread_name);
game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data());
}
return res;
}
unsigned int scr_get_object(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_POINTER)
{
return value->u.pointerValue;
}
scr_error(va("Type %s is not an object", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_string(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (game::Scr_CastString(value))
{
assert(value->type == game::VAR_STRING);
return value->u.stringValue;
}
game::Scr_ErrorInternal();
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_istring(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_ISTRING)
{
return value->u.stringValue;
}
scr_error(va("Type %s is not a localized string", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
void scr_validate_localized_string_ref(int parm_index, const char* token, int token_len)
{
assert(token);
assert(token_len >= 0);
if (token_len < 2)
{
return;
}
for (auto char_iter = 0; char_iter < token_len; ++char_iter)
{
if (!std::isalnum(static_cast<unsigned char>(token[char_iter])) && token[char_iter] != '_')
{
scr_error(va("Illegal localized string reference: %s must contain only alpha-numeric characters and underscores", token));
}
}
}
void scr_get_vector(unsigned int index, float* vector_value)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_VECTOR)
{
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
return;
}
scr_error(va("Type %s is not a vector", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
}
int scr_get_int(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_INTEGER)
{
return value->u.intValue;
}
scr_error(va("Type %s is not an int", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
float scr_get_float(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_FLOAT)
{
return value->u.floatValue;
}
if (value->type == game::VAR_INTEGER)
{
return static_cast<float>(value->u.intValue);
}
scr_error(va("Type %s is not a float", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0.0f;
}
int scr_get_pointer_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
{
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
}
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
int scr_get_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return (game::scr_VmPub->top - index)->type;
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
const char* scr_get_type_name(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return var_typename[(game::scr_VmPub->top - index)->type];
}
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
}
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
{
for (const auto& file : scripting::script_function_table_sort)
{
for (auto i = file.second.begin(); i != file.second.end() && std::next(i) != file.second.end(); ++i)
{
const auto next = std::next(i);
if (pos >= i->second && pos < next->second)
{
return {std::make_pair(i->first, file.first)};
}
}
}
return {};
}
class error final : public component_interface
{
public:
void post_unpack() override
{
scr_emit_function_hook.create(0xBFCF90_b, &scr_emit_function_stub);
utils::hook::call(0xBFCF3A_b, compile_error_stub); // CompileError (LinkFile)
utils::hook::call(0xBFCF86_b, compile_error_stub); // ^
utils::hook::call(0xBFD06F_b, find_variable_stub); // Scr_EmitFunction
// Restore basic error messages for commonly used scr functions
utils::hook::jump(0xC0BA10_b, scr_get_object);
utils::hook::jump(0xC0B4C0_b, scr_get_const_string);
utils::hook::jump(0xC0B270_b, scr_get_const_istring);
utils::hook::jump(0xB52210_b, scr_validate_localized_string_ref);
utils::hook::jump(0xC0BF40_b, scr_get_vector);
utils::hook::jump(0xC0B950_b, scr_get_int);
utils::hook::jump(0xC0B7E0_b, scr_get_float);
utils::hook::jump(0xC0BC00_b, scr_get_pointer_type);
utils::hook::jump(0xC0BDE0_b, scr_get_type);
utils::hook::jump(0xC0BE50_b, scr_get_type_name);
}
};
}
REGISTER_COMPONENT(gsc::error)

View File

@ -0,0 +1,6 @@
#pragma once
namespace gsc
{
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
}

View File

@ -0,0 +1,536 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "component/command.hpp"
#include "component/console/console.hpp"
#include "component/dvars.hpp"
#include "component/scripting.hpp"
#include "script_error.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp"
#include <utils/hook.hpp>
namespace gsc
{
std::uint16_t function_id_start = 806;
std::uint16_t method_id_start = 1483 + 0x8000;
constexpr size_t func_table_count = 0x1000;
constexpr size_t meth_table_count = 0x1000;
builtin_function func_table[func_table_count]{};
builtin_method meth_table[meth_table_count]{};
const game::dvar_t* developer_script = nullptr;
namespace
{
std::unordered_map<std::uint16_t, script_function> functions;
std::unordered_map<std::uint16_t, script_method> methods;
bool force_error_print = false;
std::optional<std::string> gsc_error_msg;
std::unordered_map<const char*, const char*> vm_execute_hooks;
const char* target_function = nullptr;
function_args get_arguments()
{
std::vector<scripting::script_value> args;
for (auto i = 0; static_cast<std::uint32_t>(i) < game::scr_VmPub->outparamcount; ++i)
{
const auto value = game::scr_VmPub->top[-i];
args.push_back(value);
}
return args;
}
void return_value(const scripting::script_value& value)
{
if (game::scr_VmPub->outparamcount)
{
game::Scr_ClearOutParams();
}
scripting::push_value(value);
}
std::uint16_t get_function_id()
{
const auto pos = game::scr_function_stack->pos;
return *reinterpret_cast<std::uint16_t*>(
reinterpret_cast<size_t>(pos - 2));
}
void nullstub_func() {}
void nullstub_meth(game::scr_entref_t) {}
void execute_custom_function(const std::uint16_t id)
{
try
{
const auto& function = functions[id];
const auto result = function(get_arguments());
const auto type = result.get_raw().type;
if (type)
{
return_value(result);
}
}
catch (const std::exception& ex)
{
scr_error(ex.what());
}
}
void vm_call_builtin_function_internal()
{
const std::uint16_t function_id = get_function_id();
const auto custom_function_id = static_cast<std::uint16_t>(function_id); // cast for gsc-tool & our custom func map
const auto custom = functions.contains(custom_function_id);
if (custom)
{
execute_custom_function(custom_function_id);
return;
}
builtin_function func = func_table[function_id - 1]; // game does this for the stock func table
if (func == nullptr)
{
scr_error(utils::string::va("builtin function \"%s\" doesn't exist", gsc_ctx->func_name(function_id).data()), true);
return;
}
func();
}
void execute_custom_method(const std::uint16_t id, game::scr_entref_t ent_ref)
{
try
{
const auto& method = methods[id];
const auto result = method(ent_ref, get_arguments());
const auto type = result.get_raw().type;
if (type)
{
return_value(result);
}
}
catch (const std::exception& ex)
{
scr_error(ex.what());
}
}
void builtin_call_error(const std::string& error)
{
const auto function_id = get_function_id();
if (function_id > func_table_count)
{
console::warn("in call to builtin method \"%s\"%s", gsc_ctx->meth_name(function_id).data(), error.data());
}
else
{
console::warn("in call to builtin function \"%s\"%s", gsc_ctx->func_name(function_id).data(), error.data());
}
}
std::optional<std::string> get_opcode_name(const std::uint8_t opcode)
{
try
{
const auto index = gsc_ctx->opcode_enum(opcode);
return { gsc_ctx->opcode_name(index) };
}
catch (...)
{
return {};
}
}
void print_callstack()
{
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
{
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
const auto function = find_function(frame->fs.pos);
if (function.has_value())
{
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
}
else
{
console::warn("\tat unknown location %p\n", pos);
}
}
}
void vm_error_internal()
{
const bool dev_script = developer_script ? developer_script->current.enabled : false;
if (!dev_script && !force_error_print)
{
return;
}
console::warn("*********** script runtime error *************\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x6B22940_b);
const std::string error_str = gsc_error_msg.has_value()
? utils::string::va(": %s", gsc_error_msg.value().data())
: "";
if ((opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin))
|| (opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod)))
{
builtin_call_error(error_str);
}
else
{
const auto opcode = get_opcode_name(opcode_id);
if (opcode.has_value())
{
console::warn("while processing instruction %s%s\n", opcode.value().data(), error_str.data());
}
else
{
console::warn("while processing instruction 0x%X%s\n", opcode_id, error_str.data());
}
}
force_error_print = false;
gsc_error_msg = {};
print_callstack();
console::warn("**********************************************\n");
}
void vm_error_stub(__int64 mark_pos)
{
vm_error_internal();
if (!game::CL_IsGameClientActive(0))
{
//return game::Com_Error(game::errorParm::ERR_SCRIPT_DROP, gsc_error_msg.has_value() ? gsc_error_msg.value().data() : "Fatal script error");
}
utils::hook::invoke<void>(0x510C80_b, mark_pos);
}
void print(const function_args& args)
{
std::string buffer{};
for (auto i = 0u; i < args.size(); ++i)
{
const auto str = args[i].to_string();
buffer.append(str);
buffer.append("\t");
}
console::info("%s\n", buffer.data());
}
scripting::script_value typeof(const function_args& args)
{
return args[0].type_name();
}
bool get_replaced_pos(const char* pos)
{
if (vm_execute_hooks.contains(pos))
{
target_function = vm_execute_hooks[pos];
return true;
}
return false;
}
void vm_execute_stub(utils::hook::assembler& a)
{
const auto replace = a.newLabel();
const auto end = a.newLabel();
a.pushad64();
a.mov(rcx, rsi);
a.call_aligned(get_replaced_pos);
a.cmp(al, 0);
a.jne(replace);
a.popad64();
a.jmp(end);
a.bind(end);
a.movzx(r15d, byte_ptr(rsi));
a.inc(rsi);
a.mov(dword_ptr(rbp, 0x94), r15d);
a.jmp(0xC0D0B2_b);
a.bind(replace);
a.popad64();
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&target_function)));
a.mov(rsi, rax);
a.jmp(end);
}
}
void scr_error(const char* error, const bool force_print)
{
force_error_print = force_print;
gsc_error_msg = error;
game::Scr_ErrorInternal();
}
namespace function
{
void add(const std::string& name, script_function function)
{
if (gsc_ctx->func_exists(name))
{
const auto id = gsc_ctx->func_id(name);
functions[id] = function;
}
else
{
const auto id = ++function_id_start;
gsc_ctx->func_add(name, id);
functions[id] = function;
}
}
}
namespace method
{
void add(const std::string& name, script_method method)
{
if (gsc_ctx->meth_exists(name))
{
const auto id = gsc_ctx->meth_id(name);
methods[id] = method;
}
else
{
const auto id = ++method_id_start;
gsc_ctx->meth_add(name, static_cast<std::uint16_t>(id));
methods[id] = method;
}
}
}
function_args::function_args(std::vector<scripting::script_value> values)
: values_(values)
{
}
std::uint32_t function_args::size() const
{
return static_cast<std::uint32_t>(this->values_.size());
}
std::vector<scripting::script_value> function_args::get_raw() const
{
return this->values_;
}
scripting::value_wrap function_args::get(const int index) const
{
if (index >= this->values_.size())
{
throw std::runtime_error(utils::string::va("parameter %d does not exist", index));
}
return {this->values_[index], index};
}
void vm_call_builtin_method_internal(game::scr_entref_t ent_ref, int function_id)
{
const auto custom_function_id = static_cast<std::uint16_t>(function_id); // cast for gsc-tool & our custom method map
const auto custom = methods.contains(custom_function_id);
if (custom)
{
execute_custom_method(custom_function_id, ent_ref);
return;
}
builtin_method meth = meth_table[function_id - 0x8000];
if (meth == nullptr)
{
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
return;
}
meth(ent_ref);
}
void vm_call_builtin_method_stub(utils::hook::assembler& a)
{
a.pushad64();
a.push(rdx);
a.push(ecx);
a.mov(rdx, rdi); // function id is stored in rdi
a.mov(ecx, ebx); // ent ref is stored in ebx
a.call_aligned(vm_call_builtin_method_internal);
a.pop(rdx);
a.pop(ecx);
a.popad64();
a.jmp(0xC0E8F9_b);
}
class extension final : public component_interface
{
public:
void post_unpack() override
{
developer_script = game::Dvar_RegisterBool("developer_script", true, 0, "Enable developer script comments"); // enable by default for now
utils::hook::set<uint32_t>(0xBFD16B_b + 1, func_table_count); // change builtin func count
utils::hook::set<uint32_t>(0xBFD172_b + 4, static_cast<uint32_t>(reverse_b((&func_table))));
utils::hook::inject(0xBFD5A1_b + 3, &func_table);
utils::hook::set<uint32_t>(0xBFD595_b + 2, sizeof(func_table));
utils::hook::nop(0xC0E5CE_b, 7);
utils::hook::call(0xC0E5CE_b, vm_call_builtin_function_internal);
utils::hook::set<uint32_t>(0xBFD182_b + 4, static_cast<uint32_t>(reverse_b((&meth_table))));
utils::hook::inject(0xBFD5AF_b + 3, &meth_table);
utils::hook::set<uint32_t>(0xBFD5B6_b + 2, sizeof(meth_table));
utils::hook::nop(0xC0E8EB_b, 14); // nop the lea & call at the end of call_builtin_method
utils::hook::jump(0xC0E8EB_b, utils::hook::assemble(vm_call_builtin_method_stub), true);
utils::hook::call(0xC0F8C1_b, vm_error_stub); // LargeLocalResetToMark
utils::hook::jump(0xC0D0A4_b, utils::hook::assemble(vm_execute_stub), true);
/*
if (game::environment::is_dedi())
{
function::add("isusingmatchrulesdata", [](const function_args& args)
{
// return 0 so the game doesn't override the cfg
return 0;
});
}
*/
function::add("print", [](const function_args& args)
{
print(args);
return scripting::script_value{};
});
function::add("println", [](const function_args& args)
{
print(args);
return scripting::script_value{};
});
function::add("assert", [](const function_args& args)
{
const auto expr = args[0].as<int>();
if (!expr)
{
throw std::runtime_error("assert fail");
}
return scripting::script_value{};
});
function::add("assertex", [](const function_args& args)
{
const auto expr = args[0].as<int>();
if (!expr)
{
const auto error = args[1].as<std::string>();
throw std::runtime_error(error);
}
return scripting::script_value{};
});
function::add("getfunction", [](const function_args& args)
{
const auto filename = args[0].as<std::string>();
const auto function = args[1].as<std::string>();
if (!scripting::script_function_table[filename].contains(function))
{
throw std::runtime_error("function not found");
}
return scripting::function{scripting::script_function_table[filename][function]};
});
function::add("replacefunc", [](const function_args& args)
{
const auto what = args[0].get_raw();
const auto with = args[1].get_raw();
if (what.type != game::VAR_FUNCTION || with.type != game::VAR_FUNCTION)
{
throw std::runtime_error("replacefunc: parameter 1 must be a function");
}
vm_execute_hooks[what.u.codePosValue] = with.u.codePosValue;
return scripting::script_value{};
});
function::add("toupper", [](const function_args& args)
{
const auto string = args[0].as<std::string>();
return utils::string::to_upper(string);
});
/*
function::add("logprint", [](const function_args& args)
{
std::string buffer{};
for (auto i = 0u; i < args.size(); ++i)
{
const auto string = args[i].as<std::string>();
buffer.append(string);
}
game::G_LogPrintf("%s", buffer.data());
return scripting::script_value{};
});
*/
function::add("executecommand", [](const function_args& args)
{
command::execute(args[0].as<std::string>(), false);
return scripting::script_value{};
});
function::add("typeof", typeof);
function::add("type", typeof);
method::add("test_method", [](game::scr_entref_t ent_ref, const function_args& args)
{
print(args);
return scripting::script_value{};
});
}
};
}
REGISTER_COMPONENT(gsc::extension)

View File

@ -0,0 +1,48 @@
#pragma once
#include "game/scripting/array.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/function.hpp"
namespace gsc
{
class function_args
{
public:
function_args(std::vector<scripting::script_value>);
unsigned int size() const;
std::vector<scripting::script_value> get_raw() const;
scripting::value_wrap get(const int index) const;
scripting::value_wrap operator[](const int index) const
{
return this->get(index);
}
private:
std::vector<scripting::script_value> values_;
};
using builtin_function = void(*)();
using builtin_method = void(*)(game::scr_entref_t);
using script_function = std::function<scripting::script_value(const function_args&)>;
using script_method = std::function<scripting::script_value(const game::scr_entref_t, const function_args&)>;
extern builtin_function func_table[0x1000];
extern builtin_method meth_table[0x1000];
extern const game::dvar_t* developer_script;
void scr_error(const char* error, const bool force_print = false);
namespace function
{
void add(const std::string& name, script_function function);
}
namespace method
{
void add(const std::string& name, script_method function);
}
}

View File

@ -0,0 +1,471 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "component/console/console.hpp"
#include "component/fastfiles.hpp"
#include "component/filesystem.hpp"
#include "component/scripting.hpp"
#include "script_extension.hpp"
#include "game/game.hpp"
#include "script_loading.hpp"
#include <utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
namespace gsc
{
std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx = std::make_unique<xsk::gsc::iw7::context>();;
namespace
{
utils::hook::detour scr_begin_load_scripts_hook;
utils::hook::detour scr_end_load_scripts_hook;
std::unordered_map<std::string, std::uint32_t> main_handles;
std::unordered_map<std::string, std::uint32_t> init_handles;
utils::memory::allocator scriptfile_allocator;
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
char* script_mem_buf = nullptr;
std::uint64_t script_mem_buf_size = 0;
struct
{
char* buf = nullptr;
char* pos = nullptr;
const std::uint64_t size = 0x100000i64;
} script_memory;
char* allocate_buffer(size_t size)
{
if (script_memory.buf == nullptr)
{
script_memory.buf = script_mem_buf;
script_memory.pos = script_memory.buf;
}
if (script_memory.pos + size > script_memory.buf + script_memory.size)
{
game::Com_Error(game::ERR_FATAL, "Out of custom script memory");
}
const auto pos = script_memory.pos;
script_memory.pos += size;
return pos;
}
void free_script_memory()
{
if (script_memory.buf != nullptr)
{
memset(script_memory.buf, 0, reinterpret_cast<size_t>(script_memory.pos) - reinterpret_cast<size_t>(script_memory.buf));
script_memory.buf = nullptr;
script_memory.pos = nullptr;
}
}
void clear()
{
main_handles.clear();
init_handles.clear();
loaded_scripts.clear();
scriptfile_allocator.clear();
free_script_memory();
}
bool read_raw_script_file(const std::string& name, std::string* data)
{
if (filesystem::read_file(name, data))
{
return true;
}
const auto* name_str = name.data();
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, name_str))
{
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_RAWFILE, name_str, false);
const auto len = game::DB_GetRawFileLen(asset.rawfile);
data->resize(len);
game::DB_GetRawBuffer(asset.rawfile, data->data(), len);
if (len > 0)
{
data->pop_back();
}
return true;
}
return false;
}
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
{
if (const auto itr = loaded_scripts.find(file_name); itr != loaded_scripts.end())
{
return itr->second;
}
if (game::Com_FrontEnd_IsInFrontEnd())
{
return nullptr;
}
std::string source_buffer{};
if (!read_raw_script_file(real_name + ".gsc", &source_buffer) || source_buffer.empty())
{
return nullptr;
}
// filter out "GSC rawfiles" that were used for development usage and are not meant for us.
// each "GSC rawfile" has a ScriptFile counterpart to be used instead
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, file_name) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_SCRIPTFILE, file_name))
{
if (real_name.starts_with(utils::string::va("scripts/%s/maps/", game::Com_GameMode_GetActiveGameModeStr()))
&& (real_name.ends_with("_fx") || real_name.ends_with("_sound")))
{
console::debug("Refusing to compile rawfile '%s'\n", real_name.data());
return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, file_name, false).scriptfile;
}
}
console::debug("Loading custom gsc '%s.gsc'", real_name.data());
try
{
auto& compiler = gsc_ctx->compiler();
auto& assembler = gsc_ctx->assembler();
std::vector<std::uint8_t> data;
data.assign(source_buffer.begin(), source_buffer.end());
const auto assembly_ptr = compiler.compile(real_name, data);
const auto output_script = assembler.assemble(*assembly_ptr);
const auto bytecode = output_script.first;
const auto stack = output_script.second;
const auto script_file_ptr = static_cast<game::ScriptFile*>(scriptfile_allocator.allocate(sizeof(game::ScriptFile)));
script_file_ptr->name = file_name;
script_file_ptr->len = static_cast<int>(stack.size);
script_file_ptr->bytecodeLen = static_cast<int>(bytecode.size);
const auto stack_size = static_cast<std::uint32_t>(stack.size + 1);
const auto byte_code_size = static_cast<std::uint32_t>(bytecode.size + 1);
script_file_ptr->buffer = static_cast<char*>(scriptfile_allocator.allocate(stack_size));
std::memcpy(const_cast<char*>(script_file_ptr->buffer), stack.data, stack.size);
script_file_ptr->bytecode = allocate_buffer(byte_code_size);
std::memcpy(script_file_ptr->bytecode, bytecode.data, bytecode.size);
script_file_ptr->compressedLen = 0;
loaded_scripts[file_name] = script_file_ptr;
console::debug("Loaded custom gsc '%s.gsc'", real_name.data());
return script_file_ptr;
}
catch (const std::exception& e)
{
console::error("*********** script compile error *************\n");
console::error("failed to compile '%s':\n%s", real_name.data(), e.what());
console::error("**********************************************\n");
return nullptr;
}
}
std::string get_raw_script_file_name(const std::string& name)
{
if (name.ends_with(".gsh"))
{
return name;
}
return name + ".gsc";
}
std::string get_script_file_name(const std::string& name)
{
const auto id = gsc_ctx->token_id(name);
if (!id)
{
return name;
}
return std::to_string(id);
}
std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>> read_compiled_script_file(const std::string& name, const std::string& real_name)
{
const auto* script_file = game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name.data(), false).scriptfile;
if (script_file == nullptr)
{
throw std::runtime_error(std::format("Could not load scriptfile '{}'", real_name));
}
console::debug("Decompiling scriptfile '%s'\n", real_name.data());
const auto len = script_file->compressedLen;
const std::string stack{script_file->buffer, static_cast<std::uint32_t>(len)};
const auto decompressed_stack = utils::compression::zlib::decompress(stack);
std::vector<std::uint8_t> stack_data;
stack_data.assign(decompressed_stack.begin(), decompressed_stack.end());
return {{reinterpret_cast<std::uint8_t*>(script_file->bytecode), static_cast<std::uint32_t>(script_file->bytecodeLen)}, stack_data};
}
void load_script(const std::string& name)
{
if (!game::Scr_LoadScript(name.data()))
{
return;
}
const auto main_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("init"));
if (main_handle)
{
console::debug("Loaded '%s::main'\n", name.data());
main_handles[name] = main_handle;
}
if (init_handle)
{
console::debug("Loaded '%s::init'\n", name.data());
init_handles[name] = init_handle;
}
}
void load_scripts(const std::filesystem::path& root_dir, const std::filesystem::path& subfolder)
{
std::filesystem::path script_dir = root_dir / subfolder;
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts)
{
if (!script.ends_with(".gsc"))
{
continue;
}
std::filesystem::path path(script);
const auto relative = path.lexically_relative(root_dir).generic_string();
const auto base_name = relative.substr(0, relative.size() - 4);
load_script(base_name);
}
}
void load_scripts()
{
if (!game::Com_FrontEnd_IsInFrontEnd())
{
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path, "scripts/"); // meant to override stock GSC
load_scripts(path, "custom_scripts/"); // for no issues, use custom_scripts/
load_scripts(path, "custom_scripts/"s + game::Com_GameMode_GetActiveGameModeStr() + "/");
}
}
}
void init_compiler()
{
const bool dev_script = developer_script ? developer_script->current.enabled : false;
const auto comp_mode = dev_script ?
xsk::gsc::build::dev :
xsk::gsc::build::prod;
gsc_ctx->init(comp_mode, [](const std::string& include_name)
-> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
{
const auto real_name = get_raw_script_file_name(include_name);
std::string file_buffer;
if (!read_raw_script_file(real_name, &file_buffer) || file_buffer.empty())
{
const auto name = get_script_file_name(include_name);
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data()))
{
return read_compiled_script_file(name, real_name);
}
throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name));
}
std::vector<std::uint8_t> script_data;
script_data.assign(file_buffer.begin(), file_buffer.end());
return { {}, script_data };
});
}
void scr_begin_load_scripts_stub(bool a1)
{
// start the compiler
init_compiler();
scr_begin_load_scripts_hook.invoke<void>(a1);
// load scripts
load_scripts();
}
void scr_end_load_scripts_stub(const char* a1)
{
// cleanup the compiler
gsc_ctx->cleanup();
scr_end_load_scripts_hook.invoke<void>(a1);
}
utils::hook::detour g_load_structs_hook;
void g_load_structs_stub(float a1)
{
for (auto& function_handle : main_handles)
{
console::debug("Executing '%s::main'\n", function_handle.first.data());
game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0));
}
g_load_structs_hook.invoke<void>(a1);
}
utils::hook::detour scr_load_level_hook;
void scr_load_level_stub()
{
for (auto& function_handle : init_handles)
{
console::debug("Executing '%s::init'\n", function_handle.first.data());
game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0));
}
scr_load_level_hook.invoke<void>();
}
int db_is_x_asset_default(game::XAssetType type, const char* name)
{
if (loaded_scripts.contains(name))
{
return 0;
}
return game::DB_IsXAssetDefault(type, name);
}
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
{
if (rawfile->len > 0 && rawfile->compressedLen == 0)
{
std::memset(buf, 0, size);
std::memcpy(buf, rawfile->buffer, std::min(rawfile->len, size));
return;
}
game::DB_GetRawBuffer(rawfile, buf, size);
}
// donetsk developers will paste this in days
utils::hook::detour db_alloc_x_zone_memory_internal_hook;
void db_alloc_x_zone_memory_internal_stub(unsigned __int64* blockSize, const char* filename, game::XZoneMemory* zoneMem, unsigned int type)
{
bool patch = false; // ugly fix for script memory allocation
if (!_stricmp(filename, "code_post_gfx") && type == 2)
{
patch = true;
console::debug("patching memory for '%s'\n", filename);
}
if (patch)
{
blockSize[game::XFILE_BLOCK_SCRIPT] += script_memory.size;
}
db_alloc_x_zone_memory_internal_hook.invoke<void>(blockSize, filename, zoneMem, type);
if (patch)
{
blockSize[game::XFILE_BLOCK_SCRIPT] -= script_memory.size;
script_mem_buf = zoneMem->blocks[game::XFILE_BLOCK_SCRIPT].alloc + blockSize[game::XFILE_BLOCK_SCRIPT];
script_mem_buf_size = script_memory.size;
}
}
}
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default)
{
std::string real_name = name;
const auto id = static_cast<std::uint16_t>(std::atoi(name));
if (id)
{
real_name = gsc_ctx->token_name(id);
}
auto* script = load_custom_script(name, real_name);
if (script)
{
return script;
}
return game::DB_FindXAssetHeader(type, name, allow_create_default).scriptfile;
}
class loading final : public component_interface
{
public:
void post_unpack() override
{
// Allocate script memory (PMem doesn't work)
db_alloc_x_zone_memory_internal_hook.create(0xA75450_b, db_alloc_x_zone_memory_internal_stub);
// Increase allocated script memory
utils::hook::set<uint32_t>(0xA75B5C_b + 1, 0x480000 + static_cast<std::uint32_t>(script_memory.size));
utils::hook::set<uint32_t>(0xA75BAA_b + 4, 0x480 + (static_cast<std::uint32_t>(script_memory.size) >> 12));
utils::hook::set<uint32_t>(0xA75BBE_b + 6, 0x480 + (static_cast<std::uint32_t>(script_memory.size) >> 12));
// Load our scripts with an uncompressed stack
utils::hook::call(0xC09DA7_b, db_get_raw_buffer_stub);
// Compiler start and cleanup, also loads scripts
scr_begin_load_scripts_hook.create(0xBFD500_b, scr_begin_load_scripts_stub);
scr_end_load_scripts_hook.create(0xBFD630_b, scr_end_load_scripts_stub);
// ProcessScript: hook xasset functions to return our own custom scripts
utils::hook::call(0xC09D37_b, find_script);
utils::hook::call(0xC09D47_b, db_is_x_asset_default);
// execute main handle
g_load_structs_hook.create(0x409FB0_b, g_load_structs_stub);
// execute init handle
scr_load_level_hook.create(0xB51B40_b, scr_load_level_stub);
scripting::on_shutdown([](bool free_scripts, bool post_shutdown)
{
if (free_scripts && post_shutdown)
{
clear();
}
});
}
};
}
REGISTER_COMPONENT(gsc::loading)

View File

@ -0,0 +1,9 @@
#pragma once
#include <xsk/gsc/engine/iw7.hpp>
namespace gsc
{
extern std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx;
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);
}

View File

@ -7,6 +7,8 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include "version.h"
namespace logger namespace logger
{ {
namespace namespace
@ -77,6 +79,27 @@ namespace logger
vsnprintf(buffer, buffer_length, msg, va); vsnprintf(buffer, buffer_length, msg, va);
console::warn(buffer); console::warn(buffer);
} }
void com_init_pre()
{
console::info("%s %s build %s %s\n", "IW7", VERSION, "win64", __DATE__);
console::info("--- Common Initialization ---\n");
}
void com_init_post()
{
console::info("--- Common Initialization Complete ---\n");
console::info("Working directory: %s\n", game::Sys_Cwd());
}
void com_init_stub(void* a1)
{
com_init_pre();
utils::hook::invoke<void>(0xB8EF90_b, a1);
com_init_post();
}
} }
class component final : public component_interface class component final : public component_interface
@ -93,6 +116,8 @@ namespace logger
// Com_Printf // Com_Printf
utils::hook::jump(0x343080_b, print_info); utils::hook::jump(0x343080_b, print_info);
utils::hook::call(0xD4D8D8_b, com_init_stub);
if (!game::environment::is_dedi()) if (!game::environment::is_dedi())
{ {
// R_WarnOncePerFrame // R_WarnOncePerFrame

View File

@ -198,10 +198,16 @@ namespace patches
char* db_read_raw_file_stub(const char* filename, char* buf, const int size) char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
{ {
const auto file = game::filesystem::file(filename); std::string file_name = filename;
if (file.exists()) if (file_name.find(".cfg") == std::string::npos)
{ {
snprintf(buf, size, "%s\n", file.get_buffer().data()); file_name.append(".cfg");
}
std::string buffer{};
if (filesystem::read_file(file_name, &buffer))
{
snprintf(buf, size, "%s\n", buffer.data());
return buf; return buf;
} }
@ -301,6 +307,9 @@ namespace patches
// block changing name in-game // block changing name in-game
utils::hook::set<uint8_t>(0xC4DF90_b, 0xC3); utils::hook::set<uint8_t>(0xC4DF90_b, 0xC3);
// disable host migration
utils::hook::set<uint8_t>(0xC5A200_b, 0xC3);
} }
}; };
} }

View File

@ -0,0 +1,39 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "component/dvars.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/flags.hpp>
namespace ranked
{
class component final : public component_interface
{
public:
void post_unpack() override
{
// This must be registered as 'true' to avoid crash when starting a private match
dvars::override::register_bool("xblive_privatematch", true, game::DVAR_FLAG_REPLICATED);
if (game::environment::is_dedi() && !utils::flags::has_flag("unranked"))
{
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE); // DVAR_FLAG_REPLICATED needed?
game::Dvar_RegisterBool("onlinegame", true, game::DVAR_FLAG_READ, "Current game is an online game with stats, custom classes, unlocks");
// Fix sessionteam always returning none (SV_ClientMP_HasAssignedTeam_Internal)
utils::hook::set(0xC50BC0_b, 0xC300B0);
}
}
component_priority priority() override
{
return component_priority::ranked;
}
};
}
REGISTER_COMPONENT(ranked::component)

View File

@ -0,0 +1,285 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "component/gsc/script_extension.hpp"
#include "component/gsc/script_loading.hpp"
#include "component/scheduler.hpp"
#include "component/scripting.hpp"
#include "component/console/console.hpp"
#include "game/game.hpp"
#include "game/scripting/event.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/functions.hpp"
#include <utils/hook.hpp>
namespace scripting
{
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
std::string current_file;
namespace
{
utils::hook::detour vm_notify_hook;
utils::hook::detour scr_add_class_field_hook;
utils::hook::detour scr_set_thread_position_hook;
utils::hook::detour process_script_hook;
utils::hook::detour sl_get_canonical_string_hook;
std::string current_script_file;
unsigned int current_file_id{};
std::vector<std::function<void(bool, bool)>> shutdown_callbacks;
std::unordered_map<unsigned int, std::string> canonical_string_table;
void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value,
game::VariableValue* top)
{
if (!game::Com_FrontEnd_IsInFrontEnd())
{
const auto* string = game::SL_ConvertToString(string_value);
if (string)
{
event e{};
e.name = string;
e.entity = notify_list_owner_id;
for (auto* value = top; value->type != game::VAR_PRECODEPOS; --value)
{
e.arguments.emplace_back(*value);
}
}
}
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
}
void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonical_string, unsigned int offset)
{
const auto name_str = game::SL_ConvertToString(name);
if (fields_table[classnum].find(name_str) == fields_table[classnum].end())
{
fields_table[classnum][name_str] = offset;
}
scr_add_class_field_hook.invoke<void>(classnum, name, canonical_string, offset);
}
void process_script_stub(const char* filename)
{
current_script_file = filename;
const auto file_id = atoi(filename);
if (file_id)
{
current_file_id = static_cast<std::uint16_t>(file_id);
}
else
{
current_file_id = 0;
current_file = filename;
}
process_script_hook.invoke<void>(filename);
}
void add_function_sort(unsigned int id, const char* pos)
{
std::string filename = current_file;
if (current_file_id)
{
filename = scripting::get_token(current_file_id);
}
if (!script_function_table_sort.contains(filename))
{
const auto script = gsc::find_script(game::ASSET_TYPE_SCRIPTFILE, current_script_file.data(), false);
if (script)
{
const auto end = &script->bytecode[script->bytecodeLen];
script_function_table_sort[filename].emplace_back("__end__", end);
}
}
const auto name = scripting::get_token(id);
auto& itr = script_function_table_sort[filename];
itr.insert(itr.end() - 1, {name, pos});
}
void add_function(const std::string& file, unsigned int id, const char* pos)
{
const auto name = get_token(id);
script_function_table[file][name] = pos;
script_function_table_rev[pos] = {file, name};
}
void scr_set_thread_position_stub(unsigned int thread_name, const char* code_pos)
{
add_function_sort(thread_name, code_pos);
if (current_file_id)
{
const auto name = get_token(current_file_id);
add_function(name, thread_name, code_pos);
}
else
{
add_function(current_file, thread_name, code_pos);
}
scr_set_thread_position_hook.invoke<void>(thread_name, code_pos);
}
unsigned int sl_get_canonical_string_stub(const char* str)
{
const auto result = sl_get_canonical_string_hook.invoke<unsigned int>(str);
canonical_string_table[result] = str;
return result;
}
void shutdown_game_pre(const int free_scripts)
{
if (free_scripts)
{
script_function_table_sort.clear();
script_function_table.clear();
script_function_table_rev.clear();
canonical_string_table.clear();
}
for (const auto& callback : shutdown_callbacks)
{
callback(free_scripts, false);
}
scripting::notify(*game::levelEntityId, "shutdownGame_called", { 1 });
}
void shutdown_game_post(const int free_scripts)
{
for (const auto& callback : shutdown_callbacks)
{
callback(free_scripts, true);
}
}
namespace mp
{
utils::hook::detour sv_initgame_vm_hook;
utils::hook::detour sv_shutdowngame_vm_hook;
void sv_initgame_vm_stub(game::sv::SvServerInitSettings* init_settings)
{
if (!game::Com_FrontEnd_IsInFrontEnd())
{
console::info("------- Game Initialization -------\n");
console::info("gamename: %s\n", "IW7");
console::info("gamedate: %s\n", __DATE__);
//G_LogPrintf("------------------------------------------------------------\n");
//G_LogPrintf("InitGame: %s\n", serverinfo);
}
sv_initgame_vm_hook.invoke<void>(init_settings);
if (!game::Com_FrontEnd_IsInFrontEnd())
{
console::info("-----------------------------------\n");
}
}
void sv_shutdowngame_vm_stub(int full_clear, int a2)
{
if (!game::Com_FrontEnd_IsInFrontEnd())
{
console::info("==== ShutdownGame (%d) ====\n", full_clear);
//G_LogPrintf("ShutdownGame:\n");
//G_LogPrintf("------------------------------------------------------------\n");
}
shutdown_game_pre(full_clear);
sv_shutdowngame_vm_hook.invoke<void>(full_clear, a2);
shutdown_game_post(full_clear);
}
}
namespace sp
{
utils::hook::detour sv_initgame_vm_hook;
utils::hook::detour sv_shutdowngame_vm_hook;
void sv_initgame_vm_stub(int random_seed, int restart, int* savegame, void** save, int load_scripts)
{
sv_initgame_vm_hook.invoke<void>(random_seed, restart, savegame, save, load_scripts);
}
void sv_shutdowngame_vm_stub(int full_clear, int a2)
{
shutdown_game_pre(full_clear);
sv_shutdowngame_vm_hook.invoke<void>(full_clear, a2);
shutdown_game_post(full_clear);
}
}
}
std::string get_token(unsigned int id)
{
if (canonical_string_table.find(id) != canonical_string_table.end())
{
return canonical_string_table[id];
}
return scripting::find_token(id);
}
void on_shutdown(const std::function<void(bool, bool)>& callback)
{
shutdown_callbacks.push_back(callback);
}
std::optional<std::string> get_canonical_string(const unsigned int id)
{
if (canonical_string_table.find(id) == canonical_string_table.end())
{
return {};
}
return {canonical_string_table[id]};
}
class component final : public component_interface
{
public:
void post_unpack() override
{
vm_notify_hook.create(0xC10460_b, vm_notify_stub);
scr_add_class_field_hook.create(0xC061F0_b, scr_add_class_field_stub);
scr_set_thread_position_hook.create(0xBFD190_b, scr_set_thread_position_stub);
process_script_hook.create(0xC09D20_b, process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub);
mp::sv_initgame_vm_hook.create(0xBA3428D_b, mp::sv_initgame_vm_stub);
sp::sv_initgame_vm_hook.create(0xBED4A96_b, sp::sv_initgame_vm_stub);
mp::sv_shutdowngame_vm_hook.create(0xBB36D86_b, mp::sv_shutdowngame_vm_stub);
sp::sv_shutdowngame_vm_hook.create(0x12159B6_b, sp::sv_shutdowngame_vm_stub);
}
};
}
REGISTER_COMPONENT(scripting::component)

View File

@ -0,0 +1,16 @@
#pragma once
#include <utils/concurrency.hpp>
namespace scripting
{
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
extern std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
extern std::string current_file;
void on_shutdown(const std::function<void(bool, bool)>& callback);
std::optional<std::string> get_canonical_string(const unsigned int id);
std::string get_token(unsigned int id);
}

View File

@ -256,7 +256,7 @@ namespace stats
console::info("%s\n", value.stringPtr); console::info("%s\n", value.stringPtr);
break; break;
case game::DDL_ENUM_TYPE: case game::DDL_ENUM_TYPE:
console::info("%s\n", game::DDL::DDL_Lookup_GetEnumString(state, value.intValue)); console::info("%s\n", game::DDL_Lookup_GetEnumString(state, value.intValue));
break; break;
default: default:
console::info("Unknown type (%d).\n", type); console::info("Unknown type (%d).\n", type);

2002
src/client/game/database.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
#include "game.hpp" #include "game.hpp"
#include <utils/flags.hpp> #include <utils/flags.hpp>
#include <utils/string.hpp>
namespace game namespace game
{ {
@ -22,6 +23,40 @@ namespace game
} }
} }
namespace shared
{
void client_println(int client_num, const std::string& text)
{
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP)
{
game::CG_Utils_GameMessage(client_num, text.data(), 0); // why is nothing printed?
}
else
{
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"%s\"", text.data()));
}
}
bool cheats_ok(int client_num, bool print)
{
if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP)
{
return true;
}
const auto sv_cheats = game::Dvar_FindVar("sv_cheats");
if (!sv_cheats || !sv_cheats->current.enabled)
{
if(print)
client_println(client_num, "GAME_CHEATSNOTENABLED");
return false;
}
return true;
}
}
int Cmd_Argc() int Cmd_Argc()
{ {
return cmd_args->argc[cmd_args->nesting]; return cmd_args->argc[cmd_args->nesting];
@ -179,4 +214,19 @@ namespace game
Cbuf_AddCall(0, SV_CmdsMP_CheckLoadGame); Cbuf_AddCall(0, SV_CmdsMP_CheckLoadGame);
} }
} }
} }
size_t operator"" _b(const size_t ptr)
{
return game::base_address + ptr;
}
size_t reverse_b(const size_t ptr)
{
return ptr - game::base_address;
}
size_t reverse_b(const void* ptr)
{
return reverse_b(reinterpret_cast<size_t>(ptr));
}

View File

@ -2,6 +2,8 @@
#include "structs.hpp" #include "structs.hpp"
#define PROTOCOL 1
namespace game namespace game
{ {
extern uint64_t base_address; extern uint64_t base_address;
@ -40,6 +42,12 @@ namespace game
bool is_dedi(); bool is_dedi();
} }
namespace shared
{
void client_println(int client_num, const std::string& text);
bool cheats_ok(int client_num = 0, bool print = false);
}
int Cmd_Argc(); int Cmd_Argc();
const char* Cmd_Argv(int index); const char* Cmd_Argv(int index);
@ -58,9 +66,8 @@ namespace game
void SV_CmdsMP_RequestMapRestart(bool loadScripts, bool migrate); void SV_CmdsMP_RequestMapRestart(bool loadScripts, bool migrate);
} }
inline uintptr_t operator"" _b(const uintptr_t ptr) size_t operator"" _b(const size_t ptr);
{ size_t reverse_b(const size_t ptr);
return game::base_address + ptr; size_t reverse_b(const void* ptr);
}
#include "symbols.hpp" #include "symbols.hpp"

View File

@ -0,0 +1,335 @@
#include <std_include.hpp>
#include "array.hpp"
#include "script_value.hpp"
#include "execution.hpp"
namespace scripting
{
array_value::array_value(unsigned int parent_id, unsigned int id)
: id_(id)
, parent_id_(parent_id)
{
if (!this->id_)
{
return;
}
const auto value = game::scr_VarGlob->childVariableValue[this->id_ + 0xFA00 * (this->parent_id_ & 3)];
game::VariableValue variable;
variable.u = value.u.u;
variable.type = value.type;
this->value_ = variable;
}
void array_value::operator=(const script_value& _value)
{
if (!this->id_)
{
return;
}
const auto value = _value.get_raw();
const auto variable = &game::scr_VarGlob->childVariableValue[this->id_ + 0xFA00 * (this->parent_id_ & 3)];
game::AddRefToValue(value.type, value.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = (char)value.type;
variable->u.u = value.u;
this->value_ = value;
}
array::array(const unsigned int id)
: id_(id)
{
this->add();
}
array::array(const array& other) : array(other.id_)
{
}
array::array(array&& other) noexcept
{
this->id_ = other.id_;
other.id_ = 0;
}
array::array()
{
this->id_ = make_array();
}
array::array(std::vector<script_value> values)
{
this->id_ = make_array();
for (const auto& value : values)
{
this->push(value);
}
}
array::array(std::unordered_map<std::string, script_value> values)
{
this->id_ = make_array();
for (const auto& value : values)
{
this->set(value.first, value.second);
}
}
array::~array()
{
this->release();
}
array& array::operator=(const array& other)
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
this->add();
}
return *this;
}
array& array::operator=(array&& other) noexcept
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
other.id_ = 0;
}
return *this;
}
void array::add() const
{
if (this->id_)
{
game::AddRefToValue(game::VAR_POINTER, {static_cast<int>(this->id_)});
}
}
void array::release() const
{
if (this->id_)
{
game::RemoveRefToValue(game::VAR_POINTER, {static_cast<int>(this->id_)});
}
}
std::vector<script_value> array::get_keys() const
{
std::vector<script_value> result;
const auto offset = 0xFA00 * (this->id_ & 3);
auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild;
for (auto i = offset + current; current; i = offset + current)
{
const auto var = game::scr_VarGlob->childVariableValue[i];
if (var.type == game::VAR_UNDEFINED)
{
current = var.nextSibling;
continue;
}
const auto string_value = (game::scr_string_t)((unsigned __int8)var.name_lo + (var.k.keys.name_hi << 8));
const auto* str = game::SL_ConvertToString(string_value);
script_value key;
if (string_value < 0x40000 && str)
{
key = str;
}
else
{
key = (string_value - 0x800000) & 0xFFFFFF;
}
result.push_back(key);
current = var.nextSibling;
}
return result;
}
int array::size() const
{
return static_cast<int>(game::scr_VarGlob->objectVariableValue[this->id_].u.f.next);
}
unsigned int array::push(script_value value) const
{
this->set(this->size(), value);
return this->size();
}
void array::erase(const unsigned int index) const
{
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
if (variable_id)
{
game::RemoveVariableValue(this->id_, variable_id);
}
}
void array::erase(const std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(this->id_, string_value);
if (variable_id)
{
game::RemoveVariableValue(this->id_, variable_id);
}
}
script_value array::pop() const
{
const auto value = this->get(this->size() - 1);
this->erase(this->size() - 1);
return value;
}
script_value array::get(const script_value& key) const
{
if (key.is<int>())
{
return this->get(key.as<int>());
}
else
{
return this->get(key.as<std::string>());
}
}
script_value array::get(const std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(this->id_, string_value);
if (!variable_id)
{
return {};
}
const auto value = game::scr_VarGlob->childVariableValue[variable_id + 0xFA00 * (this->id_ & 3)];
game::VariableValue variable;
variable.u = value.u.u;
variable.type = value.type;
return variable;
}
script_value array::get(const unsigned int index) const
{
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
if (!variable_id)
{
return {};
}
const auto value = game::scr_VarGlob->childVariableValue[variable_id + 0xFA00 * (this->id_ & 3)];
game::VariableValue variable;
variable.u = value.u.u;
variable.type = value.type;
return variable;
}
void array::set(const script_value& key, const script_value& value) const
{
if (key.is<int>())
{
this->set(key.as<int>(), value);
}
else
{
this->set(key.as<std::string>(), value);
}
}
void array::set(const std::string& key, const script_value& _value) const
{
const auto value = _value.get_raw();
const auto variable_id = this->get_value_id(key);
if (!variable_id)
{
return;
}
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + 0xFA00 * (this->id_ & 3)];
game::AddRefToValue(value.type, value.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = (char)value.type;
variable->u.u = value.u;
}
void array::set(const unsigned int index, const script_value& _value) const
{
const auto value = _value.get_raw();
const auto variable_id = this->get_value_id(index);
if (!variable_id)
{
return;
}
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + 0xFA00 * (this->id_ & 3)];
game::AddRefToValue(value.type, value.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = (char)value.type;
variable->u.u = value.u;
}
unsigned int array::get_entity_id() const
{
return this->id_;
}
unsigned int array::get_value_id(const std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(this->id_, string_value);
if (!variable_id)
{
return game::GetNewVariable(this->id_, string_value);
}
return variable_id;
}
unsigned int array::get_value_id(const unsigned int index) const
{
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
if (!variable_id)
{
return game::GetNewArrayVariable(this->id_, index);
}
return variable_id;
}
entity array::get_raw() const
{
return entity(this->id_);
}
}

View File

@ -0,0 +1,89 @@
#pragma once
#include "game/game.hpp"
#include "script_value.hpp"
namespace scripting
{
class array_value : public script_value
{
public:
array_value(unsigned int, unsigned int);
void operator=(const script_value&);
private:
unsigned int id_;
unsigned int parent_id_;
};
class array final
{
public:
array();
array(const unsigned int);
array(std::vector<script_value>);
array(std::unordered_map<std::string, script_value>);
array(const array& other);
array(array&& other) noexcept;
~array();
array& operator=(const array& other);
array& operator=(array&& other) noexcept;
std::vector<script_value> get_keys() const;
int size() const;
unsigned int push(script_value) const;
void erase(const unsigned int) const;
void erase(const std::string&) const;
script_value pop() const;
script_value get(const script_value&) const;
script_value get(const std::string&) const;
script_value get(const unsigned int) const;
void set(const script_value&, const script_value&) const;
void set(const std::string&, const script_value&) const;
void set(const unsigned int, const script_value&) const;
unsigned int get_entity_id() const;
unsigned int get_value_id(const std::string&) const;
unsigned int get_value_id(const unsigned int) const;
entity get_raw() const;
array_value operator[](const int index) const
{
return {this->id_, this->get_value_id(index)};
}
array_value operator[](const std::string& key) const
{
return {this->id_, this->get_value_id(key)};
}
template <typename I = int, typename S = std::string>
array_value operator[](const script_value& key) const
{
if (key.is<I>())
{
return { this->id_, this->get_value_id(key.as<I>()) };
}
if (key.is<S>())
{
return { this->id_, this->get_value_id(key.as<S>()) };
}
throw std::runtime_error("Invalid key type");
}
private:
void add() const;
void release() const;
unsigned int id_;
};
}

View File

@ -0,0 +1,120 @@
#include <std_include.hpp>
#include "entity.hpp"
#include "script_value.hpp"
#include "execution.hpp"
namespace scripting
{
entity::entity()
: entity(0)
{
}
entity::entity(const entity& other) : entity(other.entity_id_)
{
}
entity::entity(entity&& other) noexcept
{
this->entity_id_ = other.entity_id_;
other.entity_id_ = 0;
}
entity::entity(const unsigned int entity_id)
: entity_id_(entity_id)
{
this->add();
}
entity::entity(game::scr_entref_t entref)
: entity(game::FindEntityId(entref.entnum, entref.classnum))
{
}
entity::~entity()
{
this->release();
}
entity& entity::operator=(const entity& other)
{
if (&other != this)
{
this->release();
this->entity_id_ = other.entity_id_;
this->add();
}
return *this;
}
entity& entity::operator=(entity&& other) noexcept
{
if (&other != this)
{
this->release();
this->entity_id_ = other.entity_id_;
other.entity_id_ = 0;
}
return *this;
}
unsigned int entity::get_entity_id() const
{
return this->entity_id_;
}
game::scr_entref_t entity::get_entity_reference() const
{
if (!this->entity_id_)
{
const auto not_null = static_cast<uint16_t>(~0ui16);
return game::scr_entref_t{not_null, not_null};
}
return game::Scr_GetEntityIdRef(this->get_entity_id());
}
bool entity::operator==(const entity& other) const noexcept
{
return this->get_entity_id() == other.get_entity_id();
}
bool entity::operator!=(const entity& other) const noexcept
{
return !this->operator==(other);
}
void entity::add() const
{
if (this->entity_id_)
{
game::AddRefToValue(game::VAR_POINTER, {static_cast<int>(this->entity_id_)});
}
}
void entity::release() const
{
if (this->entity_id_)
{
game::RemoveRefToValue(game::VAR_POINTER, {static_cast<int>(this->entity_id_)});
}
}
void entity::set(const std::string& field, const script_value& value) const
{
set_entity_field(*this, field, value);
}
template <>
script_value entity::get<script_value>(const std::string& field) const
{
return get_entity_field(*this, field);
}
script_value entity::call(const std::string& name, const std::vector<script_value>& arguments) const
{
return call_function(name, *this, arguments);
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "game/game.hpp"
#include "script_value.hpp"
namespace scripting
{
class entity final
{
public:
entity();
entity(unsigned int entity_id);
entity(game::scr_entref_t entref);
entity(const entity& other);
entity(entity&& other) noexcept;
~entity();
entity& operator=(const entity& other);
entity& operator=(entity&& other) noexcept;
void set(const std::string& field, const script_value& value) const;
template <typename T = script_value>
T get(const std::string& field) const;
script_value call(const std::string& name, const std::vector<script_value>& arguments = {}) const;
unsigned int get_entity_id() const;
game::scr_entref_t get_entity_reference() const;
bool operator ==(const entity& other) const noexcept;
bool operator !=(const entity& other) const noexcept;
private:
unsigned int entity_id_;
void add() const;
void release() const;
};
template <>
script_value entity::get(const std::string& field) const;
template <typename T>
T entity::get(const std::string& field) const
{
return this->get<script_value>(field).as<T>();
}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "script_value.hpp"
#include "entity.hpp"
namespace scripting
{
struct event
{
std::string name;
entity entity{};
std::vector<script_value> arguments;
};
}

View File

@ -0,0 +1,251 @@
#include <std_include.hpp>
#include "execution.hpp"
#include "safe_execution.hpp"
#include "stack_isolation.hpp"
#include "component/scripting.hpp"
namespace scripting
{
namespace
{
game::VariableValue* allocate_argument()
{
game::VariableValue* value_ptr = ++game::scr_VmPub->top;
++game::scr_VmPub->inparamcount;
return value_ptr;
}
int get_field_id(const int classnum, const std::string& field)
{
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
{
return scripting::fields_table[classnum][field];
}
return -1;
}
script_value get_return_value()
{
if (game::scr_VmPub->inparamcount == 0)
{
return {};
}
game::Scr_ClearOutParams();
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
return script_value(game::scr_VmPub->top[1 - game::scr_VmPub->outparamcount]);
}
}
void push_value(const script_value& value)
{
auto* value_ptr = allocate_argument();
*value_ptr = value.get_raw();
game::AddRefToValue(value_ptr->type, value_ptr->u);
}
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
{
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
const auto event_id = game::SL_GetString(event.data(), 0);
game::Scr_NotifyId(entity.get_entity_id(), event_id, game::scr_VmPub->inparamcount);
}
script_value call_function(const std::string& name, const entity& entity,
const std::vector<script_value>& arguments)
{
const auto entref = entity.get_entity_reference();
const auto is_method_call = *reinterpret_cast<const int*>(&entref) != -1;
const auto function = find_function(name, !is_method_call);
if (function == nullptr)
{
throw std::runtime_error("Unknown "s + (is_method_call ? "method" : "function") + " '" + name + "'");
}
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
if (!safe_execution::call(function, entref))
{
throw std::runtime_error(
"Error executing "s + (is_method_call ? "method" : "function") + " '" + name + "'");
}
return get_return_value();
}
script_value call_function(const std::string& name, const std::vector<script_value>& arguments)
{
return call_function(name, entity(), arguments);
}
template <>
script_value call(const std::string& name, const std::vector<script_value>& arguments)
{
return call_function(name, arguments);
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
{
const auto id = entity.get_entity_id();
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
game::AddRefToObject(id);
const auto local_id = game::AllocThread(id);
const auto result = game::VM_Execute(local_id, pos, static_cast<std::uint32_t>(arguments.size()));
game::RemoveRefToObject(result);
return get_return_value();
}
const char* get_function_pos(const std::string& filename, const std::string& function)
{
if (!script_function_table.contains(filename))
{
throw std::runtime_error("File '" + filename + "' not found");
}
const auto& functions = script_function_table[filename];
if (!functions.contains(function))
{
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
}
return functions.at(function);
}
script_value call_script_function(const entity& entity, const std::string& filename,
const std::string& function, const std::vector<script_value>& arguments)
{
const auto pos = get_function_pos(filename, function);
return exec_ent_thread(entity, pos, arguments);
}
void set_entity_field(const entity& entity, const std::string& field, const script_value& value)
{
const auto entref = entity.get_entity_reference();
const int id = get_field_id(entref.classnum, field);
if (id != -1)
{
stack_isolation _;
push_value(value);
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
if (!safe_execution::set_entity_field(entref, id))
{
throw std::runtime_error("Failed to set value for field '" + field + "'");
}
}
else
{
set_object_variable(entity.get_entity_id(), field, value);
}
}
script_value get_entity_field(const entity& entity, const std::string& field)
{
const auto entref = entity.get_entity_reference();
const auto id = get_field_id(entref.classnum, field);
if (id != -1)
{
stack_isolation _;
game::VariableValue value{};
if (!safe_execution::get_entity_field(entref, id, &value))
{
throw std::runtime_error("Failed to get value for field '" + field + "'");
}
const auto _0 = gsl::finally([value]
{
game::RemoveRefToValue(value.type, value.u);
});
return value;
}
return get_object_variable(entity.get_entity_id(), field);
}
unsigned int make_array()
{
unsigned int index = 0;
const auto variable = game::AllocVariable(&index);
variable->w.type = game::VAR_ARRAY;
variable->u.f.prev = 0;
variable->u.f.next = 0;
return index;
}
void set_object_variable(const unsigned int parent_id, const unsigned int id, const script_value& value)
{
const auto offset = 0xFA00 * (parent_id & 3);
const auto variable_id = game::GetVariable(parent_id, id);
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset];
const auto& raw_value = value.get_raw();
game::AddRefToValue(raw_value.type, raw_value.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = static_cast<char>(raw_value.type);
variable->u.u = raw_value.u;
}
void set_object_variable(const unsigned int parent_id, const std::string& name, const script_value& value)
{
const auto id = scripting::find_token_id(name);
set_object_variable(parent_id, id, value);
}
script_value get_object_variable(const unsigned int parent_id, const unsigned int id)
{
const auto offset = 0xFA00 * (parent_id & 3);
const auto variable_id = game::FindVariable(parent_id, id);
if (!variable_id)
{
return {};
}
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset];
game::VariableValue value{};
value.type = static_cast<int>(variable->type);
value.u = variable->u.u;
return value;
}
script_value get_object_variable(const unsigned int parent_id, const std::string& name)
{
const auto id = scripting::find_token_id(name);
return get_object_variable(parent_id, id);
}
}

View File

@ -0,0 +1,47 @@
#pragma once
#include "game/game.hpp"
#include "entity.hpp"
#include "array.hpp"
#include "function.hpp"
#include "script_value.hpp"
namespace scripting
{
void push_value(const script_value& value);
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
script_value call_function(const std::string& name, const entity& entity,
const std::vector<script_value>& arguments);
template <typename T = script_value>
T call(const std::string& name, const std::vector<script_value>& arguments = {});
template <>
script_value call(const std::string& name, const std::vector<script_value>& arguments);
template <typename T>
T call(const std::string& name, const std::vector<script_value>& arguments)
{
return call<script_value>(name, arguments).as<T>();
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
const char* get_function_pos(const std::string& filename, const std::string& function);
script_value call_script_function(const entity& entity, const std::string& filename,
const std::string& function, const std::vector<script_value>& arguments);
void set_entity_field(const entity& entity, const std::string& field, const script_value& value);
script_value get_entity_field(const entity& entity, const std::string& field);
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
unsigned int make_array();
script_value get_object_variable(const unsigned int parent_id, const unsigned int id);
script_value get_object_variable(const unsigned int parent_id, const std::string& name);
void set_object_variable(const unsigned int parent_id, const std::string& name, const script_value& value);
void set_object_variable(const unsigned int parent_id, const unsigned int id, const script_value& value);
}

View File

@ -0,0 +1,44 @@
#include <std_include.hpp>
#include "game/scripting/function.hpp"
#include "game/scripting/execution.hpp"
#include "component/scripting.hpp"
namespace scripting
{
function::function(const char* pos)
: pos_(pos)
{
}
script_value function::get_raw() const
{
game::VariableValue value;
value.type = game::VAR_FUNCTION;
value.u.codePosValue = this->pos_;
return value;
}
const char* function::get_pos() const
{
return this->pos_;
}
std::string function::get_name() const
{
if (scripting::script_function_table_rev.contains(this->pos_))
{
const auto& func = scripting::script_function_table_rev[this->pos_];
return utils::string::va("%s::%s", func.first.data(), func.second.data());
}
return "unknown function";
}
script_value function::call(const entity& self, std::vector<script_value> arguments) const
{
return exec_ent_thread(self, this->pos_, arguments);
}
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "entity.hpp"
#include "script_value.hpp"
namespace scripting
{
class function
{
public:
function(const char*);
script_value get_raw() const;
const char* get_pos() const;
std::string get_name() const;
script_value call(const entity& self, std::vector<script_value> arguments) const;
script_value operator()(const entity& self, std::vector<script_value> arguments) const
{
return this->call(self, arguments);
}
script_value operator()(std::vector<script_value> arguments) const
{
return this->call(*game::levelEntityId, arguments);
}
script_value operator()() const
{
return this->call(*game::levelEntityId, {});
}
private:
const char* pos_;
};
}

View File

@ -0,0 +1,108 @@
#include <std_include.hpp>
#include "functions.hpp"
#include <utils/string.hpp>
#include "component/gsc/script_extension.hpp"
#include "component/gsc/script_loading.hpp"
namespace scripting
{
namespace
{
int find_function_index(const std::string& name, [[maybe_unused]] const bool prefer_global)
{
const auto target = utils::string::to_lower(name);
auto const& first = gsc::gsc_ctx->func_map();
auto const& second = gsc::gsc_ctx->meth_map();
if (!prefer_global)
{
if (const auto itr = second.find(name); itr != second.end())
{
return static_cast<int>(itr->second);
}
if (const auto itr = first.find(name); itr != first.end())
{
return static_cast<int>(itr->second);
}
}
if (const auto itr = first.find(name); itr != first.end())
{
return static_cast<int>(itr->second);
}
if (const auto itr = second.find(name); itr != second.end())
{
return static_cast<int>(itr->second);
}
return -1;
}
}
std::uint32_t parse_token_id(const std::string& name)
{
if (name.starts_with("_ID"))
{
return static_cast<std::uint32_t>(std::strtol(name.substr(3).data(), nullptr, 10));
}
if (name.starts_with("_id_"))
{
return static_cast<std::uint32_t>(std::strtol(name.substr(4).data(), nullptr, 16));
}
return 0;
}
std::string find_token(std::uint32_t id)
{
return gsc::gsc_ctx->token_name(id);
}
std::string find_token_single(std::uint32_t id)
{
return gsc::gsc_ctx->token_name(id);
}
unsigned int find_token_id(const std::string& name)
{
const auto id = gsc::gsc_ctx->token_id(name);
if (id)
{
return id;
}
const auto parsed_id = parse_token_id(name);
if (parsed_id)
{
return parsed_id;
}
return game::SL_GetCanonicalString(name.data());
}
script_function get_function_by_index(const std::uint32_t index)
{
static const auto function_table = &gsc::func_table;
static const auto method_table = &gsc::meth_table;
if (index < 0x1000)
{
return reinterpret_cast<script_function*>(function_table)[index - 1];
}
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
}
script_function find_function(const std::string& name, const bool prefer_global)
{
const auto index = find_function_index(name, prefer_global);
if (index < 0) return nullptr;
return get_function_by_index(index);
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
using script_function = void(*)(game::scr_entref_t);
std::string find_token(std::uint32_t id);
std::string find_token_single(std::uint32_t id);
unsigned int find_token_id(const std::string& name);
script_function get_function_by_index(std::uint32_t index);
script_function find_function(const std::string& name, const bool prefer_global);
}

View File

@ -0,0 +1,72 @@
#include <std_include.hpp>
#include "safe_execution.hpp"
#pragma warning(push)
#pragma warning(disable: 4611)
namespace scripting::safe_execution
{
namespace
{
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref)
{
__try
{
function(entref);
return true;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
}
}
bool call(const script_function function, const game::scr_entref_t& entref)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
{
*game::g_script_error_level -= 1;
return false;
}
const auto result = execute_with_seh(function, entref);
*game::g_script_error_level -= 1;
return result;
}
bool set_entity_field(const game::scr_entref_t& entref, const int offset)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
{
*game::g_script_error_level -= 1;
return false;
}
game::Scr_SetObjectField(entref.classnum, entref.entnum, offset);
*game::g_script_error_level -= 1;
return true;
}
bool get_entity_field(const game::scr_entref_t& entref, const int offset, game::VariableValue* value)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
{
value->type = game::VAR_UNDEFINED;
value->u.intValue = 0;
*game::g_script_error_level -= 1;
return false;
}
game::GetEntityFieldValue(value, entref.classnum, entref.entnum, offset);
*game::g_script_error_level -= 1;
return true;
}
}
#pragma warning(pop)

View File

@ -0,0 +1,10 @@
#pragma once
#include "functions.hpp"
namespace scripting::safe_execution
{
bool call(script_function function, const game::scr_entref_t& entref);
bool set_entity_field(const game::scr_entref_t& entref, int offset);
bool get_entity_field(const game::scr_entref_t& entref, int offset, game::VariableValue* value);
}

View File

@ -0,0 +1,356 @@
#include <std_include.hpp>
#include "script_value.hpp"
#include "entity.hpp"
#include "array.hpp"
#include "function.hpp"
namespace scripting
{
/***************************************************************
* Constructors
**************************************************************/
script_value::script_value(const game::VariableValue& value)
: value_(value)
{
}
script_value::script_value(const value_wrap& value)
: value_(value.get_raw())
{
}
script_value::script_value(const int value)
{
game::VariableValue variable{};
variable.type = game::VAR_INTEGER;
variable.u.intValue = value;
this->value_ = variable;
}
script_value::script_value(const unsigned int value)
{
game::VariableValue variable{};
variable.type = game::VAR_INTEGER;
variable.u.uintValue = value;
this->value_ = variable;
}
script_value::script_value(const bool value)
: script_value(static_cast<unsigned>(value))
{
}
script_value::script_value(const float value)
{
game::VariableValue variable{};
variable.type = game::VAR_FLOAT;
variable.u.floatValue = value;
this->value_ = variable;
}
script_value::script_value(const double value)
: script_value(static_cast<float>(value))
{
}
script_value::script_value(const char* value)
{
game::VariableValue variable{};
variable.type = game::VAR_STRING;
variable.u.stringValue = game::SL_GetString(value, 0);
const auto _ = gsl::finally([&variable]()
{
game::RemoveRefToValue(variable.type, variable.u);
});
this->value_ = variable;
}
script_value::script_value(const std::string& value)
: script_value(value.data())
{
}
script_value::script_value(const entity& value)
{
game::VariableValue variable{};
variable.type = game::VAR_POINTER;
variable.u.pointerValue = value.get_entity_id();
this->value_ = variable;
}
script_value::script_value(const array& value)
{
game::VariableValue variable{};
variable.type = game::VAR_POINTER;
variable.u.pointerValue = value.get_entity_id();
this->value_ = variable;
}
script_value::script_value(const function& value)
{
game::VariableValue variable{};
variable.type = game::VAR_FUNCTION;
variable.u.codePosValue = value.get_pos();
this->value_ = variable;
}
script_value::script_value(const vector& value)
{
game::VariableValue variable{};
variable.type = game::VAR_VECTOR;
variable.u.vectorValue = game::Scr_AllocVector(value);
const auto _ = gsl::finally([&variable]()
{
game::RemoveRefToValue(variable.type, variable.u);
});
this->value_ = variable;
}
/***************************************************************
* Integer
**************************************************************/
template <>
bool script_value::is<int>() const
{
return this->get_raw().type == game::VAR_INTEGER;
}
template <>
bool script_value::is<unsigned int>() const
{
return this->is<int>();
}
template <>
bool script_value::is<bool>() const
{
return this->is<int>();
}
template <>
int script_value::get() const
{
return this->get_raw().u.intValue;
}
template <>
unsigned int script_value::get() const
{
return this->get_raw().u.uintValue;
}
template <>
bool script_value::get() const
{
return this->get_raw().u.uintValue != 0;
}
/***************************************************************
* Float
**************************************************************/
template <>
bool script_value::is<float>() const
{
return this->get_raw().type == game::VAR_FLOAT;
}
template <>
bool script_value::is<double>() const
{
return this->is<float>();
}
template <>
float script_value::get() const
{
return this->get_raw().u.floatValue;
}
template <>
double script_value::get() const
{
return static_cast<double>(this->get_raw().u.floatValue);
}
/***************************************************************
* String
**************************************************************/
template <>
bool script_value::is<const char*>() const
{
return this->get_raw().type == game::VAR_STRING;
}
template <>
bool script_value::is<std::string>() const
{
return this->is<const char*>();
}
template <>
const char* script_value::get() const
{
return game::SL_ConvertToString(static_cast<game::scr_string_t>(this->get_raw().u.stringValue));
}
template <>
std::string script_value::get() const
{
return this->get<const char*>();
}
/***************************************************************
* Array
**************************************************************/
template <>
bool script_value::is<array>() const
{
if (this->get_raw().type != game::VAR_POINTER)
{
return false;
}
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::VAR_ARRAY;
}
template <>
array script_value::get() const
{
return array(this->get_raw().u.uintValue);
}
/***************************************************************
* Struct
**************************************************************/
template <>
bool script_value::is<std::map<std::string, script_value>>() const
{
if (this->get_raw().type != game::VAR_POINTER)
{
return false;
}
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::VAR_OBJECT;
}
/***************************************************************
* Function
**************************************************************/
template <>
bool script_value::is<function>() const
{
return this->get_raw().type == game::VAR_FUNCTION;
}
template <>
function script_value::get() const
{
return function(this->get_raw().u.codePosValue);
}
/***************************************************************
* Entity
**************************************************************/
template <>
bool script_value::is<entity>() const
{
return this->get_raw().type == game::VAR_POINTER;
}
template <>
entity script_value::get() const
{
return entity(this->get_raw().u.pointerValue);
}
/***************************************************************
* Vector
**************************************************************/
template <>
bool script_value::is<vector>() const
{
return this->get_raw().type == game::VAR_VECTOR;
}
template <>
vector script_value::get() const
{
return this->get_raw().u.vectorValue;
}
/***************************************************************
*
**************************************************************/
const game::VariableValue& script_value::get_raw() const
{
return this->value_.get();
}
value_wrap::value_wrap(const scripting::script_value& value, int argument_index)
: value_(value)
, argument_index_(argument_index)
{
}
std::string script_value::to_string() const
{
if (this->is<int>())
{
return utils::string::va("%i", this->as<int>());
}
if (this->is<float>())
{
return utils::string::va("%f", this->as<float>());
}
if (this->is<std::string>())
{
return this->as<std::string>();
}
if (this->is<vector>())
{
const auto vec = this->as<vector>();
return utils::string::va("(%g, %g, %g)",
vec.get_x(),
vec.get_y(),
vec.get_z()
);
}
if (this->is<function>())
{
const auto func = this->as<function>();
return utils::string::va("[[ %s ]]", func.get_name().data());
}
return this->type_name();
}
}

View File

@ -0,0 +1,223 @@
#pragma once
#include "game/game.hpp"
#include "variable_value.hpp"
#include "vector.hpp"
#include <utils/string.hpp>
namespace scripting
{
class entity;
class array;
class function;
class value_wrap;
namespace
{
std::array<const char*, 27> var_typename =
{
"undefined",
"object",
"string",
"localized string",
"vector",
"float",
"int",
"codepos",
"precodepos",
"function",
"builtin function",
"builtin method",
"stack",
"animation",
"pre animation",
"thread",
"thread",
"thread",
"thread",
"struct",
"removed entity",
"entity",
"array",
"removed thread",
"<free>",
"thread list",
"endon list",
};
std::string get_typename(const game::VariableValue& value)
{
if (value.type == game::VAR_POINTER)
{
const auto type = game::scr_VarGlob->objectVariableValue[value.u.uintValue].w.type;
return var_typename[type];
}
else
{
return var_typename[value.type];
}
}
template <typename T, typename A = array>
std::string get_c_typename()
{
auto& info = typeid(T);
if (info == typeid(std::string))
{
return "string";
}
if (info == typeid(const char*))
{
return "string";
}
if (info == typeid(entity))
{
return "entity";
}
if (info == typeid(array))
{
return "array";
}
if (info == typeid(function))
{
return "function";
}
if (info == typeid(vector))
{
return "vector";
}
return info.name();
}
}
class script_value
{
public:
script_value() = default;
script_value(const game::VariableValue& value);
script_value(const value_wrap& value);
script_value(int value);
script_value(unsigned int value);
script_value(bool value);
script_value(float value);
script_value(double value);
script_value(const char* value);
script_value(const std::string& value);
script_value(const entity& value);
script_value(const array& value);
script_value(const function& value);
script_value(const vector& value);
std::string type_name() const
{
return get_typename(this->get_raw());
}
std::string to_string() const;
template <typename T>
bool is() const;
template <typename T>
T as() const
{
if (!this->is<T>())
{
const auto type = get_typename(this->get_raw());
const auto c_type = get_c_typename<T>();
throw std::runtime_error(std::format("has type '{}' but should be '{}'", type, c_type));
}
return get<T>();
}
template <typename T, typename I = int>
T* as_ptr()
{
const auto value = this->as<I>();
if (!value)
{
throw std::runtime_error("is null");
}
return reinterpret_cast<T*>(value);
}
const game::VariableValue& get_raw() const;
variable_value value_{};
private:
template <typename T>
T get() const;
};
class value_wrap
{
public:
value_wrap(const scripting::script_value& value, int argument_index);
std::string to_string() const
{
return this->value_.to_string();
}
std::string type_name() const
{
return this->value_.type_name();
}
template <typename T>
T as() const
{
try
{
return this->value_.as<T>();
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what()));
}
}
template <typename T, typename I = int>
T* as_ptr()
{
try
{
return this->value_.as_ptr<T>();
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what()));
}
}
template <typename T>
T is() const
{
return this->value_.is<T>();
}
const game::VariableValue& get_raw() const
{
return this->value_.get_raw();
}
int argument_index_{};
scripting::script_value value_;
};
}

View File

@ -0,0 +1,27 @@
#include <std_include.hpp>
#include "stack_isolation.hpp"
namespace scripting
{
stack_isolation::stack_isolation()
{
this->in_param_count_ = game::scr_VmPub->inparamcount;
this->out_param_count_ = game::scr_VmPub->outparamcount;
this->top_ = game::scr_VmPub->top;
this->max_stack_ = game::scr_VmPub->maxstack;
game::scr_VmPub->top = this->stack_;
game::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
game::scr_VmPub->inparamcount = 0;
game::scr_VmPub->outparamcount = 0;
}
stack_isolation::~stack_isolation()
{
game::Scr_ClearOutParams();
game::scr_VmPub->inparamcount = this->in_param_count_;
game::scr_VmPub->outparamcount = this->out_param_count_;
game::scr_VmPub->top = this->top_;
game::scr_VmPub->maxstack = this->max_stack_;
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class stack_isolation final
{
public:
stack_isolation();
~stack_isolation();
stack_isolation(stack_isolation&&) = delete;
stack_isolation(const stack_isolation&) = delete;
stack_isolation& operator=(stack_isolation&&) = delete;
stack_isolation& operator=(const stack_isolation&) = delete;
private:
game::VariableValue stack_[512]{};
game::VariableValue* max_stack_;
game::VariableValue* top_;
unsigned int in_param_count_;
unsigned int out_param_count_;
};
}

View File

@ -0,0 +1,68 @@
#include <std_include.hpp>
#include "variable_value.hpp"
namespace scripting
{
variable_value::variable_value(const game::VariableValue& value)
{
this->assign(value);
}
variable_value::variable_value(const variable_value& other) noexcept
{
this->operator=(other);
}
variable_value::variable_value(variable_value&& other) noexcept
{
this->operator=(std::move(other));
}
variable_value& variable_value::operator=(const variable_value& other) noexcept
{
if (this != &other)
{
this->release();
this->assign(other.value_);
}
return *this;
}
variable_value& variable_value::operator=(variable_value&& other) noexcept
{
if (this != &other)
{
this->release();
this->value_ = other.value_;
other.value_.type = game::VAR_UNDEFINED;
}
return *this;
}
variable_value::~variable_value()
{
this->release();
}
const game::VariableValue& variable_value::get() const
{
return this->value_;
}
void variable_value::assign(const game::VariableValue& value)
{
this->value_ = value;
game::AddRefToValue(this->value_.type, this->value_.u);
}
void variable_value::release()
{
if (this->value_.type != game::VAR_UNDEFINED)
{
game::RemoveRefToValue(this->value_.type, this->value_.u);
this->value_.type = game::VAR_UNDEFINED;
}
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class variable_value
{
public:
variable_value() = default;
variable_value(const game::VariableValue& value);
variable_value(const variable_value& other) noexcept;
variable_value(variable_value&& other) noexcept;
variable_value& operator=(const variable_value& other) noexcept;
variable_value& operator=(variable_value&& other) noexcept;
~variable_value();
const game::VariableValue& get() const;
private:
void assign(const game::VariableValue& value);
void release();
game::VariableValue value_{{0}, game::VAR_UNDEFINED};
};
}

View File

@ -0,0 +1,85 @@
#include <std_include.hpp>
#include "vector.hpp"
namespace scripting
{
vector::vector(const float* value)
{
for (auto i = 0; i < 3; ++i)
{
this->value_[i] = value[i];
}
}
vector::vector(const game::vec3_t& value)
: vector(&value[0])
{
}
vector::vector(const float x, const float y, const float z)
{
this->value_[0] = x;
this->value_[1] = y;
this->value_[2] = z;
}
vector::operator game::vec3_t&()
{
return this->value_;
}
vector::operator const game::vec3_t&() const
{
return this->value_;
}
game::vec_t& vector::operator[](const size_t i)
{
if (i >= 3)
{
throw std::runtime_error("Out of bounds.");
}
return this->value_[i];
}
const game::vec_t& vector::operator[](const size_t i) const
{
if (i >= 3)
{
throw std::runtime_error("Out of bounds.");
}
return this->value_[i];
}
float vector::get_x() const
{
return this->operator[](0);
}
float vector::get_y() const
{
return this->operator[](1);
}
float vector::get_z() const
{
return this->operator[](2);
}
void vector::set_x(const float value)
{
this->operator[](0) = value;
}
void vector::set_y(const float value)
{
this->operator[](1) = value;
}
void vector::set_z(const float value)
{
this->operator[](2) = value;
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class vector final
{
public:
vector() = default;
vector(const float* value);
vector(const game::vec3_t& value);
vector(float x, float y, float z);
operator game::vec3_t&();
operator const game::vec3_t&() const;
game::vec_t& operator[](size_t i);
const game::vec_t& operator[](size_t i) const;
float get_x() const;
float get_y() const;
float get_z() const;
void set_x(float value);
void set_y(float value);
void set_z(float value);
private:
game::vec3_t value_{0};
};
}

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,16 @@ namespace game
* Functions * Functions
**************************************************************/ **************************************************************/
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{ 0xC04360 };
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{ 0xC05DB0 };
WEAK symbol<void(unsigned int id)> AddRefToObject{ 0xC04350 };
WEAK symbol<void(unsigned int id)> RemoveRefToObject{ 0xC05CA0 };
WEAK symbol<unsigned int(unsigned int id)> AllocThread{ 0xC04580 };
WEAK symbol<ObjectVariableValue* (unsigned int* id)> AllocVariable{ 0xC04650 };
WEAK symbol<unsigned int(const char* filename)> Scr_LoadScript{ 0xBFD900 };
WEAK symbol<unsigned int(const char* filename, unsigned int handle)> Scr_GetFunctionHandle{ 0xBFD780 };
WEAK symbol<unsigned int(int handle, int num_param)> Scr_ExecThread{ 0xC0ACD0 };
WEAK symbol<float()> BG_GetGravity{ 0x68DD0 }; WEAK symbol<float()> BG_GetGravity{ 0x68DD0 };
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{ 0xB8D830 }; WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{ 0xB8D830 };
@ -52,18 +62,26 @@ namespace game
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{ 0xB7D850 }; WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{ 0xB7D850 };
WEAK symbol<void()> Cmd_EndTokenizeString{ 0xB7CC90 }; WEAK symbol<void()> Cmd_EndTokenizeString{ 0xB7CC90 };
WEAK symbol<__int64(void* stream, int flush)> db_inflate{ 0xE77380 };
WEAK symbol<__int64(void* stream, const char* version, int stream_size)> db_inflateInit_{ 0xE77980 };
WEAK symbol<__int64(void* stream)> db_inflateEnd{ 0xE777F0 };
WEAK symbol<void(void* stream, void* memory, int size)> DB_Zip_InitThreadMemory{ 0xE78290 };
WEAK symbol<void(void* stream)> DB_Zip_ShutdownThreadMemory{ 0xE782D0 };
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, char syncMode)> DB_LoadXAssets{ 0xA78630 };
WEAK symbol<int(XAssetType type, const char* name)> DB_XAssetExists{ 0xA7C3A0 };
WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{ 0xA77AB0 };
WEAK symbol<const char* (XAssetType type, XAssetHeader header)> DB_GetXAssetHeaderName{ 0x9E5BA0 }; WEAK symbol<const char* (XAssetType type, XAssetHeader header)> DB_GetXAssetHeaderName{ 0x9E5BA0 };
WEAK symbol<bool(std::int32_t, void(__cdecl*)(XAssetHeader, void*), const void*)> DB_EnumXAssets_FastFile{ 0xA76CE0 }; WEAK symbol<bool(std::int32_t, void(__cdecl*)(XAssetHeader, void*), const void*)> DB_EnumXAssets_FastFile{ 0xA76CE0 };
WEAK symbol<bool(XAssetType type, const char* name)> DB_IsXAssetDefault{ 0xA780D0 }; WEAK symbol<bool(XAssetType type, const char* name)> DB_IsXAssetDefault{ 0xA780D0 };
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{ 0xA76E00 }; WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{ 0xA76E00 };
WEAK symbol<bool(const char* zoneName)> DB_IsLocalized{ 0x3BC500 }; WEAK symbol<bool(const char* zoneName)> DB_IsLocalized{ 0x3BC500 };
WEAK symbol<char* (const char* filename, char* buf, int size)> DB_ReadRawFile{ 0xA79E30 }; WEAK symbol<char* (const char* filename, char* buf, int size)> DB_ReadRawFile{ 0xA79E30 };
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{ 0xF20AF0 };
namespace DDL WEAK symbol<const char* (const DDLState* state, int enumValue)> DDL_Lookup_GetEnumString{ 0x30430 };
{
WEAK symbol<const char*(const DDLState* state, int enumValue)> DDL_Lookup_GetEnumString{ 0x30430 };
}
WEAK symbol<bool(const DDLState* state)> DDL_StateIsLeaf{ 0x2E3C0 }; WEAK symbol<bool(const DDLState* state)> DDL_StateIsLeaf{ 0x2E3C0 };
WEAK symbol<DDLType(const DDLState* state)> DDL_GetType{ 0x2E5A0 }; WEAK symbol<DDLType(const DDLState* state)> DDL_GetType{ 0x2E5A0 };
WEAK symbol<DDLValue(const DDLState* state, const DDLContext* ddlContext)> DDL_GetValue{ 0x2F5E0 }; WEAK symbol<DDLValue(const DDLState* state, const DDLContext* ddlContext)> DDL_GetValue{ 0x2F5E0 };
@ -104,7 +122,20 @@ namespace game
WEAK symbol<void(char* buffer)> FS_FreeFile{ 0xCDE1F0 }; WEAK symbol<void(char* buffer)> FS_FreeFile{ 0xCDE1F0 };
WEAK symbol<void(int h, const char* fmt, ...)> FS_Printf{ 0xCDD1C0 }; WEAK symbol<void(int h, const char* fmt, ...)> FS_Printf{ 0xCDD1C0 };
WEAK symbol<unsigned int(unsigned int)> GetObjectType{ 0xC059E0 };
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{ 0xC05A90 };
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{ 0xC05660 };
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{ 0xC054E0 };
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{ 0xC05100 };
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{ 0xC05000 };
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{ 0xC05E90 };
WEAK symbol<void(VariableValue* result, unsigned int classnum,
int entnum, int offset)> GetEntityFieldValue{ 0xC09CC0 };
WEAK symbol<int(int clientNum)> G_MainMP_GetClientScore{ 0xB20550 }; WEAK symbol<int(int clientNum)> G_MainMP_GetClientScore{ 0xB20550 };
WEAK symbol<void* (void*, const char* name)> G_GetWeaponForName { 0x733D40 };
WEAK symbol<int (void*, const void* weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{ 0x733D40 };
WEAK symbol<int(void*, const void* weapon, const bool isAlternate, int hadWeapon)> G_InitializeAmmo{ 0x733D40 };
WEAK symbol<char* (char* string)> I_CleanStr{ 0xCFACC0 }; WEAK symbol<char* (char* string)> I_CleanStr{ 0xCFACC0 };
@ -147,6 +178,8 @@ namespace game
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \ #define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_DrawSomething(S), 0, 0, 0, 0) IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_DrawSomething(S), 0, 0, 0, 0)
WEAK symbol<char* ()> Sys_Cwd{ 0xCFE5A0 };
WEAK symbol<int()> Sys_Milliseconds{ 0xD58110 }; WEAK symbol<int()> Sys_Milliseconds{ 0xD58110 };
WEAK symbol<HANDLE(Sys_Folder folder, const char* baseFilename)> Sys_CreateFile{ 0xCFDF50 }; WEAK symbol<HANDLE(Sys_Folder folder, const char* baseFilename)> Sys_CreateFile{ 0xCFDF50 };
@ -154,14 +187,40 @@ namespace game
WEAK symbol<int(int length, void const* data, const netadr_s* to)> Sys_SendPacket{ 0xD57DE0 }; WEAK symbol<int(int length, void const* data, const netadr_s* to)> Sys_SendPacket{ 0xD57DE0 };
WEAK symbol<int(netadr_s* net_from, msg_t* net_message)> Sys_GetPacket{ 0xD57D50 }; WEAK symbol<int(netadr_s* net_from, msg_t* net_message)> Sys_GetPacket{ 0xD57D50 };
WEAK symbol<bool()> Sys_IsDatabaseThread{ 0xBB7B30 };
WEAK symbol<const char* ()> SEH_GetCurrentLanguageCode{ 0xCBAF50 };
WEAK symbol<char* ()> SEH_GetCurrentLanguageName{ 0xCBB090 };
WEAK symbol<char* (int code)> SEH_GetLanguageName{ 0xCBB140 };
WEAK symbol<void(const char* name, int allocDir)> PMem_BeginAlloc{ 0xCF0E10 };
WEAK symbol<void(const char* name, int allocDir)> PMem_EndAlloc{ 0xCF1070 };
WEAK symbol<char* (const size_t size, unsigned int alignment, int type, int source)> PMem_AllocFromSource_NoDebug{ 0xCF0A90 };
WEAK symbol<void(const char* name, int allocDir)> PMem_Free{ 0xCF10D0 };
WEAK symbol<unsigned int(unsigned int localId, const char* pos,
unsigned int paramcount)> VM_Execute{ 0xC0CDB0 };
WEAK symbol<void(unsigned int id, scr_string_t stringValue,
unsigned int paramcount)> Scr_NotifyId{ 0xC0C2B0 };
WEAK symbol<const float* (const float* v)> Scr_AllocVector{ 0xC06960 };
WEAK symbol<void(int)> Scr_AddInt{ 0xC0A580 }; WEAK symbol<void(int)> Scr_AddInt{ 0xC0A580 };
WEAK symbol<bool(VariableValue* value)> Scr_CastString{ 0xC06AE0 };
WEAK symbol<void()> Scr_ClearOutParams{ 0xC0ABC0 };
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{ 0xC09050 };
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{ 0x40B6E0 };
WEAK symbol<int()> Scr_GetInt{ 0xC0B950 }; WEAK symbol<int()> Scr_GetInt{ 0xC0B950 };
WEAK symbol<void()> Scr_ErrorInternal{ 0xC0AC30 };
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{ 0x9E4090 }; WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{ 0x9E4090 };
WEAK symbol<const char* (const StringTable* table, const int comparisonColumn, const char* value, const int valueColumn)> StringTable_Lookup{ 0xCE7950 }; WEAK symbol<const char* (const StringTable* table, const int comparisonColumn, const char* value, const int valueColumn)> StringTable_Lookup{ 0xCE7950 };
WEAK symbol<const char* (const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{ 0xCE78E0 }; WEAK symbol<const char* (const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{ 0xCE78E0 };
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{ 0xC037E0 };
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{ 0xC03300 };
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{ 0xBFD340 };
WEAK symbol<void(const char* string)> SV_Cmd_TokenizeString{ 0xB7DD00 }; WEAK symbol<void(const char* string)> SV_Cmd_TokenizeString{ 0xB7DD00 };
WEAK symbol<void()> SV_Cmd_EndTokenizedString{ 0xB7DCC0 }; WEAK symbol<void()> SV_Cmd_EndTokenizedString{ 0xB7DCC0 };
WEAK symbol<void(const char* map, const char* gameType, int clientCount, int agentCount, bool hardcore, WEAK symbol<void(const char* map, const char* gameType, int clientCount, int agentCount, bool hardcore,
@ -177,14 +236,23 @@ namespace game
WEAK symbol<bool()> SV_Loaded{ 0xC114C0 }; WEAK symbol<bool()> SV_Loaded{ 0xC114C0 };
WEAK symbol<bool(const char* name)> SV_MapExists{ 0xCDB620 }; WEAK symbol<bool(const char* name)> SV_MapExists{ 0xCDB620 };
WEAK symbol<bool(int clientNum)> SV_BotIsBot{ 0xC3BC90 }; WEAK symbol<bool(int clientNum)> SV_BotIsBot{ 0xC3BC90 };
WEAK symbol<void* (int num)> SV_GetPlayerstateForClientNum{ 0xC123A0 };
WEAK symbol<void(int)> SND_StopSounds{ 0xCA06E0 }; WEAK symbol<void(int)> SND_StopSounds{ 0xCA06E0 };
WEAK symbol<void(const char*)> SND_SetMusicState{ 0xC9E110 }; WEAK symbol<void(const char*)> SND_SetMusicState{ 0xC9E110 };
WEAK symbol<void* (jmp_buf* Buf, int Value)> longjmp{ 0x12C0758 };
WEAK symbol<int(jmp_buf* Buf)> _setjmp{ 0x1423110 };
/*************************************************************** /***************************************************************
* Variables * Variables
**************************************************************/ **************************************************************/
WEAK symbol<int> g_script_error_level{ 0x6B16298 };
WEAK symbol<jmp_buf> g_script_error{ 0x6B162A0 };
WEAK symbol<unsigned int> levelEntityId{ 0x665A120 };
WEAK symbol<CmdArgs> sv_cmd_args{ 0x5D65C20 }; WEAK symbol<CmdArgs> sv_cmd_args{ 0x5D65C20 };
WEAK symbol<CmdArgs> cmd_args{ 0x5D65B70 }; WEAK symbol<CmdArgs> cmd_args{ 0x5D65B70 };
WEAK symbol<cmd_function_s*> cmd_functions{ 0x5D65CC8 }; WEAK symbol<cmd_function_s*> cmd_functions{ 0x5D65CC8 };
@ -216,4 +284,21 @@ namespace game
WEAK symbol<SOCKET> query_socket{ 0x779FDC8 }; WEAK symbol<SOCKET> query_socket{ 0x779FDC8 };
WEAK symbol<DWORD> threadIds{ 0x602BAB0 }; WEAK symbol<DWORD> threadIds{ 0x602BAB0 };
WEAK symbol<scrVarGlob_t> scr_VarGlob{ 0x6691180 };
WEAK symbol<scrVmPub_t> scr_VmPub{ 0x6B183B0 };
WEAK symbol<function_stack_t> scr_function_stack{ 0x6B22908 };
WEAK symbol<PhysicalMemory> g_mem{ 0x7685560 };
WEAK symbol<PhysicalMemory> g_scriptmem{ 0x7685FC0 };
WEAK symbol<searchpath_s> fs_searchpaths{ 0x756DEE0 };
WEAK symbol<DB_LoadData> g_load{ 0x52A8010 };
WEAK symbol<int> g_authLoad_isSecure{ 0x529DD90 };
WEAK symbol<DB_ReadStream> db_stream{ 0x52A8050 };
WEAK symbol<db_z_stream_s> db_zip_stream{ 0x529DD30 };
WEAK symbol<char*> db_zip_memory{ 0x525B500 };
WEAK symbol<unsigned __int64> g_streamPos{ 0x5687E30 };
} }

View File

@ -4,6 +4,7 @@ enum class component_priority
{ {
min = 0, min = 0,
dvars, dvars,
ranked,
steam_proxy, steam_proxy,
arxan, arxan,
}; };

View File

@ -25,11 +25,25 @@ public:
installer(const std::string& component_name) installer(const std::string& component_name)
{ {
auto last = component_name.find("::component");
if (last == std::string::npos)
{
last = component_name.find("::error");
}
if (last == std::string::npos)
{
last = component_name.find("::extension");
}
if (last == std::string::npos)
{
last = component_name.find("::loading");
}
register_component( register_component(
std::make_unique<T>(), std::make_unique<T>(),
std::string( std::string(
component_name.begin(), component_name.begin(),
component_name.begin() + component_name.find("::component") component_name.begin() + last
) )
); );
} }

View File

@ -107,7 +107,9 @@ void limit_parallel_dll_loading()
int main() int main()
{ {
ShowWindow(GetConsoleWindow(), SW_HIDE); if (!game::environment::is_dedi())
ShowWindow(GetConsoleWindow(), SW_HIDE);
console::init(); console::init();
FARPROC entry_point; FARPROC entry_point;

View File

@ -15,27 +15,45 @@
#undef min #undef min
#endif #endif
Mem seg_ptr(const SReg& segment, const uint64_t off)
{
auto mem = ptr_abs(off);
mem.setSegment(segment);
return mem;
}
namespace utils::hook namespace utils::hook
{ {
namespace namespace
{ {
uint8_t* allocate_somewhere_near(const void* base_address, const size_t size) size_t get_allocation_granularity()
{ {
size_t offset = 0; SYSTEM_INFO info{};
GetSystemInfo(&info);
return info.dwAllocationGranularity;
}
uint8_t* allocate_somewhere_near(const void* base_address, const size_t granularity, const size_t size)
{
size_t target_address = reinterpret_cast<size_t>(base_address) - (1ull << 31);
target_address &= ~(granularity - 1);
while (true) while (true)
{ {
offset += size; target_address += granularity;
auto* target_address = static_cast<const uint8_t*>(base_address) - offset;
if (is_relatively_far(base_address, target_address)) auto* target_ptr = reinterpret_cast<uint8_t*>(target_address);
if (is_relatively_far(base_address, target_ptr))
{ {
return nullptr; return nullptr;
} }
const auto res = VirtualAlloc(const_cast<uint8_t*>(target_address), size, MEM_RESERVE | MEM_COMMIT, const auto res = VirtualAlloc(target_ptr, size, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE); PAGE_EXECUTE_READWRITE);
if (res) if (res)
{ {
if (is_relatively_far(base_address, target_address)) if (is_relatively_far(base_address, target_ptr))
{ {
VirtualFree(res, 0, MEM_RELEASE); VirtualFree(res, 0, MEM_RELEASE);
return nullptr; return nullptr;
@ -54,8 +72,10 @@ namespace utils::hook
memory(const void* ptr) memory(const void* ptr)
: memory() : memory()
{ {
this->length_ = 0x1000; static const auto allocation_granularity = get_allocation_granularity();
this->buffer_ = allocate_somewhere_near(ptr, this->length_); this->length_ = allocation_granularity;
this->buffer_ = allocate_somewhere_near(ptr, allocation_granularity, this->length_);
if (!this->buffer_) if (!this->buffer_)
{ {
throw std::runtime_error("Failed to allocate"); throw std::runtime_error("Failed to allocate");
@ -144,28 +164,6 @@ namespace utils::hook
}); });
} }
concurrency::container<std::map<const void*, uint8_t>>& get_original_data_map()
{
static concurrency::container<std::map<const void*, uint8_t>> og_data{};
return og_data;
}
void store_original_data(const void* /*data*/, size_t /*length*/)
{
/*get_original_data_map().access([data, length](std::map<const void*, uint8_t>& og_map)
{
const auto data_ptr = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < length; ++i)
{
const auto pos = data_ptr + i;
if (!og_map.contains(pos))
{
og_map[pos] = *pos;
}
}
});*/
}
void* initialize_min_hook() void* initialize_min_hook()
{ {
static class min_hook_init static class min_hook_init
@ -199,12 +197,26 @@ namespace utils::hook
this->push(rsi); this->push(rsi);
this->push(rdi); this->push(rdi);
this->sub(rsp, 0x40); this->push(r8);
this->push(r9);
this->push(r10);
this->push(r11);
this->push(r12);
this->push(r13);
this->push(r14);
this->push(r15);
} }
void assembler::popad64() void assembler::popad64()
{ {
this->add(rsp, 0x40); this->pop(r15);
this->pop(r14);
this->pop(r13);
this->pop(r12);
this->pop(r11);
this->pop(r10);
this->pop(r9);
this->pop(r8);
this->pop(rdi); this->pop(rdi);
this->pop(rsi); this->pop(rsi);
@ -250,12 +262,12 @@ namespace utils::hook
asmjit::Error assembler::call(void* target) asmjit::Error assembler::call(void* target)
{ {
return Assembler::call(size_t(target)); return Assembler::call(reinterpret_cast<size_t>(target));
} }
asmjit::Error assembler::jmp(void* target) asmjit::Error assembler::jmp(void* target)
{ {
return Assembler::jmp(size_t(target)); return Assembler::jmp(reinterpret_cast<size_t>(target));
} }
detour::detour() detour::detour()
@ -299,7 +311,6 @@ namespace utils::hook
{ {
this->clear(); this->clear();
this->place_ = place; this->place_ = place;
store_original_data(place, 14);
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
{ {
@ -332,6 +343,11 @@ namespace utils::hook
this->moved_data_ = move_hook(this->place_); this->moved_data_ = move_hook(this->place_);
} }
void* detour::get_place() const
{
return this->place_;
}
void* detour::get_original() const void* detour::get_original() const
{ {
return this->original_; return this->original_;
@ -345,15 +361,14 @@ namespace utils::hook
} }
} }
std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub) std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library,
const std::string& process, void* stub)
{ {
if (!library.is_valid()) return {}; if (!library.is_valid()) return {};
auto* const ptr = library.get_iat_entry(target_library, process); auto* const ptr = library.get_iat_entry(target_library, process);
if (!ptr) return {}; if (!ptr) return {};
store_original_data(ptr, sizeof(*ptr));
DWORD protect; DWORD protect;
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
@ -365,8 +380,6 @@ namespace utils::hook
void nop(void* place, const size_t length) void nop(void* place, const size_t length)
{ {
store_original_data(place, length);
DWORD old_protect{}; DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
@ -383,8 +396,6 @@ namespace utils::hook
void copy(void* place, const void* data, const size_t length) void copy(void* place, const void* data, const size_t length)
{ {
store_original_data(place, length);
DWORD old_protect{}; DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
@ -411,9 +422,14 @@ namespace utils::hook
bool is_relatively_far(const void* pointer, const void* data, const int offset) bool is_relatively_far(const void* pointer, const void* data, const int offset)
{ {
const int64_t diff = size_t(data) - (size_t(pointer) + offset); return is_relatively_far(reinterpret_cast<size_t>(pointer), reinterpret_cast<size_t>(data), offset);
const auto small_diff = int32_t(diff); }
return diff != int64_t(small_diff);
bool is_relatively_far(const size_t pointer, const size_t data, const int offset)
{
const auto diff = static_cast<int64_t>(data - (pointer + offset));
const auto small_diff = static_cast<int32_t>(diff);
return diff != static_cast<int64_t>(small_diff);
} }
void call(void* pointer, void* data) void call(void* pointer, void* data)
@ -433,9 +449,10 @@ namespace utils::hook
uint8_t copy_data[5]; uint8_t copy_data[5];
copy_data[0] = 0xE8; copy_data[0] = 0xE8;
*reinterpret_cast<int32_t*>(&copy_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5)); *reinterpret_cast<int32_t*>(&copy_data[1]) = static_cast<int32_t>(reinterpret_cast<size_t>(data) - (
reinterpret_cast<size_t>(pointer) + 5));
auto* patch_pointer = PBYTE(pointer); auto* patch_pointer = static_cast<PBYTE>(pointer);
copy(patch_pointer, copy_data, sizeof(copy_data)); copy(patch_pointer, copy_data, sizeof(copy_data));
} }
@ -524,24 +541,35 @@ namespace utils::hook
asm_function(a); asm_function(a);
void* result = nullptr; void* result = nullptr;
runtime.add(&result, &code); auto err_result = runtime.add(&result, &code);
if (err_result != asmjit::ErrorCode::kErrorOk)
{
printf("ASMJIT ERROR: %s\n", asmjit::DebugUtils::errorAsString(err_result));
}
return result; return result;
} }
void inject(void* pointer, const void* data) void inject(size_t pointer, size_t data)
{ {
if (is_relatively_far(pointer, data, 4)) if (is_relatively_far(pointer, data, 4))
{ {
throw std::runtime_error("Too far away to create 32bit relative branch"); throw std::runtime_error("Too far away to create 32bit relative branch");
} }
set<int32_t>(pointer, int32_t(size_t(data) - (size_t(pointer) + 4))); set<int32_t>(
pointer, static_cast<int32_t>(data - (pointer + 4)));
}
void inject(void* pointer, const void* data)
{
return inject(reinterpret_cast<size_t>(pointer), reinterpret_cast<size_t>(data));
} }
void inject(const size_t pointer, const void* data) void inject(const size_t pointer, const void* data)
{ {
return inject(reinterpret_cast<void*>(pointer), data); return inject(pointer, reinterpret_cast<size_t>(data));
} }
std::vector<uint8_t> move_hook(void* pointer) std::vector<uint8_t> move_hook(void* pointer)
@ -589,26 +617,4 @@ namespace utils::hook
return extract<void*>(data + 1); return extract<void*>(data + 1);
} }
}
std::vector<uint8_t> query_original_data(const void* data, const size_t length)
{
std::vector<uint8_t> og_data{};
og_data.resize(length);
memcpy(og_data.data(), data, length);
get_original_data_map().access([data, length, &og_data](const std::map<const void*, uint8_t>& og_map)
{
auto* ptr = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < length; ++i)
{
auto entry = og_map.find(ptr + i);
if (entry != og_map.end())
{
og_data[i] = entry->second;
}
}
});
return og_data;
}
}

View File

@ -1,11 +1,14 @@
#pragma once #pragma once
#include "signature.hpp" #include "signature.hpp"
#include "memory.hpp"
#include <asmjit/core/jitruntime.h> #include <asmjit/core/jitruntime.h>
#include <asmjit/x86/x86assembler.h> #include <asmjit/x86/x86assembler.h>
using namespace asmjit::x86; using namespace asmjit::x86;
Mem seg_ptr(const SReg& segment, const uint64_t off);
namespace utils::hook namespace utils::hook
{ {
namespace detail namespace detail
@ -125,6 +128,8 @@ namespace utils::hook
void move(); void move();
void* get_place() const;
template <typename T> template <typename T>
T* get() const T* get() const
{ {
@ -147,7 +152,8 @@ namespace utils::hook
void un_move(); void un_move();
}; };
std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library,
const std::string& process, void* stub);
void nop(void* place, size_t length); void nop(void* place, size_t length);
void nop(size_t place, size_t length); void nop(size_t place, size_t length);
@ -159,6 +165,7 @@ namespace utils::hook
void copy_string(size_t place, const char* str); void copy_string(size_t place, const char* str);
bool is_relatively_far(const void* pointer, const void* data, int offset = 5); bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
bool is_relatively_far(size_t pointer, size_t data, int offset = 5);
void call(void* pointer, void* data); void call(void* pointer, void* data);
void call(size_t pointer, void* data); void call(size_t pointer, void* data);
@ -172,6 +179,7 @@ namespace utils::hook
void inject(void* pointer, const void* data); void inject(void* pointer, const void* data);
void inject(size_t pointer, const void* data); void inject(size_t pointer, const void* data);
void inject(size_t pointer, size_t data);
std::vector<uint8_t> move_hook(void* pointer); std::vector<uint8_t> move_hook(void* pointer);
std::vector<uint8_t> move_hook(size_t pointer); std::vector<uint8_t> move_hook(size_t pointer);
@ -210,5 +218,51 @@ namespace utils::hook
return static_cast<T(*)(Args ...)>(func)(args...); return static_cast<T(*)(Args ...)>(func)(args...);
} }
std::vector<uint8_t> query_original_data(const void* data, size_t length); template <size_t Base>
} void* allocate_far_jump()
{
constexpr auto alloc_size = 0x1000;
constexpr auto far_jmp_size = 0xC;
const auto alloc_jump_table = []
{
return reinterpret_cast<char*>(
memory::allocate_near(Base, alloc_size, PAGE_EXECUTE_READWRITE));
};
static auto jump_table = alloc_jump_table();
static auto current_pos = jump_table;
if (current_pos + far_jmp_size >= jump_table + alloc_size)
{
jump_table = alloc_jump_table();
current_pos = jump_table;
}
const auto ptr = current_pos;
current_pos += far_jmp_size;
return ptr;
}
template <size_t Base, typename T>
void* create_far_jump(const T dest)
{
static std::unordered_map<void*, void*> allocated_jumps;
if (const auto iter = allocated_jumps.find(reinterpret_cast<void*>(dest)); iter != allocated_jumps.end())
{
return iter->second;
}
const auto pos = allocate_far_jump<Base>();
jump(pos, dest, true);
allocated_jumps.insert(std::make_pair(dest, pos));
return pos;
}
template <size_t Base, typename T>
void far_jump(const size_t address, const T dest)
{
const auto pos = create_far_jump<Base>(dest);
jump(address, pos, false);
}
}

View File

@ -166,6 +166,33 @@ namespace utils
return false; return false;
} }
void* memory::allocate_near(const size_t address, const size_t size, const std::uint32_t protect)
{
SYSTEM_INFO system_info{};
GetSystemInfo(&system_info);
const auto page_size = system_info.dwPageSize;
const auto aligned_size = size + (~size & (page_size - 1));
auto current_address = address;
while (true)
{
current_address -= page_size;
if (current_address <= reinterpret_cast<size_t>(system_info.lpMinimumApplicationAddress))
{
return nullptr;
}
const auto result = VirtualAlloc(reinterpret_cast<void*>(current_address), aligned_size, MEM_RESERVE | MEM_COMMIT, protect);
if (result != nullptr)
{
std::memset(result, 0, aligned_size);
return result;
}
}
}
memory::allocator* memory::get_allocator() memory::allocator* memory::get_allocator()
{ {
return &memory::mem_allocator_; return &memory::mem_allocator_;

View File

@ -69,6 +69,8 @@ namespace utils
static bool is_bad_code_ptr(const void* ptr); static bool is_bad_code_ptr(const void* ptr);
static bool is_rdata_ptr(void* ptr); static bool is_rdata_ptr(void* ptr);
static void* allocate_near(const size_t address, const size_t size, const std::uint32_t protect);
static allocator* get_allocator(); static allocator* get_allocator();
private: private:

2
update_quak_zt.bat Normal file
View File

@ -0,0 +1,2 @@
cd deps/gsc-tool
git pull origin iw7