diff --git a/.gitmodules b/.gitmodules index fc70e9f2..24839421 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,7 @@ [submodule "deps/rapidjson"] path = deps/rapidjson 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 diff --git a/deps/gsc-tool b/deps/gsc-tool new file mode 160000 index 00000000..4761bfba --- /dev/null +++ b/deps/gsc-tool @@ -0,0 +1 @@ +Subproject commit 4761bfbadeba5f4fd04714b837aef93b85977ec2 diff --git a/deps/premake/gsc-tool.lua b/deps/premake/gsc-tool.lua new file mode 100644 index 00000000..22849237 --- /dev/null +++ b/deps/premake/gsc-tool.lua @@ -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) diff --git a/premake5.lua b/premake5.lua index 6a4cad50..701e2ddc 100644 --- a/premake5.lua +++ b/premake5.lua @@ -322,6 +322,15 @@ links {"common"} 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 postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""} end diff --git a/src/client/component/arxan/arxan.cpp b/src/client/component/arxan/arxan.cpp index 373a066b..81f7139e 100644 --- a/src/client/component/arxan/arxan.cpp +++ b/src/client/component/arxan/arxan.cpp @@ -10,8 +10,12 @@ #include #include "integrity.hpp" +#include "breakpoints.hpp" +#include "illegal_instructions.hpp" #define PRECOMPUTED_INTEGRITY_CHECKS +#define PRECOMPUTED_BREAKPOINTS +#define PRECOMPUTED_ILLEGAL_INSTRUCTIONS #define ProcessDebugPort 7 #define ProcessDebugObjectHandle 30 @@ -19,85 +23,8 @@ 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(static_cast(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(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(info)->Reserved3 = PVOID(DWORD64(explorer_pid)); - } - else if (info_class == ProcessDebugObjectHandle) - { - *static_cast(info) = nullptr; - - return 0xC0000353; - } - else if (info_class == ProcessDebugPort) - { - *static_cast(info) = nullptr; - } - else if (info_class == ProcessDebugFlags) - { - *static_cast(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(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(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>& get_text_sections() { static const std::vector> text = [] @@ -179,7 +106,7 @@ namespace arxan uint32_t adjust_integrity_checksum(const uint64_t return_address, uint8_t* stack_frame, 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); if (!context) @@ -320,9 +247,6 @@ namespace arxan assert(game::base_address == 0x140000000); search_and_patch_integrity_checks_precomputed(); #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 split_results = "89 04 8A E9"_sig; @@ -338,14 +262,405 @@ namespace arxan #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(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(info) = nullptr; + return static_cast(0xC0000353); + } + else if (info_class == ProcessDebugPort) + { + *static_cast(info) = nullptr; + } + else if (info_class == ProcessDebugFlags) + { + *static_cast(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(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(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(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 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(reinterpret_cast(address) + 3); + fake_record.ExceptionCode = exception; + + for (auto handler : handle_handler) + { + if (handler.second) + { + auto result = utils::hook::invoke(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(address); + + const auto jump_target = utils::hook::extract(reinterpret_cast(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(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(address); + + const auto jump_target = game_address + 6; + + const auto a1 = *reinterpret_cast(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(address); + + const auto jump_target = utils::hook::extract(reinterpret_cast(game_address + 5)); + + if (*reinterpret_cast(jump_target) != 0x0B0F) // illegal instruction + { + return; // false positive + } + + const auto a1 = *reinterpret_cast(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(i)); + } + + for (const auto i : illegal_instructions_split) + { + patch_illegal_instruction_split(reinterpret_cast(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 { 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 { + remove_hardware_breakpoints(); hide_being_debugged(); scheduler::loop(hide_being_debugged, scheduler::pipeline::async); + store_debug_functions(); const utils::nt::library ntdll("ntdll.dll"); nt_close_hook.create(ntdll.get_proc("NtClose"), nt_close_stub); @@ -359,7 +674,9 @@ namespace arxan void post_unpack() override { + remove_hardware_breakpoints(); search_and_patch_integrity_checks(); + restore_debug_functions(); } component_priority priority() override diff --git a/src/client/component/arxan/breakpoints.hpp b/src/client/component/arxan/breakpoints.hpp new file mode 100644 index 00000000..5438ed33 --- /dev/null +++ b/src/client/component/arxan/breakpoints.hpp @@ -0,0 +1,594 @@ +#pragma once + +#include + +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, +}; \ No newline at end of file diff --git a/src/client/component/arxan/illegal_instructions.hpp b/src/client/component/arxan/illegal_instructions.hpp new file mode 100644 index 00000000..6d92a647 --- /dev/null +++ b/src/client/component/arxan/illegal_instructions.hpp @@ -0,0 +1,597 @@ +#pragma once + +#include + +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, +}; \ No newline at end of file diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 04676927..42385642 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -4,6 +4,7 @@ #include "game/game.hpp" #include "game/dvars.hpp" +#include "game/scripting/execution.hpp" #include "console/console.hpp" #include "game_console.hpp" @@ -194,34 +195,105 @@ namespace command return 0; } - void client_println(int client_num, const std::string& text) + void cmd_give(const int client_num, const std::vector& 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(client_num), 0 }); + + if (arg == "ammo") + { + const auto weapon = player.call("getcurrentweapon").as(); + player.call("givemaxammo", { weapon }); + } + else if (arg == "allammo") + { + const auto weapons = player.call("getweaponslistall").as(); + 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(); + 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(client_num), 0 }); + const auto weapon = player.call("getcurrentweapon"); + player.call("dropitem", { weapon }); + } + catch (...) + { + } + } + + void cmd_take(int client_num, const std::vector& 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"); - if (!sv_cheats || !sv_cheats->current.enabled) - { - client_println(client_num, "GAME_CHEATSNOTENABLED"); - return false; - } + const auto& weapon = params[1]; - return true; + try + { + const auto player = scripting::entity({ static_cast(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&) { - if (!cheats_ok(client_num)) + if (!game::shared::cheats_ok(client_num, true)) { return; } 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_GODMODE_ON" : "GAME_GODMODE_OFF"); @@ -430,13 +502,13 @@ namespace command add_sv("demigod", [](const int client_num, const params_sv&) { - if (!cheats_ok(client_num)) + if (!game::shared::cheats_ok(client_num, true)) { return; } 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_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"); @@ -444,13 +516,13 @@ namespace command add_sv("notarget", [](const int client_num, const params_sv&) { - if (!cheats_ok(client_num)) + if (!game::shared::cheats_ok(client_num, true)) { return; } 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_NOTARGETON" : "GAME_NOTARGETOFF"); @@ -458,13 +530,13 @@ namespace command add_sv("noclip", [](const int client_num, const params_sv&) { - if (!cheats_ok(client_num)) + if (!game::shared::cheats_ok(client_num, true)) { return; } 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_NOCLIPON" : "GAME_NOCLIPOFF"); @@ -472,17 +544,47 @@ namespace command add_sv("ufo", [](const int client_num, const params_sv&) { - if (!cheats_ok(client_num)) + if (!game::shared::cheats_ok(client_num, true)) { return; } 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_UFOON" : "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()); + }); } }; } diff --git a/src/client/component/console/terminal.cpp b/src/client/component/console/terminal.cpp index df64bc2f..ec0d1ed3 100644 --- a/src/client/component/console/terminal.cpp +++ b/src/client/component/console/terminal.cpp @@ -341,7 +341,8 @@ namespace terminal public: component() { - ShowWindow(GetConsoleWindow(), SW_HIDE); + if(!game::environment::is_dedi()) + ShowWindow(GetConsoleWindow(), SW_HIDE); } void post_unpack() override diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index 111ca62b..3941d2c6 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -8,6 +8,7 @@ #include "command.hpp" #include "console/console.hpp" #include "scheduler.hpp" +#include "filesystem.hpp" #include @@ -111,8 +112,6 @@ namespace dedicated } }; - command::execute("onlinegame 1", true); - command::execute("xblive_privatematch 1", true); initialize_gamemode(); } @@ -121,7 +120,7 @@ namespace dedicated 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 buffer = utils::io::read_file(path); + const auto buffer = filesystem::read_file(path); if (!buffer.empty()) { try @@ -153,6 +152,7 @@ namespace dedicated } else { + //console::error("[SND]: failed to find sound length soundalias \"%s\"\n", alias); return 0; } } @@ -233,8 +233,6 @@ namespace dedicated utils::hook::nop(0x34296F_b, 2); // ^ utils::hook::set(0xE08360_b, 0xC3); // don't shutdown renderer - utils::hook::set(0xC5A200_b, 0xC3); // disable host migration - // SOUND patches //utils::hook::nop(0xC93213_b, 5); // snd stream thread //utils::hook::set(0xC93206_b, 0); // snd_active diff --git a/src/client/component/dvar_cheats.cpp b/src/client/component/dvar_cheats.cpp index 384d7667..0f5f8d8c 100644 --- a/src/client/component/dvar_cheats.cpp +++ b/src/client/component/dvar_cheats.cpp @@ -69,8 +69,7 @@ namespace dvar_cheats return false; } - const auto sv_cheats = game::Dvar_FindVar("sv_cheats"); - if ((dvar->flags & game::DvarFlags::DVAR_FLAG_CHEAT) && ((sv_cheats && !sv_cheats->current.enabled) && *game::isCheatOverride)) + if ((dvar->flags & game::DvarFlags::DVAR_FLAG_CHEAT) && (!game::shared::cheats_ok() && *game::isCheatOverride)) { //#ifdef DEBUG console::error("%s is cheat protected\n", dvars::dvar_get_name(dvar).data()); diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 19a9e071..5745c904 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -10,6 +10,9 @@ #include #include #include +#include + +#include //#define XFILE_DEBUG @@ -33,6 +36,7 @@ namespace fastfiles utils::hook::detour db_init_load_x_file_hook; utils::hook::detour db_load_x_zone_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, 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(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(&header.scriptfile->compressedLen), 4); + buffer.append(reinterpret_cast(&header.scriptfile->len), 4); + buffer.append(reinterpret_cast(&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) { auto result = db_find_xasset_header_hook.invoke(type, name, allow_create_default); @@ -65,10 +91,76 @@ namespace fastfiles game::g_assetNames[static_cast(type)], 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(type, header_ptr); 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(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) { const auto is_localized = game::DB_IsLocalized(zone.data()); @@ -97,6 +189,45 @@ namespace fastfiles #endif 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(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 \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) { diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index ecdeb879..225807e0 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -2,42 +2,225 @@ #include "loader/component_loader.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 #include +#include -namespace game::filesystem +namespace filesystem { - file::file(std::string name) - : name_(std::move(name)) + namespace { - char* buffer{}; - const auto size = game::FS_ReadFile(this->name_.data(), &buffer); + utils::hook::detour fs_startup_hook; - if (size >= 0 && buffer) + bool initialized = false; + + std::deque& get_search_paths_internal() { - this->valid_ = true; - this->buffer_.append(buffer, size); - game::FS_FreeFile(buffer); + static std::deque search_paths{}; + return search_paths; + } + + void fs_display_path() + { + console::info("Current language: %s\n", game::SEH_GetLanguageName(*reinterpret_cast(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(name); + + fs_display_path(); + console::info("----------------------\n"); + } + + std::vector get_paths(const std::filesystem::path& path) + { + std::vector 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 get_search_paths() + { + std::vector paths{}; + + for (const auto& path : get_search_paths_internal()) + { + paths.push_back(path.generic_string()); + } + + return paths; + } + + std::vector get_search_paths_rev() + { + std::vector 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 @@ -45,9 +228,14 @@ namespace game::filesystem public: 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(0xCDD415_b, 0); } }; } -REGISTER_COMPONENT(game::filesystem::component) \ No newline at end of file +REGISTER_COMPONENT(filesystem::component) diff --git a/src/client/component/filesystem.hpp b/src/client/component/filesystem.hpp index c393796b..67d1d236 100644 --- a/src/client/component/filesystem.hpp +++ b/src/client/component/filesystem.hpp @@ -1,19 +1,15 @@ #pragma once -namespace game::filesystem +namespace filesystem { - class file - { - public: - file(std::string name); + std::string read_file(const std::string& path); + bool read_file(const std::string& path, std::string* data, std::string* real_path = nullptr); + bool find_file(const std::string& path, std::string* real_path); + bool exists(const std::string& path); - [[nodiscard]] bool exists() const; - [[nodiscard]] const std::string& get_buffer() const; - [[nodiscard]] const std::string& get_name() const; + void register_path(const std::filesystem::path& path); + void unregister_path(const std::filesystem::path& path); - private: - bool valid_ = false; - std::string name_; - std::string buffer_; - }; -} \ No newline at end of file + std::vector get_search_paths(); + std::vector get_search_paths_rev(); +} diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp new file mode 100644 index 00000000..208bf21f --- /dev/null +++ b/src/client/component/gsc/script_error.cpp @@ -0,0 +1,328 @@ +#include + +#include "loader/component_loader.hpp" +#include "game/game.hpp" + +#include "script_extension.hpp" +#include "script_error.hpp" + +#include "component/scripting.hpp" + +#include +#include + +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 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", + "", + "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(filename, thread_name, code_pos); + } + + std::string get_filename_name() + { + const auto filename_str = game::SL_ConvertToString(static_cast(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(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(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(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> 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) diff --git a/src/client/component/gsc/script_error.hpp b/src/client/component/gsc/script_error.hpp new file mode 100644 index 00000000..1cee00d2 --- /dev/null +++ b/src/client/component/gsc/script_error.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace gsc +{ + std::optional> find_function(const char* pos); +} \ No newline at end of file diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp new file mode 100644 index 00000000..75a2d696 --- /dev/null +++ b/src/client/component/gsc/script_extension.cpp @@ -0,0 +1,536 @@ +#include +#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 + +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 functions; + std::unordered_map methods; + + bool force_error_print = false; + std::optional gsc_error_msg; + + std::unordered_map vm_execute_hooks; + const char* target_function = nullptr; + + function_args get_arguments() + { + std::vector args; + + for (auto i = 0; static_cast(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( + reinterpret_cast(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(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 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(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(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(&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(id)); + methods[id] = method; + } + } + } + + function_args::function_args(std::vector values) + : values_(values) + { + } + + std::uint32_t function_args::size() const + { + return static_cast(this->values_.size()); + } + + std::vector 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(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(0xBFD16B_b + 1, func_table_count); // change builtin func count + utils::hook::set(0xBFD172_b + 4, static_cast(reverse_b((&func_table)))); + utils::hook::inject(0xBFD5A1_b + 3, &func_table); + utils::hook::set(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(0xBFD182_b + 4, static_cast(reverse_b((&meth_table)))); + utils::hook::inject(0xBFD5AF_b + 3, &meth_table); + utils::hook::set(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(); + 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(); + if (!expr) + { + const auto error = args[1].as(); + throw std::runtime_error(error); + } + + return scripting::script_value{}; + }); + + function::add("getfunction", [](const function_args& args) + { + const auto filename = args[0].as(); + const auto function = args[1].as(); + + 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(); + 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(); + 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(), 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) diff --git a/src/client/component/gsc/script_extension.hpp b/src/client/component/gsc/script_extension.hpp new file mode 100644 index 00000000..ad57e2b5 --- /dev/null +++ b/src/client/component/gsc/script_extension.hpp @@ -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); + + unsigned int size() const; + std::vector 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 values_; + }; + + using builtin_function = void(*)(); + using builtin_method = void(*)(game::scr_entref_t); + + using script_function = std::function; + using script_method = std::function; + + 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); + } +} \ No newline at end of file diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp new file mode 100644 index 00000000..fa6b8435 --- /dev/null +++ b/src/client/component/gsc/script_loading.cpp @@ -0,0 +1,471 @@ +#include +#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 +#include +#include +#include + +namespace gsc +{ + std::unique_ptr gsc_ctx = std::make_unique();; + + namespace + { + utils::hook::detour scr_begin_load_scripts_hook; + utils::hook::detour scr_end_load_scripts_hook; + + std::unordered_map main_handles; + std::unordered_map init_handles; + + utils::memory::allocator scriptfile_allocator; + std::unordered_map 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(script_memory.pos) - reinterpret_cast(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 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(scriptfile_allocator.allocate(sizeof(game::ScriptFile))); + script_file_ptr->name = file_name; + + script_file_ptr->len = static_cast(stack.size); + script_file_ptr->bytecodeLen = static_cast(bytecode.size); + + const auto stack_size = static_cast(stack.size + 1); + const auto byte_code_size = static_cast(bytecode.size + 1); + + script_file_ptr->buffer = static_cast(scriptfile_allocator.allocate(stack_size)); + std::memcpy(const_cast(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> 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(len)}; + + const auto decompressed_stack = utils::compression::zlib::decompress(stack); + + std::vector stack_data; + stack_data.assign(decompressed_stack.begin(), decompressed_stack.end()); + + return {{reinterpret_cast(script_file->bytecode), static_cast(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> + { + 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 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(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(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(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(); + } + + 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(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::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(0xA75B5C_b + 1, 0x480000 + static_cast(script_memory.size)); + utils::hook::set(0xA75BAA_b + 4, 0x480 + (static_cast(script_memory.size) >> 12)); + utils::hook::set(0xA75BBE_b + 6, 0x480 + (static_cast(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) diff --git a/src/client/component/gsc/script_loading.hpp b/src/client/component/gsc/script_loading.hpp new file mode 100644 index 00000000..0ab9ae94 --- /dev/null +++ b/src/client/component/gsc/script_loading.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace gsc +{ + extern std::unique_ptr gsc_ctx; + + game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default); +} diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index e127c244..924fb8b3 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -7,6 +7,8 @@ #include +#include "version.h" + namespace logger { namespace @@ -77,6 +79,27 @@ namespace logger vsnprintf(buffer, buffer_length, msg, va); 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(0xB8EF90_b, a1); + com_init_post(); + } } class component final : public component_interface @@ -93,6 +116,8 @@ namespace logger // Com_Printf utils::hook::jump(0x343080_b, print_info); + utils::hook::call(0xD4D8D8_b, com_init_stub); + if (!game::environment::is_dedi()) { // R_WarnOncePerFrame diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 6a66ccea..a686a163 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -198,10 +198,16 @@ namespace patches char* db_read_raw_file_stub(const char* filename, char* buf, const int size) { - const auto file = game::filesystem::file(filename); - if (file.exists()) + std::string file_name = filename; + 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; } @@ -301,6 +307,9 @@ namespace patches // block changing name in-game utils::hook::set(0xC4DF90_b, 0xC3); + + // disable host migration + utils::hook::set(0xC5A200_b, 0xC3); } }; } diff --git a/src/client/component/ranked.cpp b/src/client/component/ranked.cpp new file mode 100644 index 00000000..2f14ea66 --- /dev/null +++ b/src/client/component/ranked.cpp @@ -0,0 +1,39 @@ +#include +#include "loader/component_loader.hpp" + +#include "component/dvars.hpp" + +#include "game/game.hpp" + +#include +#include + +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) diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp new file mode 100644 index 00000000..5fc1a1d4 --- /dev/null +++ b/src/client/component/scripting.cpp @@ -0,0 +1,285 @@ +#include +#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 + +namespace scripting +{ + std::unordered_map> fields_table; + + std::unordered_map> script_function_table; + std::unordered_map>> script_function_table_sort; + std::unordered_map> 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> shutdown_callbacks; + + std::unordered_map 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(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(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(file_id); + } + else + { + current_file_id = 0; + current_file = filename; + } + + process_script_hook.invoke(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(thread_name, code_pos); + } + + unsigned int sl_get_canonical_string_stub(const char* str) + { + const auto result = sl_get_canonical_string_hook.invoke(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(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(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(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(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& callback) + { + shutdown_callbacks.push_back(callback); + } + + std::optional 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) diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp new file mode 100644 index 00000000..46048217 --- /dev/null +++ b/src/client/component/scripting.hpp @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace scripting +{ + extern std::unordered_map> fields_table; + extern std::unordered_map> script_function_table; + extern std::unordered_map>> script_function_table_sort; + extern std::unordered_map> script_function_table_rev; + + extern std::string current_file; + + void on_shutdown(const std::function& callback); + std::optional get_canonical_string(const unsigned int id); + std::string get_token(unsigned int id); +} \ No newline at end of file diff --git a/src/client/component/stats.cpp b/src/client/component/stats.cpp index d5fc285e..65dd09dc 100644 --- a/src/client/component/stats.cpp +++ b/src/client/component/stats.cpp @@ -256,7 +256,7 @@ namespace stats console::info("%s\n", value.stringPtr); break; 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; default: console::info("Unknown type (%d).\n", type); diff --git a/src/client/game/database.hpp b/src/client/game/database.hpp new file mode 100644 index 00000000..8c413977 --- /dev/null +++ b/src/client/game/database.hpp @@ -0,0 +1,2002 @@ +#pragma once +#include + +#define assert_sizeof(__ASSET__, __SIZE__) static_assert(sizeof(__ASSET__) == __SIZE__) +#define assert_offsetof(__ASSET__, __VARIABLE__, __OFFSET__) static_assert(offsetof(__ASSET__, __VARIABLE__) == __OFFSET__) + +namespace game::database +{ + typedef float vec_t; + typedef vec_t vec2_t[2]; + typedef vec_t vec3_t[3]; + typedef vec_t vec4_t[4]; + + struct dummy + { + }; + + enum scr_string_t : std::int32_t + { + }; + + enum XAssetType : std::int32_t + { + ASSET_TYPE_PHYSICSLIBRARY = 0, + ASSET_TYPE_PHYSICS_SFX_EVENT_ASSET = 1, + ASSET_TYPE_PHYSICS_VFX_EVENT_ASSET = 2, + ASSET_TYPE_PHYSICSASSET = 3, + ASSET_TYPE_PHYSICS_FX_PIPELINE = 4, + ASSET_TYPE_PHYSICS_FX_SHAPE = 5, + ASSET_TYPE_XANIMPARTS = 6, + ASSET_TYPE_XMODEL_SURFS = 7, + ASSET_TYPE_XMODEL = 8, + ASSET_TYPE_MAYHEM = 9, + ASSET_TYPE_MATERIAL = 10, + ASSET_TYPE_COMPUTESHADER = 11, + ASSET_TYPE_VERTEXSHADER = 12, + ASSET_TYPE_HULLSHADER = 13, + ASSET_TYPE_DOMAINSHADER = 14, + ASSET_TYPE_PIXELSHADER = 15, + ASSET_TYPE_VERTEXDECL = 16, + ASSET_TYPE_TECHNIQUE_SET = 17, + ASSET_TYPE_IMAGE = 18, + ASSET_TYPE_SOUND_GLOBALS = 19, + ASSET_TYPE_SOUND_BANK = 20, + ASSET_TYPE_SOUND_BANK_TRANSIENT = 21, + ASSET_TYPE_CLIPMAP = 22, + ASSET_TYPE_COMWORLD = 23, + ASSET_TYPE_GLASSWORLD = 24, + ASSET_TYPE_PATHDATA = 25, + ASSET_TYPE_NAVMESH = 26, + ASSET_TYPE_MAP_ENTS = 27, + ASSET_TYPE_FXWORLD = 28, + ASSET_TYPE_GFXWORLD = 29, + ASSET_TYPE_GFXWORLD_TRANSIENT_ZONE = 30, + ASSET_TYPE_IESPROFILE = 31, // unused + ASSET_TYPE_LIGHT_DEF = 32, + ASSET_TYPE_UI_MAP = 33, // unused + ASSET_TYPE_ANIMCLASS = 34, + ASSET_TYPE_PLAYERANIM = 35, + ASSET_TYPE_GESTURE = 36, + ASSET_TYPE_LOCALIZE_ENTRY = 37, + ASSET_TYPE_ATTACHMENT = 38, + ASSET_TYPE_WEAPON = 39, + ASSET_TYPE_VFX = 40, + ASSET_TYPE_IMPACT_FX = 41, + ASSET_TYPE_SURFACE_FX = 42, + ASSET_TYPE_AITYPE = 43, // unused + ASSET_TYPE_MPTYPE = 44, // unused + ASSET_TYPE_CHARACTER = 45, // unused + ASSET_TYPE_XMODELALIAS = 46, // unused + ASSET_TYPE_UNKNOWN = 47, // unused + ASSET_TYPE_RAWFILE = 48, + ASSET_TYPE_SCRIPTFILE = 49, + ASSET_TYPE_STRINGTABLE = 50, + ASSET_TYPE_LEADERBOARD = 51, + ASSET_TYPE_VIRTUAL_LEADERBOARD = 52, + ASSET_TYPE_STRUCTUREDDATADEF = 53, + ASSET_TYPE_DDL = 54, + ASSET_TYPE_TRACER = 55, + ASSET_TYPE_VEHICLE = 56, + ASSET_TYPE_ADDON_MAP_ENTS = 57, + ASSET_TYPE_NET_CONST_STRINGS = 58, + ASSET_TYPE_LUA_FILE = 59, + ASSET_TYPE_SCRIPTABLE = 60, + ASSET_TYPE_EQUIPMENT_SND_TABLE = 61, + ASSET_TYPE_VECTORFIELD = 62, + ASSET_TYPE_PARTICLE_SIM_ANIMATION = 63, + ASSET_TYPE_STREAMING_INFO = 64, + ASSET_TYPE_LASER = 65, + ASSET_TYPE_TTF = 66, + ASSET_TYPE_SUIT = 67, + ASSET_TYPE_SUITANIMPACKAGE = 68, + ASSET_TYPE_SPACESHIPTARGET = 69, + ASSET_TYPE_RUMBLE = 70, + ASSET_TYPE_RUMBLE_GRAPH = 71, + ASSET_TYPE_ANIM_PACKAGE = 72, + ASSET_TYPE_SFX_PACKAGE = 73, + ASSET_TYPE_VFX_PACKAGE = 74, + ASSET_TYPE_BEHAVIOR_TREE = 75, + ASSET_TYPE_XANIM_ARCHETYPE = 76, + ASSET_TYPE_XANIM_PROCEDURALBONES = 77, + ASSET_TYPE_RETICLE = 78, + ASSET_TYPE_GFXLIGHTMAP = 79, + ASSET_TYPE_COUNT = 80 + }; + + struct PhysicsLibrary; + struct PhysicsSFXEventAsset; + struct PhysicsVFXEventAsset; + struct PhysicsAsset; + struct PhysicsFXPipeline; + struct PhysicsFXShape; + struct XAnimParts; + struct XModelSurfs; + struct XModel; + struct Mayhem; + struct Material; + struct ComputeShader; + struct MaterialVertexShader; + struct MaterialHullShader; + struct MaterialDomainShader; + struct MaterialPixelShader; + struct MaterialVertexDeclaration; + struct MaterialTechniqueSet; + struct GfxImage; + //ASSET_TYPE_SOUND_GLOBALS = 19, + //ASSET_TYPE_SOUND_BANK = 20, + //ASSET_TYPE_SOUND_BANK_TRANSIENT = 21, + //ASSET_TYPE_CLIPMAP = 22, + //ASSET_TYPE_COMWORLD = 23, + //ASSET_TYPE_GLASSWORLD = 24, + //ASSET_TYPE_PATHDATA = 25, + //ASSET_TYPE_NAVMESH = 26, + struct MapEnts; + //ASSET_TYPE_FXWORLD = 28, + //ASSET_TYPE_GFXWORLD = 29, + //ASSET_TYPE_GFXWORLD_TRANSIENT_ZONE = 30, + //ASSET_TYPE_IESPROFILE = 31, // unused + //ASSET_TYPE_LIGHT_DEF = 32, + //ASSET_TYPE_UI_MAP = 33, // unused + //ASSET_TYPE_ANIMCLASS = 34, + //ASSET_TYPE_PLAYERANIM = 35, + //ASSET_TYPE_GESTURE = 36, + //ASSET_TYPE_LOCALIZE_ENTRY = 37, + //ASSET_TYPE_ATTACHMENT = 38, + //ASSET_TYPE_WEAPON = 39, + //ASSET_TYPE_VFX = 40, + //ASSET_TYPE_IMPACT_FX = 41, + //ASSET_TYPE_SURFACE_FX = 42, + //ASSET_TYPE_AITYPE = 43, // unused + //ASSET_TYPE_MPTYPE = 44, // unused + //ASSET_TYPE_CHARACTER = 45, // unused + //ASSET_TYPE_XMODELALIAS = 46, // unused + //ASSET_TYPE_UNKNOWN = 47, // unused + struct RawFile; + //ASSET_TYPE_SCRIPTFILE = 49, + //ASSET_TYPE_STRINGTABLE = 50, + //ASSET_TYPE_LEADERBOARD = 51, + //ASSET_TYPE_VIRTUAL_LEADERBOARD = 52, + //ASSET_TYPE_STRUCTUREDDATADEF = 53, + //ASSET_TYPE_DDL = 54, + //ASSET_TYPE_TRACER = 55, + //ASSET_TYPE_VEHICLE = 56, + //ASSET_TYPE_ADDON_MAP_ENTS = 57, + //ASSET_TYPE_NET_CONST_STRINGS = 58, + //ASSET_TYPE_LUA_FILE = 59, + struct ScriptableDef; + //ASSET_TYPE_EQUIPMENT_SND_TABLE = 61, + //ASSET_TYPE_VECTORFIELD = 62, + //ASSET_TYPE_PARTICLE_SIM_ANIMATION = 63, + //ASSET_TYPE_STREAMING_INFO = 64, + //ASSET_TYPE_LASER = 65, + //ASSET_TYPE_TTF = 66, + //ASSET_TYPE_SUIT = 67, + //ASSET_TYPE_SUITANIMPACKAGE = 68, + //ASSET_TYPE_SPACESHIPTARGET = 69, + //ASSET_TYPE_RUMBLE = 70, + //ASSET_TYPE_RUMBLE_GRAPH = 71, + //ASSET_TYPE_ANIM_PACKAGE = 72, + //ASSET_TYPE_SFX_PACKAGE = 73, + //ASSET_TYPE_VFX_PACKAGE = 74, + //ASSET_TYPE_BEHAVIOR_TREE = 75, + //ASSET_TYPE_XANIM_ARCHETYPE = 76, + struct XAnimProceduralBones; + //ASSET_TYPE_RETICLE = 78, + //ASSET_TYPE_GFXLIGHTMAP = 79, + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + }; + + struct PhysicsLibrary + { + const char* name; + bool isMaterialList; + bool isBodyQualityList; + bool isMotionPropertiesList; + bool isGlobalTypeCompendium; + unsigned int havokDataSize; + char* havokData; + }; assert_sizeof(PhysicsLibrary, 0x18); + + struct PhysicsSFXEventAssetRule; + struct PhysicsSFXEventAssetRules + { + int numrules; + PhysicsSFXEventAssetRule* rules; + }; + + enum PhysicsSFXEventAssetRuleType + { + Types_PhysicsSFXEventRule = 0x0, + Types_PhysicsSFXEventSoundRule = 0x1, + Types_PhysicsSFXEventMaterialRule = 0x2, + Types_PhysicsSFXEventAngleRule = 0x3, + Types_PhysicsSFXEventSpeedRule = 0x4, + Types_PhysicsSFXEventMassRule = 0x5, + Types_PhysicsSFXEventGravityRule = 0x6, + Types_PhysicsSFXEventAtmosphereRule = 0x7, + Types_PhysicsSFXEventCameraDistanceRule = 0x8, + Types_PhysicsSFXEventFrequencyRule = 0x9, + Types_PhysicsSFXEventRuleCount = 0xA, + }; + + struct PhysicsSFXEventAssetSoundRule + { + char* hitSoundAlias; + char* scrapeSoundAlias; + float scrapeSoundVolumeSpeedThresholdMin; + float scrapeSoundVolumeSpeedThresholdMax; + float scrapeSoundVolumeBlendSpeed; + }; + + struct PhysicsSFXEventAssetMaterialEntry + { + int surfaceType; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetMaterialRule + { + int numentries; + PhysicsSFXEventAssetMaterialEntry* entries; + }; + + struct PhysicsSFXEventAssetAngleEntry + { + float angle; + float hitSoundVolumeModifierMin; + float hitSoundVolumeModifierMax; + float hitSoundPitchModifierMin; + float hitSoundPitchModifierMax; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetAngleRule + { + int numentries; + PhysicsSFXEventAssetAngleEntry* entries; + }; + + struct PhysicsSFXEventAssetSpeedEntry + { + float speedThreshold; + float hitSoundVolumeModifierMin; + float hitSoundVolumeModifierMax; + float hitSoundPitchModifierMin; + float hitSoundPitchModifierMax; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetSpeedRule + { + int numentries; + PhysicsSFXEventAssetSpeedEntry* entries; + }; + + struct PhysicsSFXEventAssetMassEntry + { + float massThreshold; + float hitSoundVolumeModifierMin; + float hitSoundVolumeModifierMax; + float hitSoundPitchModifierMin; + float hitSoundPitchModifierMax; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetMassRule + { + int numentries; + PhysicsSFXEventAssetMassEntry* entries; + }; + + struct PhysicsSFXEventAssetGravityEntry + { + bool gravity; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetGravityRule + { + int numentries; + PhysicsSFXEventAssetGravityEntry* entries; + }; + + struct PhysicsSFXEventAssetAtmosphereEntry + { + bool atmosphere; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetAtmosphereRule + { + int numentries; + PhysicsSFXEventAssetAtmosphereEntry* entries; + }; + + struct PhysicsSFXEventAssetCameraDistanceEntry + { + float distance; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetCameraDistanceRule + { + int numentries; + PhysicsSFXEventAssetCameraDistanceEntry* entries; + }; + + struct PhysicsSFXEventAssetFrequencyEntry + { + int maxEvents; + PhysicsSFXEventAssetRules rules; + }; + + struct PhysicsSFXEventAssetFrequencyRule + { + int numentries; + PhysicsSFXEventAssetFrequencyEntry* entries; + }; + + union PhysicsSFXEventAssetRuleUnion + { + PhysicsSFXEventAssetSoundRule soundRule; + PhysicsSFXEventAssetMaterialRule materialRule; + PhysicsSFXEventAssetAngleRule angleRule; + PhysicsSFXEventAssetSpeedRule speedRule; + PhysicsSFXEventAssetMassRule massRule; + PhysicsSFXEventAssetGravityRule gravityRule; + PhysicsSFXEventAssetAtmosphereRule atmosphereRule; + PhysicsSFXEventAssetCameraDistanceRule cameraDistanceRule; + PhysicsSFXEventAssetFrequencyRule frequencyRule; + }; + + struct PhysicsSFXEventAssetRule + { + PhysicsSFXEventAssetRuleType type; + PhysicsSFXEventAssetRuleUnion u; + }; + + struct PhysicsSFXEventAsset + { + const char* name; + int priority; + PhysicsSFXEventAssetRules rules; + }; assert_sizeof(PhysicsSFXEventAsset, 0x20); + + struct PhysicsVFXEventAssetRule; + struct PhysicsVFXEventAssetRules + { + int numrules; + PhysicsVFXEventAssetRule* rules; + }; + + enum PhysicsVFXEventAssetRuleType + { + Types_PhysicsVFXEventRule = 0x0, + Types_PhysicsVFXEventParticleEffectRule = 0x1, + Types_PhysicsVFXEventMaterialRule = 0x2, + Types_PhysicsVFXEventAngleRule = 0x3, + Types_PhysicsVFXEventSpeedRule = 0x4, + Types_PhysicsVFXEventMassRule = 0x5, + Types_PhysicsVFXEventGravityRule = 0x6, + Types_PhysicsVFXEventAtmosphereRule = 0x7, + Types_PhysicsVFXEventCameraDistanceRule = 0x8, + Types_PhysicsVFXEventFrequencyRule = 0x9, + Types_PhysicsVFXEventRuleCount = 0xA, + }; + + struct PhysicsVFXEventAssetParticleEffectRule + { + char* hitParticleEffectAlias; + char* scrapeParticleEffectAlias; + }; + + struct PhysicsVFXEventAssetMaterialEntry + { + int surfaceType; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetMaterialRule + { + int numentries; + PhysicsVFXEventAssetMaterialEntry* entries; + }; + + struct PhysicsVFXEventAssetAngleEntry + { + float angle; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetAngleRule + { + int numentries; + PhysicsVFXEventAssetAngleEntry* entries; + }; + + struct PhysicsVFXEventAssetSpeedEntry + { + float speedThreshold; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetSpeedRule + { + int numentries; + PhysicsVFXEventAssetSpeedEntry* entries; + }; + + struct PhysicsVFXEventAssetMassEntry + { + float massThreshold; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetMassRule + { + int numentries; + PhysicsVFXEventAssetMassEntry* entries; + }; + + struct PhysicsVFXEventAssetGravityEntry + { + bool gravity; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetGravityRule + { + int numentries; + PhysicsVFXEventAssetGravityEntry* entries; + }; + + struct PhysicsVFXEventAssetAtmosphereEntry + { + bool atmosphere; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetAtmosphereRule + { + int numentries; + PhysicsVFXEventAssetAtmosphereEntry* entries; + }; + + struct PhysicsVFXEventAssetCameraDistanceEntry + { + float distance; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetCameraDistanceRule + { + int numentries; + PhysicsVFXEventAssetCameraDistanceEntry* entries; + }; + + struct PhysicsVFXEventAssetFrequencyEntry + { + int maxEvents; + PhysicsVFXEventAssetRules rules; + }; + + struct PhysicsVFXEventAssetFrequencyRule + { + int numentries; + PhysicsVFXEventAssetFrequencyEntry* entries; + }; + + union PhysicsVFXEventAssetRuleUnion + { + PhysicsVFXEventAssetParticleEffectRule particleEffectRule; + PhysicsVFXEventAssetMaterialRule materialRule; + PhysicsVFXEventAssetAngleRule angleRule; + PhysicsVFXEventAssetSpeedRule speedRule; + PhysicsVFXEventAssetMassRule massRule; + PhysicsVFXEventAssetGravityRule gravityRule; + PhysicsVFXEventAssetAtmosphereRule atmosphereRule; + PhysicsVFXEventAssetCameraDistanceRule cameraDistanceRule; + PhysicsVFXEventAssetFrequencyRule frequencyRule; + }; + + struct PhysicsVFXEventAssetRule + { + PhysicsVFXEventAssetRuleType type; + PhysicsVFXEventAssetRuleUnion u; + }; + + struct PhysicsVFXEventAsset + { + const char* name; + int priority; + PhysicsVFXEventAssetRules rules; + }; assert_sizeof(PhysicsVFXEventAsset, 0x20); + + struct PhysicsAsset + { + const char* name; + unsigned int havokDataSize; + char* havokData; + char __pad0[8]; + int numConstraints; + int numSFXEventAssets; + void** sfxEventAssets; + int numVFXEventAssets; + void** vfxEventAssets; + char __pad1[16]; + }; assert_sizeof(PhysicsAsset, 0x50); + + struct PhysicsFXPipeline + { + const char* name; + char __pad0[24]; + }; assert_sizeof(PhysicsFXPipeline, 0x20); + + struct PhysicsFXShape + { + const char* name; + unsigned int size; + vec3_t* vecs; + }; assert_sizeof(PhysicsFXShape, 0x18); + + union XAnimIndices + { + char* _1; + unsigned short* _2; + void* data; + }; + + struct XAnimNotifyInfo + { + scr_string_t name; + float time; + }; + + union XAnimDynamicFrames + { + char(*_1)[3]; + unsigned short(*_2)[3]; + }; + + union XAnimDynamicIndices + { + char _1[1]; + unsigned short _2[1]; + }; + + struct XAnimPartTransFrames + { + vec3_t mins; + vec3_t size; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + vec3_t frame0; + }; + + struct XAnimPartTrans + { + unsigned short size; + char smallTrans; + XAnimPartTransData u; + }; + + struct XAnimDeltaPartQuatDataFrames2 + { + short(*frames)[2]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData2 + { + XAnimDeltaPartQuatDataFrames2 frames; + short frame0[2]; + }; + + struct XAnimDeltaPartQuat2 + { + unsigned short size; + XAnimDeltaPartQuatData2 u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + short(*frames)[4]; + XAnimDynamicIndices indices; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + short frame0[4]; + }; + + struct XAnimDeltaPartQuat + { + unsigned short size; + XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans* trans; + XAnimDeltaPartQuat2* quat2; + XAnimDeltaPartQuat* quat; + }; + + struct XAnimParts + { + const char* name; // 0 + scr_string_t* names; // 8 + char* dataByte; // 16 + short* dataShort; // 24 + int* dataInt; // 32 + short* randomDataShort; // 40 + char* randomDataByte; // 48 + int* randomDataInt; // 56 + XAnimIndices indices; // 64 + XAnimNotifyInfo* notify; // 72 + XAnimDeltaPart* deltaPart; // 80 + unsigned int randomDataShortCount; // 88 + unsigned int randomDataByteCount; // 92 + unsigned int indexCount; // 96 + float framerate; // 100 + float frequency; // 104 + unsigned short dataByteCount; // 108 + unsigned short dataShortCount; // 110 + unsigned short dataIntCount; // 112 + unsigned short randomDataIntCount; // 114 + unsigned short numframes; // 116 + char flags; // 118 + char boneCount[10]; // 119 + char notifyCount; // 129 + char assetType; // 130 + char ikType; // 131 + }; assert_sizeof(XAnimParts, 0x88); + + struct XSurfaceCollisionAabb + { + unsigned short mins[3]; + unsigned short maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned short childBeginIndex; + unsigned short childCount; + }; + + struct XSurfaceCollisionLeaf + { + unsigned short triangleBeginIndex; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode* nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf* leafs; + }; + + struct XRigidVertList + { + unsigned short boneOffset; + unsigned short vertCount; + unsigned short triOffset; + unsigned short triCount; + XSurfaceCollisionTree* collisionTree; + }; + + union PackedUnitVec + { + unsigned int packed; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union GfxColor + { + unsigned char array[4]; + unsigned int packed; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxPackedMotionVertex + { + float xyz[3]; + float binormalSignAndHeight; + GfxColor pieceIndex; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + union GfxVertexUnion0 + { + GfxPackedVertex* packedVerts0; + GfxPackedMotionVertex* packedMotionVerts0; + void* verts0; + }; + + struct Face + { + unsigned short v1; + unsigned short v2; + unsigned short v3; + }; + + typedef unsigned int XBlendInfo; + + struct XSubdivRigidVertList + { + unsigned int firstFace; + unsigned int faceCount; + unsigned int firstRegularPatch; + unsigned int regularPatchCount; + }; + + struct XSurfaceSubdivLevel + { + XSubdivRigidVertList* rigidVertLists; + unsigned int faceCount; + unsigned int regularPatchCount; + unsigned int regularPatchOffset; + unsigned int facePointCount; + unsigned int facePointValence4Count; + unsigned int facePointBufferSize; + unsigned int edgePointCount; + unsigned int edgePointSmoothEnd; + unsigned int edgePointUVBorderEnd; + unsigned int vertexPointCount; + unsigned int vertexPointValence4Count; + unsigned int vertexPointBufferSize; + unsigned int normalCount; + unsigned int normalBufferSize; + unsigned int transitionPointCount; + unsigned int vertCount; + unsigned int vertOffset; + unsigned short* faceIndices; + unsigned short* regularPatchIndices; + unsigned int* regularPatchFlags; + unsigned int* facePoints; + unsigned int* edgePoints; + unsigned int* vertexPoints; + unsigned int* normals; + unsigned int* transitionPoints; + float* regularPatchCones; + ID3D11Buffer* regularPatchIndexBuffer; + ID3D11Buffer* faceIndexBuffer; + ID3D11ShaderResourceView* regularPatchIndexBufferView; + ID3D11ShaderResourceView* regularPatchFlagsView; + ID3D11ShaderResourceView* facePointsView; + ID3D11ShaderResourceView* edgePointsView; + ID3D11ShaderResourceView* vertexPointsView; + ID3D11ShaderResourceView* normalsView; + ID3D11ShaderResourceView* transitionPointsView; + ID3D11ShaderResourceView* regularPatchConesView; + }; assert_sizeof(XSurfaceSubdivLevel, 0xE8); + + struct GfxSubdivCache + { + unsigned int size; + ID3D11Buffer* subdivCacheBuffer; + ID3D11ShaderResourceView* subdivCacheView; + }; assert_sizeof(GfxSubdivCache, 0x18); + + struct XSurfaceSubdivInfo + { + XSurfaceSubdivLevel* levels; + char __pad0[24]; + GfxSubdivCache cache; + }; assert_sizeof(XSurfaceSubdivInfo, 0x38); + + struct TensionData + { + char __pad0[32]; + }; + + struct SHProbeSimplexData0 + { + char __pad0[32]; + }; + + struct SHProbeSimplexData1 + { + char __pad0[8]; + }; + + struct SHProbeSimplexData2 + { + char __pad0[24]; + }; + + union SHProbeSimplexDataUnion + { + SHProbeSimplexData0 data0; + SHProbeSimplexData1 data1; + SHProbeSimplexData2 data2; + }; + + struct XSurface + { + unsigned short flags; // 0 + unsigned short vertCount; // 2 + unsigned short triCount; // 4 + char rigidVertListCount; // 6 + char subdivLevelCount; // 7 + short blendVertCounts[8]; // 8 + int unkBlendCount; // 24 + char __pad0[4]; // 28 + GfxVertexUnion0 verts0; // 32 + Face* triIndices; // 40 + ID3D11Buffer* vb0; // 48 + ID3D11ShaderResourceView* vb0View; // 56 + ID3D11Buffer* indexBuffer; // 64 + XRigidVertList* rigidVertLists; // 72 + char* unkBlend; // 80 + ID3D11Buffer* blendVertsBuffer; // 88 + ID3D11ShaderResourceView* blendVertsView; // 96 + __int64* lmapUnwrap; // 104 + ID3D11Buffer* vblmapBuffer; // 112 + ID3D11ShaderResourceView* vblmapView; // 120 + XSurfaceSubdivInfo* subdiv; // 128 + XBlendInfo* blendVerts; // 136 + TensionData* tensionData; // 144 + void* tensionAccumTable; // 152 + ID3D11Buffer* tensionAccumTableBuffer; // 160 + ID3D11ShaderResourceView* tensionAccumTableView; // 168 + ID3D11Buffer* tensionDataBuffer; // 176 + ID3D11ShaderResourceView* tensionDataView; // 184 + ID3D11ShaderResourceView* indexBufferView; // 192 + SHProbeSimplexDataUnion* shProbeSimplexVertData; // 200 + ID3D11Buffer* shProbeSimplexVertBuffer; // 208 + ID3D11ShaderResourceView* shProbeSimplexVertBufferView; // 216 + int partBits[8]; // 224 + }; assert_sizeof(XSurface, 0x100); + + struct XModelSurfs + { + const char* name; + XSurface* surfs; + unsigned short numsurfs; + int partBits[8]; + }; assert_sizeof(XModelSurfs, 0x38); + + struct XModelAngle + { + short x; + short y; + short z; + short base; + }; + + struct XModelTagPos + { + float x; + float y; + float z; + }; + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + + struct ReactiveMotionModelPart + { + float center[3]; + float stiffness; + }; + + struct ReactiveMotionModelTweaks + { + float scale[4]; + }; + + struct XModelLodInfo + { + float dist; + unsigned short numsurfs; + unsigned short surfIndex; + XModelSurfs* modelSurfs; + int partBits[8]; + XSurface* surfs; + int unk; // subdivLodValidMask? + char flags; + char pad[3]; + }; assert_sizeof(XModelLodInfo, 0x40); + + struct XModelCollSurf_s + { + Bounds bounds; + int boneIdx; + int contents; + int surfFlags; + }; assert_sizeof(XModelCollSurf_s, 36); + + struct XBoneInfo + { + Bounds bounds; + union + { + float radiusSquared; + unsigned int radiusSquaredAsInt; + }; + }; + + struct XModel + { + const char* name; // 0 + char __pad0[12]; + unsigned char numBones; // 20 + unsigned char numRootBones; // 21 + unsigned char numReactiveMotionParts; // 22 + char b; + unsigned short numClientBones; // 24 + unsigned char numsurfs; // 26 + char __pad1[5]; + unsigned short numCollSurfs; // 32 + char __pad2[38]; + unsigned char unk1Count; // 72 + char __pad3[15]; + ScriptableDef* scriptableMoverDef; // 88 + XAnimProceduralBones* proceduralBones; // 96 + scr_string_t* aimAssistBones; // 104 + unsigned char numAimAssistBones; // 112 + char __pad4[39]; + scr_string_t* boneNames; // 152 + unsigned char* parentList; // 160 + XModelAngle* tagAngles; // 168 + XModelTagPos* tagPositions; // 176 + unsigned char* partClassification; // 184 + DObjAnimMat* baseMat; // 192 + ReactiveMotionModelPart* reactiveMotionParts; // 200 + ReactiveMotionModelTweaks* reactiveMotionTweaks; // 208 + Material** materialHandles; // 216 + XModelLodInfo lodInfo[6]; // 224 + XModelCollSurf_s* collSurfs; // 608 + XBoneInfo* boneInfo; // 616 + unsigned short* invHighMipRadius; // 624 + PhysicsAsset* physAsset; // 632 + PhysicsFXShape* physFxShape; // 640 + char* unk1; // 648 + unsigned short unkNamesCount; // 656 + char __pad5[6]; + scr_string_t* unkNames; // 664 + char __pad6[16]; + int unk2Count; + char __pad8[4]; + char* unk2; + char __pad9[1]; + unsigned char unkVec3Count; + char __pad10[6]; + vec3_t* unkVec3; + char __pad11[1]; + char unk3Count; + char __pad12[6]; + void* unk3; + }; assert_sizeof(XModel, 0x2E0); // 736 + + struct MayhemModel + { + XModel* model; + }; + + struct MayhemTransBounds + { + vec3_t mins; + vec3_t size; + }; + + struct GfxWrappedBaseBuffer + { + ID3D11Resource* buffer; + //ID3D11ShaderResourceView* view; + }; + + struct MayhemAnimFramesSplineCompressed + { + char* diskQuat; + char* diskPos; + unsigned __int16* diskQuatFrames; + unsigned __int16* diskPosFrames; + unsigned __int16* numDiskQuatFrames; + unsigned __int16* numDiskPosFrames; + MayhemTransBounds* transBounds; + GfxWrappedBaseBuffer quat; + GfxWrappedBaseBuffer pos; + unsigned int quatStride; + unsigned int posStride; + unsigned int totalCompressedQuatFrames; + unsigned int totalCompressedPosFrames; + }; assert_sizeof(MayhemAnimFramesSplineCompressed, 0x58); + + struct MayhemAnimFramesUncompressed + { + char* diskQuat; + char* diskPos; + MayhemTransBounds* transBounds; + GfxWrappedBaseBuffer quat; + GfxWrappedBaseBuffer pos; + unsigned int quatStride; + unsigned int posStride; + }; assert_sizeof(MayhemAnimFramesUncompressed, 0x30); + + union MayhemFramesUnion + { + void* data; + MayhemAnimFramesSplineCompressed* splineCompressedFrames; + MayhemAnimFramesUncompressed* uncompressedFrames; + }; + + struct MayhemDataKeysSplineCompressed + { + char* keys; + unsigned __int16* numKeys; + unsigned __int16* keyFrames; + unsigned int totalCompressedKeyFrames; + unsigned int numStreams; + unsigned int keyStride; + }; assert_sizeof(MayhemDataKeysSplineCompressed, 0x28); + + struct MayhemDataKeysUncompressed + { + char* keys; + unsigned int numStreams; + unsigned int keyStride; + }; assert_sizeof(MayhemDataKeysUncompressed, 0x10); + + union MayhemDataChannelsUnion + { + void* data; + MayhemDataKeysSplineCompressed* splineCompressedKeys; + MayhemDataKeysUncompressed* uncompressedKeys; + }; + + struct MayhemAnim + { + MayhemFramesUnion frames; + unsigned int numBones; + unsigned int numFrames; + float framerate; + float frequency; + unsigned int notifyCount; + unsigned int dataChannelCount; + bool isLooping; + bool isSplineCompressed; + bool quantizeQuats; + bool quantizeTrans; + bool quantizeChannels; + XAnimNotifyInfo* notify; + MayhemDataChannelsUnion dataChannels; + }; assert_sizeof(MayhemAnim, 56); + + struct MayhemObject + { + unsigned int modelIndex; + unsigned int animIndex; + }; + + struct MayhemData + { + const char* name; + XModel* preModel; + XModel* postModel; + unsigned int numModels; + MayhemModel* models; + unsigned int numAnims; + MayhemAnim* anims; + unsigned int numObjects; + MayhemObject* objects; + float length; + }; assert_sizeof(MayhemData, 0x50); + + enum MaterialTechniqueType + { + TECHNIQUE_UNLIT = 8, + TECHNIQUE_EMISSIVE = 10, + TECHNIQUE_LIT = 13, + }; + + struct Packed128 + { + std::uint64_t p0; + std::uint64_t p1; + }; + + struct GfxDrawSurfFields + { + + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + Packed128 packed; + }; + + enum SurfaceTypeBits : std::uint64_t + { + + }; + + struct MaterialInfo + { + const char* name; + unsigned char gameFlags; + unsigned char sortKey; + unsigned char textureAtlasRowCount; + unsigned char textureAtlasColumnCount; + unsigned char textureAtlasFrameBlend; + unsigned char textureAtlasAsArray; + unsigned char renderFlags; + GfxDrawSurf drawSurf; + SurfaceTypeBits surfaceTypeBits; + unsigned int hashIndex; + }; assert_sizeof(MaterialInfo, 48); + + struct MaterialTextureDef + { + unsigned int nameHash; + char nameStart; + char nameEnd; + unsigned char samplerState; + unsigned char semantic; + GfxImage* image; + }; assert_sizeof(MaterialTextureDef, 16); + + struct MaterialConstantDef + { + unsigned int nameHash; + char name[12]; + float literal[4]; + }; assert_sizeof(MaterialConstantDef, 32); + + struct GfxStateBits + { + unsigned int loadBits[6]; // loadbits[3], blendstatebits[3] + unsigned short zone; + unsigned char depthStencilState[14]; + unsigned char blendState; + unsigned char rasterizerState; + }; assert_sizeof(GfxStateBits, 44); + assert_offsetof(GfxStateBits, zone, 24); + + struct MaterialConstantBufferDef + { + unsigned int vsDataSize; + unsigned int hsDataSize; + unsigned int dsDataSize; + unsigned int psDataSize; + unsigned int vsOffsetDataSize; + unsigned int hsOffsetDataSize; + unsigned int dsOffsetDataSize; + unsigned int psOffsetDataSize; + unsigned char* vsData; + unsigned char* hsData; + unsigned char* dsData; + unsigned char* psData; + unsigned short* vsOffsetData; + unsigned short* hsOffsetData; + unsigned short* dsOffsetData; + unsigned short* psOffsetData; + ID3D11Buffer* vsConstantBuffer; + ID3D11Buffer* hsConstantBuffer; + ID3D11Buffer* dsConstantBuffer; + ID3D11Buffer* psConstantBuffer; + }; + + struct Material + { + union + { + const char* name; + MaterialInfo info; + }; + unsigned char textureCount; + unsigned char constantCount; + unsigned char stateFlags; + unsigned char cameraRegion; + unsigned char materialType; + unsigned char layerCount; + unsigned char assetFlags; + char pad; + MaterialTechniqueSet* techniqueSet; + MaterialTextureDef* textureTable; + MaterialConstantDef* constantTable; + unsigned char stateBitsEntry[152]; + unsigned char stateBitsCount; + GfxStateBits* stateBitsTable; + unsigned char* constantBufferIndex; // [152] + MaterialConstantBufferDef* constantBufferTable; + unsigned char constantBufferCount; + const char** subMaterials; + }; assert_sizeof(Material, 0x118); + assert_offsetof(Material, techniqueSet, 56); + assert_offsetof(Material, textureCount, 48); + assert_offsetof(Material, constantCount, 49); + assert_offsetof(Material, stateBitsTable, 240); + assert_offsetof(Material, constantBufferIndex, 248); + assert_offsetof(Material, stateBitsCount, 232); + assert_offsetof(Material, constantBufferTable, 256); + assert_offsetof(Material, constantBufferCount, 264); + assert_offsetof(Material, subMaterials, 272); + assert_offsetof(Material, layerCount, 53); + + struct GfxComputeShaderLoadDef + { + unsigned char* program; + unsigned int programSize; + }; + + struct ComputeShaderProgram + { + ID3D11ComputeShader* cs; + GfxComputeShaderLoadDef loadDef; + }; + + struct ComputeShader + { + const char* name; + const char* debugName; + ComputeShaderProgram prog; + }; assert_sizeof(ComputeShader, 0x28); + + struct GfxPixelShaderLoadDef + { + unsigned char* program; + unsigned int programSize; + unsigned int microCodeCrc; + }; + + struct MaterialPixelShaderProgram + { + ID3D11PixelShader* ps; + GfxPixelShaderLoadDef loadDef; + }; + + struct MaterialPixelShader + { + const char* name; + const char* debugName; + MaterialPixelShaderProgram prog; + }; assert_sizeof(MaterialPixelShader, 0x28); + + struct GfxVertexShaderLoadDef + { + unsigned char* program; + unsigned int programSize; + unsigned int microCodeCrc; + }; + + struct MaterialVertexShaderProgram + { + ID3D11VertexShader* vs; + GfxVertexShaderLoadDef loadDef; + }; + + struct MaterialVertexShader + { + const char* name; + const char* debugName; + MaterialVertexShaderProgram prog; + }; assert_sizeof(MaterialVertexShader, 0x28); + + struct GfxHullShaderLoadDef + { + unsigned char* program; + unsigned int programSize; + }; + + struct MaterialHullShaderProgram + { + ID3D11HullShader* hs; + GfxHullShaderLoadDef loadDef; + }; + + struct MaterialHullShader + { + const char* name; + const char* debugName; + MaterialHullShaderProgram prog; + }; assert_sizeof(MaterialHullShader, 0x28); + + struct GfxDomainShaderLoadDef + { + unsigned char* program; + unsigned int programSize; + }; + + struct MaterialDomainShaderProgram + { + ID3D11DomainShader* ds; + GfxDomainShaderLoadDef loadDef; + }; + + struct MaterialDomainShader + { + const char* name; + const char* debugName; + MaterialDomainShaderProgram prog; + }; assert_sizeof(MaterialDomainShader, 0x28); + + struct MaterialStreamRouting + { + unsigned char source; + unsigned char dest; + unsigned char mask; + }; + + struct MaterialVertexStreamRouting + { + MaterialStreamRouting data[34]; + ID3D11InputLayout* decl[270]; + }; + + struct MaterialVertexDeclaration + { + const char* name; + unsigned char streamCount; + bool hasOptionalSource; + MaterialVertexStreamRouting routing; + }; assert_sizeof(MaterialVertexDeclaration, 0x8E8); + + struct MaterialArgumentCodeConst + { + unsigned short index; + unsigned char firstRow; + unsigned char rowCount; + }; + + union MaterialArgumentDef + { + const float* literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; + }; + + enum MaterialShaderArgumentType : std::uint8_t + { + + }; + + struct MaterialShaderArgument + { + unsigned char type; + unsigned char shader; + unsigned short dest; + MaterialArgumentDef u; + }; assert_sizeof(MaterialShaderArgument, 0x10); + + struct MaterialPass + { + MaterialVertexShader* vertexShader; + MaterialVertexDeclaration* vertexDecl; + MaterialHullShader* hullShader; + MaterialDomainShader* domainShader; + MaterialPixelShader* pixelShader; + unsigned char pixelOutputMask; + unsigned char perPrimArgCount; + unsigned char perObjArgCount; + unsigned char stableArgCount; + unsigned short perPrimArgSize; + unsigned short perObjArgSize; + unsigned short stableArgSize; + unsigned short zone; + char __pad0[44]; + MaterialShaderArgument* args; + }; assert_sizeof(MaterialPass, 104); + assert_offsetof(MaterialPass, zone, 50); + assert_offsetof(MaterialPass, args, 96); + + struct MaterialTechniqueHeader + { + const char* name; + unsigned short flags; + unsigned short passCount; + }; assert_sizeof(MaterialTechniqueHeader, 0x10); + + struct MaterialTechnique + { + MaterialTechniqueHeader hdr; + MaterialPass passArray[1]; + }; + + struct MaterialTechniqueSet + { + const char* name; + unsigned short flags; + unsigned char worldVertFormat; + unsigned char preDisplacementOnlyCount; + unsigned int techniqueCount; + char __pad0[32]; + MaterialTechnique* techniques; + }; assert_sizeof(MaterialTechniqueSet, 0x38); + assert_offsetof(MaterialTechniqueSet, techniqueCount, 12); + assert_offsetof(MaterialTechniqueSet, techniques, 48); + + enum MapType : std::uint8_t + { + MAPTYPE_NONE = 0x0, + MAPTYPE_INVALID1 = 0x1, + MAPTYPE_1D = 0x2, + MAPTYPE_2D = 0x3, + MAPTYPE_3D = 0x4, + MAPTYPE_CUBE = 0x5, + MAPTYPE_ARRAY = 0x6, + MAPTYPE_CUBE_ARRAY = 0x7, + MAPTYPE_COUNT = 0x8, + }; + + struct GfxImageStreamData + { + unsigned short width; + unsigned short height; + unsigned int pixelSize; + }; + + struct GfxImage + { + char __pad0[24]; + DXGI_FORMAT imageFormat; // 24 + char flags; // 28 + char __pad1[3]; + MapType mapType; // 32 + char sematic; // 33 + char category; // 34 + unsigned char platform[2]; // 35 + char __pad2[3]; + unsigned int dataLen1; // 40 + unsigned int dataLen2; // 44 + unsigned short width; // 48 + unsigned short height; // 50 + unsigned short depth; // 52 + unsigned short numElements; // 54 + unsigned char mipmapCount; // 56 + bool streamed; // 57 + char __pad3[6]; + char* pixelData; // 64 + GfxImageStreamData streams[4]; // 72 + const char* name; // 104 + }; assert_sizeof(GfxImage, 0x70); + + struct snd_volmod_info_t + { + char name[64]; + unsigned int id; + float value; + }; + + struct SndGlobals + { + const char* name; + float globalVolumeModifier; + float binkVolumeModifier; + unsigned int volmodinfoCount; + snd_volmod_info_t* volmodinfo; + char __pad0[176]; + }; assert_sizeof(SndGlobals, 0xD0); + + struct SndAlias + { + const char* aliasName; + char __pad0[192]; + }; assert_sizeof(SndAlias, 200); + + struct SndAliasList + { + const char* aliasName; + unsigned int id; + SndAlias* head; + int count; + int sequence; + }; assert_sizeof(SndAliasList, 32); + assert_offsetof(SndAliasList, head, 16); + assert_offsetof(SndAliasList, count, 24); + + struct SndIndexEntry + { + unsigned short value; + unsigned short next; + }; assert_sizeof(SndIndexEntry, 4); + + struct SndSendEffectParams + { + char __pad0[136]; + }; assert_sizeof(SndSendEffectParams, 136); + + struct SoundTable + { + char __pad0[248]; + }; assert_sizeof(SoundTable, 248); + + struct SndDuck + { + char __pad0[144]; + }; assert_sizeof(SndDuck, 144); + + struct SndMusicState + { + char __pad0[384]; + }; assert_sizeof(SndMusicState, 384); + + struct SndMusicSet + { + char name[64]; + unsigned int id; + unsigned int stateCount; + SndMusicState* states; + }; assert_sizeof(SndMusicSet, 80); + assert_offsetof(SndMusicSet, stateCount, 68); + assert_offsetof(SndMusicSet, states, 72); + + struct SndBank + { + const char* name; + const char* zone; + const char* gameLanguage; + const char* soundLanguage; + unsigned int aliasCount; + SndAliasList* alias; + SndIndexEntry* aliasIndex; + unsigned int sendEffectCount; + SndSendEffectParams* sendEffects; + SoundTable soundTable; + unsigned int duckCount; + SndDuck* ducks; + unsigned int musicSetCount; + SndMusicSet* musicSets; + }; + assert_offsetof(SndBank, name, 0); + assert_offsetof(SndBank, zone, 8); + assert_offsetof(SndBank, gameLanguage, 16); + assert_offsetof(SndBank, soundLanguage, 24); + assert_offsetof(SndBank, aliasCount, 32); + assert_offsetof(SndBank, aliasIndex, 48); + assert_offsetof(SndBank, sendEffectCount, 56); + assert_offsetof(SndBank, sendEffects, 64); + assert_offsetof(SndBank, soundTable, 72); + assert_offsetof(SndBank, duckCount, 320); + assert_offsetof(SndBank, ducks, 328); + assert_offsetof(SndBank, musicSetCount, 336); + assert_offsetof(SndBank, musicSets, 344); + + struct SndBankResident + { + SndBank bank; + }; assert_sizeof(SndBankResident, 0x160); + + struct SndBankTransient + { + const char* name; + unsigned int elementCount; + unsigned int* elements; + }; assert_sizeof(SndBankTransient, 0x18); + + struct LocalizeEntry + { + const char* name; + const char* value; + }; + + struct RawFile + { + const char* name; + int compressedLen; + int len; + const char* buffer; + }; + + struct ScriptFile + { + const char* name; + int compressedLen; + int len; + int bytecodeLen; + const char* buffer; + char* bytecode; + }; + + struct StringTableCell + { + const char* string; + int hash; + }; + + struct StringTable + { + const char* name; + int columnCount; + int rowCount; + StringTableCell* values; + }; + + enum NetConstStringType + { + }; + + enum NetConstStringSource + { + }; + + struct NetConstStrings + { + const char* name; + NetConstStringType stringType; + NetConstStringSource sourceType; + unsigned int flags; + unsigned int entryCount; + const char** stringList; + }; assert_sizeof(NetConstStrings, 0x20); + + struct LuaFile + { + const char* name; + int len; + char strippingType; + const char* buffer; + }; + + struct TTFDef + { + const char* name; + int fileLen; + const char* file; + void* ftFace; + }; assert_sizeof(TTFDef, 0x20); + + union XAssetHeader + { + void* data; + PhysicsLibrary* physicsLibrary; + PhysicsSFXEventAsset* physicsSFXEventAsset; + PhysicsVFXEventAsset* physicsVFXEventAsset; + PhysicsAsset* physicsAsset; + PhysicsFXPipeline* physicsFXPipeline; + PhysicsFXShape* physicsFXShape; + XAnimParts* parts; + XModelSurfs* modelSurfs; + XModel* model; + MayhemData* mayhem; + Material* material; + //ComputeShader* computeShader; + //MaterialVertexShader* vertexShader; + //MaterialHullShader* hullShader; + //MaterialDomainShader* domainShader; + //MaterialPixelShader* pixelShader; + //MaterialVertexDeclaration* vertexDecl; + //MaterialTechniqueSet* techniqueSet; + GfxImage* image; + SndGlobals* soundGlobals; + SndBankResident* soundBankResident; + SndBankTransient* soundBankTransient; + //clipMap_t* clipMap; + //ComWorld* comWorld; + //GlassWorld* glassWorld; + //PathData* pathData; + //NavMeshData* navMeshData; + //MapEnts* mapEnts; + //FxWorld* fxWorld; + //GfxWorld* gfxWorld; + //GfxWorldTransientZone* gfxWorldTransientZone; + //GfxIESProfile* iesProfile; + //GfxLightDef* lightDef; + //void* uiMap; + //AnimationClass* animClass; + //PlayerAnimScript* playerAnim; + //Gesture* gesture; + LocalizeEntry* localize; + //WeaponAttachment* attachment; + //WeaponCompleteDef* weapon; + //ParticleSystemDef* vfx; + //FxImpactTable* impactFx; + //SurfaceFxTable* surfaceFx; + //void* aiType; + //void* mpType; + //void* character; + //void* modelAlias; + //void* unknown; + RawFile* rawfile; + ScriptFile* scriptfile; + StringTable* stringTable; + //LeaderboardDef* leaderboardDef; + //VirtualLeaderboardDef* virtualLeaderboardDef; + //DDLFile* ddlFile; + //TracerDef* tracerDef; + //VehicleDef* vehDef; + //AddonMapEnts* addonMapEnts; + NetConstStrings* netConstStrings; + LuaFile* luaFile; + //ScriptableDef* scriptable; + //EquipmentSoundTable* equipSndTable; + //VectorField* vectorField; + //FxParticleSimAnimation* particleSimAnimation; + //StreamingInfo* streamingInfo; + //LaserDef* laserDef; + TTFDef* ttfDef; + //SuitDef* suitDef; + //SuitAnimPackage* suitAnimPackage; + //SpaceshipTargetDef* spaceshipTargetDef; + //RumbleInfo* rumble; + //RumbleGraph* rumbleGraph; + //WeaponAnimPackage* weaponAnimPackage; + //WeaponSFXPackage* weaponSFXPackage; + //WeaponVFXPackage* weaponVFXPackage; + //BehaviorTree* behaviorTree; + //XAnimArcheType* archeType; + //XAnimProceduralBones *proceduralBones; + //ReticleDef* reticleDef; + //GfxLightMap* lightMap; + }; + + struct XAsset + { + XAssetType type; + XAssetHeader header; + }; + + struct XAssetEntry + { + XAssetHeader header; + int nextHash; + int nextOverride; // or nextPoolEntry + char inuseMask; + unsigned char type; + unsigned short zoneIndex; + }; assert_sizeof(XAssetEntry, 0x18); + + struct ScriptStringList + { + int count; + const char** strings; + }; + + union GfxZoneTableEntry + { + char* dataPtr; + void* voidPtr; + ID3D11Buffer* buffer; + ID3D11DepthStencilState* depthStencilState; + ID3D11BlendState* blendState; + }; + + typedef std::uint32_t GfxBlendStateBits[3]; + + struct XGfxGlobals + { + unsigned int depthStencilStateCount; + unsigned int blendStateCount; + std::uint64_t* depthStencilStateBits; + GfxBlendStateBits* blendStateBits; + GfxZoneTableEntry* depthStencilStates; + GfxZoneTableEntry* blendStates; + unsigned int perPrimConstantBufferCount; + unsigned int perObjConstantBufferCount; + unsigned int stableConstantBufferCount; + unsigned int* perPrimConstantBufferSizes; + unsigned int* perObjConstantBufferSizes; + unsigned int* stableConstantBufferSizes; + GfxZoneTableEntry* perPrimConstantBuffers; + GfxZoneTableEntry* perObjConstantBuffers; + GfxZoneTableEntry* stableConstantBuffers; + }; + + struct XGlobals + { + XGfxGlobals* gfxGlobals; + }; + + struct XAssetList + { + ScriptStringList stringList; + int assetCount; + XAsset* assets; + XGlobals* globals; + }; + + enum DBSyncMode : std::int8_t + { + DB_LOAD_SYNC = 0x0, + DB_LOAD_ASYNC = 0x1, + }; + + enum DBAllocFlags : std::int32_t + { + DB_ZONE_NONE = 0x0, + DB_ZONE_COMMON = 0x1, + DB_ZONE_GAME = 0x4, // maybe + DB_ZONE_LOAD = 0x100, + DB_ZONE_CUSTOM = 0x1000000 // added for custom zone loading + }; + + struct XZoneInfo + { + const char* name; + int allocFlags; + }; + + struct DBFile + { + char name[64]; + void* handle; + char __pad0[46]; + bool isSecure; + char __pad1[49]; + }; assert_sizeof(DBFile, 0xA8); + assert_offsetof(DBFile, isSecure, 118); + + struct db_internal_state + { + int dummy; + }; + + struct db_z_stream_s + { + unsigned char* next_in; + unsigned int avail_in; + unsigned int total_in; + unsigned char* next_out; + unsigned int avail_out; + unsigned int total_out; + char* msg; + db_internal_state* state; + unsigned __int8* (__fastcall* zalloc)(unsigned __int8*, unsigned int, unsigned int); + void(__fastcall* zfree)(unsigned __int8*, unsigned __int8*); + unsigned __int8* opaque; + int data_type; + unsigned long adler; + unsigned long reserved; + }; + + enum DB_CompressorType + { + DB_COMPRESSOR_INVALID = 0x0, + DB_COMPRESSOR_PASSTHROUGH = 0x1, + DB_COMPRESSOR_BLOCK = 0x2, + }; + + struct DB_ReadStream + { + unsigned char* next_in; + unsigned __int64 avail_in; + unsigned __int64 total_in; + unsigned char* next_out; + unsigned __int64 avail_out; + unsigned __int64 total_out; + DB_CompressorType compressor; + }; + + struct DB_LoadData + { + DBFile* file; + unsigned __int8* fileBuffer; + unsigned __int64 readSize; + unsigned __int64 completedReadSize; + unsigned __int64 offset; + unsigned __int8* start_in; + DB_ReadStream stream; + }; + + enum XFileBlock + { + XFILE_BLOCK_TEMP = 0x0, + XFILE_BLOCK_UNK1 = 0x1, + XFILE_BLOCK_UNK2 = 0x2, + XFILE_BLOCK_IMAGE_STREAM = 0x3, + XFILE_BLOCK_SHARED_STREAM = 0x4, + XFILE_BLOCK_UNK5 = 0x5, + XFILE_BLOCK_RUNTIME = 0x6, + XFILE_BLOCK_UNK7 = 0x7, + XFILE_BLOCK_VIRTUAL = 0x8, + XFILE_BLOCK_SCRIPT = 0x9, + MAX_XFILE_COUNT = 0xA, + }; + + struct XBlock + { + char* alloc; + unsigned __int64 size; + }; + + struct XZoneMemory + { + XBlock blocks[MAX_XFILE_COUNT]; + char __pad0[112]; + void* shared_ff_data; + unsigned int shared_ff_count; + int padding1; + void* unknown; // always 0 + void* image_ff_data; + unsigned int image_ff_count; + int padding2; + char __pad1[0x100]; // unk size + }; + + struct XFileSignedInfo // sub_1409E6100 + { + char magic[8]; // IWffs100 + std::uint32_t unk; + char key[32]; + char sha[256]; + char name[32]; + char pad[4]; + }; + + struct XFileStreamData + { + std::uint64_t size; + std::uint64_t unk1; + std::uint64_t unk2; + std::uint64_t block_size[MAX_XFILE_COUNT]; + std::uint64_t unk_arr[8]; + }; assert_sizeof(XFileStreamData, 168); + + struct XFileHeader + { + char header[8]; + std::uint32_t version; + std::uint8_t unused; // (unused) + std::uint8_t has_no_image_fastfile; + std::uint8_t has_no_shared_fastfile; + std::uint8_t unk1; + std::uint32_t fileTimeHigh; // (unused) + std::uint32_t fileTimeLow; // (unused) + XFileStreamData stream_data; + std::uint32_t shared_ff_hash; // shared fastfile // (unused) + std::uint32_t shared_ff_count; // shared fastfile // (unused) + std::uint32_t image_ff_hash; // image fastfile // (unused) + std::uint32_t image_ff_count; // image fastfile // (unused) + // image streams 24 * sharedcount + // image streams 24 * imagecount + std::uint64_t fileLen; + std::uint64_t fileLenUnk1; + std::uint64_t fileLenUnk2; + // if signed: XFileSignedInfo info; + }; + + struct XFileReadData + { + bool header_parsed; + std::uint8_t has_no_image_fastfile; + std::uint8_t has_no_shared_fastfile; + std::uint8_t unk1; + char __pad0[4]; + XFileStreamData stream_data; + std::uint32_t pad; + std::uint32_t shared_ff_hash; + std::uint32_t shared_ff_count; + std::uint64_t fileLenUnk1; + std::uint32_t image_ff_hash; + std::uint32_t image_ff_count; + std::uint64_t fileLen; + std::uint64_t fileLenUnk2; + }; assert_sizeof(XFileReadData, 224); +} \ No newline at end of file diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index 83c1819f..8d0fba30 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -2,6 +2,7 @@ #include "game.hpp" #include +#include 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() { return cmd_args->argc[cmd_args->nesting]; @@ -179,4 +214,19 @@ namespace game Cbuf_AddCall(0, SV_CmdsMP_CheckLoadGame); } } -} \ No newline at end of file +} + +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(ptr)); +} diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp index ab503268..d616e6b5 100644 --- a/src/client/game/game.hpp +++ b/src/client/game/game.hpp @@ -2,6 +2,8 @@ #include "structs.hpp" +#define PROTOCOL 1 + namespace game { extern uint64_t base_address; @@ -40,6 +42,12 @@ namespace game 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(); const char* Cmd_Argv(int index); @@ -58,9 +66,8 @@ namespace game void SV_CmdsMP_RequestMapRestart(bool loadScripts, bool migrate); } -inline uintptr_t operator"" _b(const uintptr_t ptr) -{ - return game::base_address + ptr; -} +size_t operator"" _b(const size_t ptr); +size_t reverse_b(const size_t ptr); +size_t reverse_b(const void* ptr); #include "symbols.hpp" diff --git a/src/client/game/scripting/array.cpp b/src/client/game/scripting/array.cpp new file mode 100644 index 00000000..c2255f08 --- /dev/null +++ b/src/client/game/scripting/array.cpp @@ -0,0 +1,335 @@ +#include +#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 values) + { + this->id_ = make_array(); + + for (const auto& value : values) + { + this->push(value); + } + } + + array::array(std::unordered_map 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(this->id_)}); + } + } + + void array::release() const + { + if (this->id_) + { + game::RemoveRefToValue(game::VAR_POINTER, {static_cast(this->id_)}); + } + } + + std::vector array::get_keys() const + { + std::vector 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(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()) + { + return this->get(key.as()); + } + else + { + return this->get(key.as()); + } + } + + 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()) + { + this->set(key.as(), value); + } + else + { + this->set(key.as(), 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_); + } +} diff --git a/src/client/game/scripting/array.hpp b/src/client/game/scripting/array.hpp new file mode 100644 index 00000000..64a9269b --- /dev/null +++ b/src/client/game/scripting/array.hpp @@ -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); + array(std::unordered_map); + + array(const array& other); + array(array&& other) noexcept; + + ~array(); + + array& operator=(const array& other); + array& operator=(array&& other) noexcept; + + std::vector 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 + array_value operator[](const script_value& key) const + { + if (key.is()) + { + return { this->id_, this->get_value_id(key.as()) }; + } + + if (key.is()) + { + return { this->id_, this->get_value_id(key.as()) }; + } + + throw std::runtime_error("Invalid key type"); + } + + private: + void add() const; + void release() const; + + unsigned int id_; + }; +} diff --git a/src/client/game/scripting/entity.cpp b/src/client/game/scripting/entity.cpp new file mode 100644 index 00000000..1119220c --- /dev/null +++ b/src/client/game/scripting/entity.cpp @@ -0,0 +1,120 @@ +#include +#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(~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(this->entity_id_)}); + } + } + + void entity::release() const + { + if (this->entity_id_) + { + game::RemoveRefToValue(game::VAR_POINTER, {static_cast(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(const std::string& field) const + { + return get_entity_field(*this, field); + } + + script_value entity::call(const std::string& name, const std::vector& arguments) const + { + return call_function(name, *this, arguments); + } +} diff --git a/src/client/game/scripting/entity.hpp b/src/client/game/scripting/entity.hpp new file mode 100644 index 00000000..b1702379 --- /dev/null +++ b/src/client/game/scripting/entity.hpp @@ -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 + T get(const std::string& field) const; + + script_value call(const std::string& name, const std::vector& 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 + T entity::get(const std::string& field) const + { + return this->get(field).as(); + } +} diff --git a/src/client/game/scripting/event.hpp b/src/client/game/scripting/event.hpp new file mode 100644 index 00000000..bc1d53e0 --- /dev/null +++ b/src/client/game/scripting/event.hpp @@ -0,0 +1,13 @@ +#pragma once +#include "script_value.hpp" +#include "entity.hpp" + +namespace scripting +{ + struct event + { + std::string name; + entity entity{}; + std::vector arguments; + }; +} diff --git a/src/client/game/scripting/execution.cpp b/src/client/game/scripting/execution.cpp new file mode 100644 index 00000000..d72fc358 --- /dev/null +++ b/src/client/game/scripting/execution.cpp @@ -0,0 +1,251 @@ +#include +#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& 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& arguments) + { + const auto entref = entity.get_entity_reference(); + + const auto is_method_call = *reinterpret_cast(&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& arguments) + { + return call_function(name, entity(), arguments); + } + + template <> + script_value call(const std::string& name, const std::vector& arguments) + { + return call_function(name, arguments); + } + + script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector& 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(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& 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(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(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); + } +} diff --git a/src/client/game/scripting/execution.hpp b/src/client/game/scripting/execution.hpp new file mode 100644 index 00000000..c1f8fe6f --- /dev/null +++ b/src/client/game/scripting/execution.hpp @@ -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& arguments); + script_value call_function(const std::string& name, const entity& entity, + const std::vector& arguments); + + template + T call(const std::string& name, const std::vector& arguments = {}); + + template <> + script_value call(const std::string& name, const std::vector& arguments); + + template + T call(const std::string& name, const std::vector& arguments) + { + return call(name, arguments).as(); + } + + script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector& 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& 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& 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); +} diff --git a/src/client/game/scripting/function.cpp b/src/client/game/scripting/function.cpp new file mode 100644 index 00000000..a70cf95b --- /dev/null +++ b/src/client/game/scripting/function.cpp @@ -0,0 +1,44 @@ +#include + +#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 arguments) const + { + return exec_ent_thread(self, this->pos_, arguments); + } +} diff --git a/src/client/game/scripting/function.hpp b/src/client/game/scripting/function.hpp new file mode 100644 index 00000000..1af7b58d --- /dev/null +++ b/src/client/game/scripting/function.hpp @@ -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 arguments) const; + + script_value operator()(const entity& self, std::vector arguments) const + { + return this->call(self, arguments); + } + + script_value operator()(std::vector arguments) const + { + return this->call(*game::levelEntityId, arguments); + } + + script_value operator()() const + { + return this->call(*game::levelEntityId, {}); + } + private: + const char* pos_; + }; +} diff --git a/src/client/game/scripting/functions.cpp b/src/client/game/scripting/functions.cpp new file mode 100644 index 00000000..daf766ee --- /dev/null +++ b/src/client/game/scripting/functions.cpp @@ -0,0 +1,108 @@ +#include +#include "functions.hpp" + +#include + +#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(itr->second); + } + + if (const auto itr = first.find(name); itr != first.end()) + { + return static_cast(itr->second); + } + } + + if (const auto itr = first.find(name); itr != first.end()) + { + return static_cast(itr->second); + } + + if (const auto itr = second.find(name); itr != second.end()) + { + return static_cast(itr->second); + } + + return -1; + } + } + + std::uint32_t parse_token_id(const std::string& name) + { + if (name.starts_with("_ID")) + { + return static_cast(std::strtol(name.substr(3).data(), nullptr, 10)); + } + + if (name.starts_with("_id_")) + { + return static_cast(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(function_table)[index - 1]; + } + + return reinterpret_cast(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); + } +} \ No newline at end of file diff --git a/src/client/game/scripting/functions.hpp b/src/client/game/scripting/functions.hpp new file mode 100644 index 00000000..5bd1c764 --- /dev/null +++ b/src/client/game/scripting/functions.hpp @@ -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); +} diff --git a/src/client/game/scripting/safe_execution.cpp b/src/client/game/scripting/safe_execution.cpp new file mode 100644 index 00000000..d100a928 --- /dev/null +++ b/src/client/game/scripting/safe_execution.cpp @@ -0,0 +1,72 @@ +#include +#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) diff --git a/src/client/game/scripting/safe_execution.hpp b/src/client/game/scripting/safe_execution.hpp new file mode 100644 index 00000000..6eea59d2 --- /dev/null +++ b/src/client/game/scripting/safe_execution.hpp @@ -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); +} diff --git a/src/client/game/scripting/script_value.cpp b/src/client/game/scripting/script_value.cpp new file mode 100644 index 00000000..1cd7af4e --- /dev/null +++ b/src/client/game/scripting/script_value.cpp @@ -0,0 +1,356 @@ +#include +#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(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(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() const + { + return this->get_raw().type == game::VAR_INTEGER; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + 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() const + { + return this->get_raw().type == game::VAR_FLOAT; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + float script_value::get() const + { + return this->get_raw().u.floatValue; + } + + template <> + double script_value::get() const + { + return static_cast(this->get_raw().u.floatValue); + } + + /*************************************************************** + * String + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().type == game::VAR_STRING; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + const char* script_value::get() const + { + return game::SL_ConvertToString(static_cast(this->get_raw().u.stringValue)); + } + + template <> + std::string script_value::get() const + { + return this->get(); + } + + /*************************************************************** + * Array + **************************************************************/ + + template <> + bool script_value::is() 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>() 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() 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() 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() 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()) + { + return utils::string::va("%i", this->as()); + } + + if (this->is()) + { + return utils::string::va("%f", this->as()); + } + + if (this->is()) + { + return this->as(); + } + + if (this->is()) + { + const auto vec = this->as(); + return utils::string::va("(%g, %g, %g)", + vec.get_x(), + vec.get_y(), + vec.get_z() + ); + } + + if (this->is()) + { + const auto func = this->as(); + return utils::string::va("[[ %s ]]", func.get_name().data()); + } + + return this->type_name(); + } +} diff --git a/src/client/game/scripting/script_value.hpp b/src/client/game/scripting/script_value.hpp new file mode 100644 index 00000000..8f8428a1 --- /dev/null +++ b/src/client/game/scripting/script_value.hpp @@ -0,0 +1,223 @@ +#pragma once +#include "game/game.hpp" +#include "variable_value.hpp" +#include "vector.hpp" + +#include + +namespace scripting +{ + class entity; + class array; + class function; + class value_wrap; + + namespace + { + std::array 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", + "", + "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 + 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 + bool is() const; + + template + T as() const + { + if (!this->is()) + { + const auto type = get_typename(this->get_raw()); + const auto c_type = get_c_typename(); + throw std::runtime_error(std::format("has type '{}' but should be '{}'", type, c_type)); + } + + return get(); + } + + template + T* as_ptr() + { + const auto value = this->as(); + + if (!value) + { + throw std::runtime_error("is null"); + } + + return reinterpret_cast(value); + } + + const game::VariableValue& get_raw() const; + + variable_value value_{}; + private: + template + 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 + T as() const + { + try + { + return this->value_.as(); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); + } + } + + template + T* as_ptr() + { + try + { + return this->value_.as_ptr(); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); + } + } + + template + T is() const + { + return this->value_.is(); + } + + const game::VariableValue& get_raw() const + { + return this->value_.get_raw(); + } + + int argument_index_{}; + scripting::script_value value_; + }; +} diff --git a/src/client/game/scripting/stack_isolation.cpp b/src/client/game/scripting/stack_isolation.cpp new file mode 100644 index 00000000..646e2584 --- /dev/null +++ b/src/client/game/scripting/stack_isolation.cpp @@ -0,0 +1,27 @@ +#include +#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_; + } +} diff --git a/src/client/game/scripting/stack_isolation.hpp b/src/client/game/scripting/stack_isolation.hpp new file mode 100644 index 00000000..8dffd4bc --- /dev/null +++ b/src/client/game/scripting/stack_isolation.hpp @@ -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_; + }; +} diff --git a/src/client/game/scripting/variable_value.cpp b/src/client/game/scripting/variable_value.cpp new file mode 100644 index 00000000..74f5f510 --- /dev/null +++ b/src/client/game/scripting/variable_value.cpp @@ -0,0 +1,68 @@ +#include +#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; + } + } +} diff --git a/src/client/game/scripting/variable_value.hpp b/src/client/game/scripting/variable_value.hpp new file mode 100644 index 00000000..af533ee5 --- /dev/null +++ b/src/client/game/scripting/variable_value.hpp @@ -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}; + }; +} diff --git a/src/client/game/scripting/vector.cpp b/src/client/game/scripting/vector.cpp new file mode 100644 index 00000000..e3b66dcd --- /dev/null +++ b/src/client/game/scripting/vector.cpp @@ -0,0 +1,85 @@ +#include +#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; + } +} diff --git a/src/client/game/scripting/vector.hpp b/src/client/game/scripting/vector.hpp new file mode 100644 index 00000000..70b146d6 --- /dev/null +++ b/src/client/game/scripting/vector.hpp @@ -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}; + }; +} diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index f3e04e81..8b5858ba 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1,18 +1,9 @@ #pragma once -#include - -#define PROTOCOL 1 +#include "database.hpp" namespace game { - typedef float vec_t; - typedef vec_t vec2_t[2]; - typedef vec_t vec3_t[3]; - typedef vec_t vec4_t[4]; - - enum scr_string_t : std::int32_t - { - }; + using namespace database; enum GameModeType : std::uint32_t { @@ -28,12 +19,6 @@ namespace game FEATURE_TIMESCALE = 69, }; - enum errorParm - { - ERR_FATAL = 0, - ERR_DROP = 1, - }; - enum Sys_Folder { SF_ZONE = 0x0, @@ -45,6 +30,18 @@ namespace game SF_COUNT = 0x6, }; + enum errorParm + { + ERR_FATAL = 0, + ERR_DROP = 1, + ERR_SERVERDISCONNECT = 2, + ERR_DISCONNECT = 3, + ERR_SCRIPT = 4, + ERR_SCRIPT_DROP = 5, + ERR_LOCALIZATION = 6, + ERR_MAPLOADERRORSUMMARY = 7, + }; + struct CmdArgs { int nesting; @@ -432,8 +429,8 @@ namespace game THREAD_CONTEXT_SOUND_STREAM = 15, THREAD_CONTEXT_SOUND = 16, THREAD_CONTEXT_SOUND_DECODE = 17, - THREAD_CONTEXT_UNKNOWN18 = 18, - THREAD_CONTEXT_UNKNOWN19 = 19, + THREAD_CONTEXT_UNKNOWN18 = 18, // server watcher? + THREAD_CONTEXT_UNKNOWN19 = 19, // umbra THREAD_CONTEXT_RECIPE = 20, }; @@ -667,1782 +664,6 @@ namespace game } using namespace sv; - namespace assets - { - enum XAssetType : std::int32_t - { - ASSET_TYPE_PHYSICSLIBRARY = 0, - ASSET_TYPE_PHYSICS_SFX_EVENT_ASSET = 1, - ASSET_TYPE_PHYSICS_VFX_EVENT_ASSET = 2, - ASSET_TYPE_PHYSICSASSET = 3, - ASSET_TYPE_PHYSICS_FX_PIPELINE = 4, - ASSET_TYPE_PHYSICS_FX_SHAPE = 5, - ASSET_TYPE_XANIMPARTS = 6, - ASSET_TYPE_XMODEL_SURFS = 7, - ASSET_TYPE_XMODEL = 8, - ASSET_TYPE_MAYHEM = 9, - ASSET_TYPE_MATERIAL = 10, - ASSET_TYPE_COMPUTESHADER = 11, - ASSET_TYPE_VERTEXSHADER = 12, - ASSET_TYPE_HULLSHADER = 13, - ASSET_TYPE_DOMAINSHADER = 14, - ASSET_TYPE_PIXELSHADER = 15, - ASSET_TYPE_VERTEXDECL = 16, - ASSET_TYPE_TECHNIQUE_SET = 17, - ASSET_TYPE_IMAGE = 18, - ASSET_TYPE_SOUND_GLOBALS = 19, - ASSET_TYPE_SOUND_BANK = 20, - ASSET_TYPE_SOUND_BANK_TRANSIENT = 21, - ASSET_TYPE_CLIPMAP = 22, - ASSET_TYPE_COMWORLD = 23, - ASSET_TYPE_GLASSWORLD = 24, - ASSET_TYPE_PATHDATA = 25, - ASSET_TYPE_NAVMESH = 26, - ASSET_TYPE_MAP_ENTS = 27, - ASSET_TYPE_FXWORLD = 28, - ASSET_TYPE_GFXWORLD = 29, - ASSET_TYPE_GFXWORLD_TRANSIENT_ZONE = 30, - ASSET_TYPE_IESPROFILE = 31, // unused - ASSET_TYPE_LIGHT_DEF = 32, - ASSET_TYPE_UI_MAP = 33, // unused - ASSET_TYPE_ANIMCLASS = 34, - ASSET_TYPE_PLAYERANIM = 35, - ASSET_TYPE_GESTURE = 36, - ASSET_TYPE_LOCALIZE_ENTRY = 37, - ASSET_TYPE_ATTACHMENT = 38, - ASSET_TYPE_WEAPON = 39, - ASSET_TYPE_VFX = 40, - ASSET_TYPE_IMPACT_FX = 41, - ASSET_TYPE_SURFACE_FX = 42, - ASSET_TYPE_AITYPE = 43, // unused - ASSET_TYPE_MPTYPE = 44, // unused - ASSET_TYPE_CHARACTER = 45, // unused - ASSET_TYPE_XMODELALIAS = 46, // unused - ASSET_TYPE_UNKNOWN = 47, // unused - ASSET_TYPE_RAWFILE = 48, - ASSET_TYPE_SCRIPTFILE = 49, - ASSET_TYPE_STRINGTABLE = 50, - ASSET_TYPE_LEADERBOARD = 51, - ASSET_TYPE_VIRTUAL_LEADERBOARD = 52, - ASSET_TYPE_STRUCTUREDDATADEF = 53, - ASSET_TYPE_DDL = 54, - ASSET_TYPE_TRACER = 55, - ASSET_TYPE_VEHICLE = 56, - ASSET_TYPE_ADDON_MAP_ENTS = 57, - ASSET_TYPE_NET_CONST_STRINGS = 58, - ASSET_TYPE_LUA_FILE = 59, - ASSET_TYPE_SCRIPTABLE = 60, - ASSET_TYPE_EQUIPMENT_SND_TABLE = 61, - ASSET_TYPE_VECTORFIELD = 62, - ASSET_TYPE_PARTICLE_SIM_ANIMATION = 63, - ASSET_TYPE_STREAMING_INFO = 64, - ASSET_TYPE_LASER = 65, - ASSET_TYPE_TTF = 66, - ASSET_TYPE_SUIT = 67, - ASSET_TYPE_SUITANIMPACKAGE = 68, - ASSET_TYPE_SPACESHIPTARGET = 69, - ASSET_TYPE_RUMBLE = 70, - ASSET_TYPE_RUMBLE_GRAPH = 71, - ASSET_TYPE_ANIM_PACKAGE = 72, - ASSET_TYPE_SFX_PACKAGE = 73, - ASSET_TYPE_VFX_PACKAGE = 74, - ASSET_TYPE_BEHAVIOR_TREE = 75, - ASSET_TYPE_XANIM_ARCHETYPE = 76, - ASSET_TYPE_XANIM_PROCEDURALBONES = 77, - ASSET_TYPE_RETICLE = 78, - ASSET_TYPE_GFXLIGHTMAP = 79, - ASSET_TYPE_COUNT = 80 - }; - - struct PhysicsLibrary; - struct PhysicsSFXEventAsset; - struct PhysicsVFXEventAsset; - struct PhysicsAsset; - struct PhysicsFXPipeline; - struct PhysicsFXShape; - struct XAnimParts; - struct XModelSurfs; - struct XModel; - struct Mayhem; - struct Material; - struct ComputeShader; - struct MaterialVertexShader; - struct MaterialHullShader; - struct MaterialDomainShader; - struct MaterialPixelShader; - struct MaterialVertexDeclaration; - struct MaterialTechniqueSet; - struct GfxImage; - //ASSET_TYPE_SOUND_GLOBALS = 19, - //ASSET_TYPE_SOUND_BANK = 20, - //ASSET_TYPE_SOUND_BANK_TRANSIENT = 21, - //ASSET_TYPE_CLIPMAP = 22, - //ASSET_TYPE_COMWORLD = 23, - //ASSET_TYPE_GLASSWORLD = 24, - //ASSET_TYPE_PATHDATA = 25, - //ASSET_TYPE_NAVMESH = 26, - struct MapEnts; - //ASSET_TYPE_FXWORLD = 28, - //ASSET_TYPE_GFXWORLD = 29, - //ASSET_TYPE_GFXWORLD_TRANSIENT_ZONE = 30, - //ASSET_TYPE_IESPROFILE = 31, // unused - //ASSET_TYPE_LIGHT_DEF = 32, - //ASSET_TYPE_UI_MAP = 33, // unused - //ASSET_TYPE_ANIMCLASS = 34, - //ASSET_TYPE_PLAYERANIM = 35, - //ASSET_TYPE_GESTURE = 36, - //ASSET_TYPE_LOCALIZE_ENTRY = 37, - //ASSET_TYPE_ATTACHMENT = 38, - //ASSET_TYPE_WEAPON = 39, - //ASSET_TYPE_VFX = 40, - //ASSET_TYPE_IMPACT_FX = 41, - //ASSET_TYPE_SURFACE_FX = 42, - //ASSET_TYPE_AITYPE = 43, // unused - //ASSET_TYPE_MPTYPE = 44, // unused - //ASSET_TYPE_CHARACTER = 45, // unused - //ASSET_TYPE_XMODELALIAS = 46, // unused - //ASSET_TYPE_UNKNOWN = 47, // unused - struct RawFile; - //ASSET_TYPE_SCRIPTFILE = 49, - //ASSET_TYPE_STRINGTABLE = 50, - //ASSET_TYPE_LEADERBOARD = 51, - //ASSET_TYPE_VIRTUAL_LEADERBOARD = 52, - //ASSET_TYPE_STRUCTUREDDATADEF = 53, - //ASSET_TYPE_DDL = 54, - //ASSET_TYPE_TRACER = 55, - //ASSET_TYPE_VEHICLE = 56, - //ASSET_TYPE_ADDON_MAP_ENTS = 57, - //ASSET_TYPE_NET_CONST_STRINGS = 58, - //ASSET_TYPE_LUA_FILE = 59, - struct ScriptableDef; - //ASSET_TYPE_EQUIPMENT_SND_TABLE = 61, - //ASSET_TYPE_VECTORFIELD = 62, - //ASSET_TYPE_PARTICLE_SIM_ANIMATION = 63, - //ASSET_TYPE_STREAMING_INFO = 64, - //ASSET_TYPE_LASER = 65, - //ASSET_TYPE_TTF = 66, - //ASSET_TYPE_SUIT = 67, - //ASSET_TYPE_SUITANIMPACKAGE = 68, - //ASSET_TYPE_SPACESHIPTARGET = 69, - //ASSET_TYPE_RUMBLE = 70, - //ASSET_TYPE_RUMBLE_GRAPH = 71, - //ASSET_TYPE_ANIM_PACKAGE = 72, - //ASSET_TYPE_SFX_PACKAGE = 73, - //ASSET_TYPE_VFX_PACKAGE = 74, - //ASSET_TYPE_BEHAVIOR_TREE = 75, - //ASSET_TYPE_XANIM_ARCHETYPE = 76, - struct XAnimProceduralBones; - //ASSET_TYPE_RETICLE = 78, - //ASSET_TYPE_GFXLIGHTMAP = 79, - - struct Bounds - { - vec3_t midPoint; - vec3_t halfSize; - }; - - struct PhysicsLibrary - { - const char* name; - bool isMaterialList; - bool isBodyQualityList; - bool isMotionPropertiesList; - bool isGlobalTypeCompendium; - unsigned int havokDataSize; - char* havokData; - }; static_assert(sizeof(PhysicsLibrary) == 0x18); - - struct PhysicsSFXEventAssetRule; - struct PhysicsSFXEventAssetRules - { - int numrules; - PhysicsSFXEventAssetRule* rules; - }; - - enum PhysicsSFXEventAssetRuleType - { - Types_PhysicsSFXEventRule = 0x0, - Types_PhysicsSFXEventSoundRule = 0x1, - Types_PhysicsSFXEventMaterialRule = 0x2, - Types_PhysicsSFXEventAngleRule = 0x3, - Types_PhysicsSFXEventSpeedRule = 0x4, - Types_PhysicsSFXEventMassRule = 0x5, - Types_PhysicsSFXEventGravityRule = 0x6, - Types_PhysicsSFXEventAtmosphereRule = 0x7, - Types_PhysicsSFXEventCameraDistanceRule = 0x8, - Types_PhysicsSFXEventFrequencyRule = 0x9, - Types_PhysicsSFXEventRuleCount = 0xA, - }; - - struct PhysicsSFXEventAssetSoundRule - { - char* hitSoundAlias; - char* scrapeSoundAlias; - float scrapeSoundVolumeSpeedThresholdMin; - float scrapeSoundVolumeSpeedThresholdMax; - float scrapeSoundVolumeBlendSpeed; - }; - - struct PhysicsSFXEventAssetMaterialEntry - { - int surfaceType; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetMaterialRule - { - int numentries; - PhysicsSFXEventAssetMaterialEntry* entries; - }; - - struct PhysicsSFXEventAssetAngleEntry - { - float angle; - float hitSoundVolumeModifierMin; - float hitSoundVolumeModifierMax; - float hitSoundPitchModifierMin; - float hitSoundPitchModifierMax; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetAngleRule - { - int numentries; - PhysicsSFXEventAssetAngleEntry* entries; - }; - - struct PhysicsSFXEventAssetSpeedEntry - { - float speedThreshold; - float hitSoundVolumeModifierMin; - float hitSoundVolumeModifierMax; - float hitSoundPitchModifierMin; - float hitSoundPitchModifierMax; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetSpeedRule - { - int numentries; - PhysicsSFXEventAssetSpeedEntry* entries; - }; - - struct PhysicsSFXEventAssetMassEntry - { - float massThreshold; - float hitSoundVolumeModifierMin; - float hitSoundVolumeModifierMax; - float hitSoundPitchModifierMin; - float hitSoundPitchModifierMax; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetMassRule - { - int numentries; - PhysicsSFXEventAssetMassEntry* entries; - }; - - struct PhysicsSFXEventAssetGravityEntry - { - bool gravity; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetGravityRule - { - int numentries; - PhysicsSFXEventAssetGravityEntry* entries; - }; - - struct PhysicsSFXEventAssetAtmosphereEntry - { - bool atmosphere; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetAtmosphereRule - { - int numentries; - PhysicsSFXEventAssetAtmosphereEntry* entries; - }; - - struct PhysicsSFXEventAssetCameraDistanceEntry - { - float distance; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetCameraDistanceRule - { - int numentries; - PhysicsSFXEventAssetCameraDistanceEntry* entries; - }; - - struct PhysicsSFXEventAssetFrequencyEntry - { - int maxEvents; - PhysicsSFXEventAssetRules rules; - }; - - struct PhysicsSFXEventAssetFrequencyRule - { - int numentries; - PhysicsSFXEventAssetFrequencyEntry* entries; - }; - - union PhysicsSFXEventAssetRuleUnion - { - PhysicsSFXEventAssetSoundRule soundRule; - PhysicsSFXEventAssetMaterialRule materialRule; - PhysicsSFXEventAssetAngleRule angleRule; - PhysicsSFXEventAssetSpeedRule speedRule; - PhysicsSFXEventAssetMassRule massRule; - PhysicsSFXEventAssetGravityRule gravityRule; - PhysicsSFXEventAssetAtmosphereRule atmosphereRule; - PhysicsSFXEventAssetCameraDistanceRule cameraDistanceRule; - PhysicsSFXEventAssetFrequencyRule frequencyRule; - }; - - struct PhysicsSFXEventAssetRule - { - PhysicsSFXEventAssetRuleType type; - PhysicsSFXEventAssetRuleUnion u; - }; - - struct PhysicsSFXEventAsset - { - const char* name; - int priority; - PhysicsSFXEventAssetRules rules; - }; static_assert(sizeof(PhysicsSFXEventAsset) == 0x20); - - struct PhysicsVFXEventAssetRule; - struct PhysicsVFXEventAssetRules - { - int numrules; - PhysicsVFXEventAssetRule* rules; - }; - - enum PhysicsVFXEventAssetRuleType - { - Types_PhysicsVFXEventRule = 0x0, - Types_PhysicsVFXEventParticleEffectRule = 0x1, - Types_PhysicsVFXEventMaterialRule = 0x2, - Types_PhysicsVFXEventAngleRule = 0x3, - Types_PhysicsVFXEventSpeedRule = 0x4, - Types_PhysicsVFXEventMassRule = 0x5, - Types_PhysicsVFXEventGravityRule = 0x6, - Types_PhysicsVFXEventAtmosphereRule = 0x7, - Types_PhysicsVFXEventCameraDistanceRule = 0x8, - Types_PhysicsVFXEventFrequencyRule = 0x9, - Types_PhysicsVFXEventRuleCount = 0xA, - }; - - struct PhysicsVFXEventAssetParticleEffectRule - { - char* hitParticleEffectAlias; - char* scrapeParticleEffectAlias; - }; - - struct PhysicsVFXEventAssetMaterialEntry - { - int surfaceType; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetMaterialRule - { - int numentries; - PhysicsVFXEventAssetMaterialEntry* entries; - }; - - struct PhysicsVFXEventAssetAngleEntry - { - float angle; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetAngleRule - { - int numentries; - PhysicsVFXEventAssetAngleEntry* entries; - }; - - struct PhysicsVFXEventAssetSpeedEntry - { - float speedThreshold; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetSpeedRule - { - int numentries; - PhysicsVFXEventAssetSpeedEntry* entries; - }; - - struct PhysicsVFXEventAssetMassEntry - { - float massThreshold; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetMassRule - { - int numentries; - PhysicsVFXEventAssetMassEntry* entries; - }; - - struct PhysicsVFXEventAssetGravityEntry - { - bool gravity; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetGravityRule - { - int numentries; - PhysicsVFXEventAssetGravityEntry* entries; - }; - - struct PhysicsVFXEventAssetAtmosphereEntry - { - bool atmosphere; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetAtmosphereRule - { - int numentries; - PhysicsVFXEventAssetAtmosphereEntry* entries; - }; - - struct PhysicsVFXEventAssetCameraDistanceEntry - { - float distance; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetCameraDistanceRule - { - int numentries; - PhysicsVFXEventAssetCameraDistanceEntry* entries; - }; - - struct PhysicsVFXEventAssetFrequencyEntry - { - int maxEvents; - PhysicsVFXEventAssetRules rules; - }; - - struct PhysicsVFXEventAssetFrequencyRule - { - int numentries; - PhysicsVFXEventAssetFrequencyEntry* entries; - }; - - union PhysicsVFXEventAssetRuleUnion - { - PhysicsVFXEventAssetParticleEffectRule particleEffectRule; - PhysicsVFXEventAssetMaterialRule materialRule; - PhysicsVFXEventAssetAngleRule angleRule; - PhysicsVFXEventAssetSpeedRule speedRule; - PhysicsVFXEventAssetMassRule massRule; - PhysicsVFXEventAssetGravityRule gravityRule; - PhysicsVFXEventAssetAtmosphereRule atmosphereRule; - PhysicsVFXEventAssetCameraDistanceRule cameraDistanceRule; - PhysicsVFXEventAssetFrequencyRule frequencyRule; - }; - - struct PhysicsVFXEventAssetRule - { - PhysicsVFXEventAssetRuleType type; - PhysicsVFXEventAssetRuleUnion u; - }; - - struct PhysicsVFXEventAsset - { - const char* name; - int priority; - PhysicsVFXEventAssetRules rules; - }; static_assert(sizeof(PhysicsVFXEventAsset) == 0x20); - - struct PhysicsAsset - { - const char* name; - unsigned int havokDataSize; - char* havokData; - char __pad0[8]; - int numConstraints; - int numSFXEventAssets; - void** sfxEventAssets; - int numVFXEventAssets; - void** vfxEventAssets; - char __pad1[16]; - }; static_assert(sizeof(PhysicsAsset) == 0x50); - - struct PhysicsFXPipeline - { - const char* name; - char __pad0[24]; - }; static_assert(sizeof(PhysicsFXPipeline) == 0x20); - - struct PhysicsFXShape - { - const char* name; - unsigned int size; - vec3_t* vecs; - }; static_assert(sizeof(PhysicsFXShape) == 0x18); - - union XAnimIndices - { - char* _1; - unsigned short* _2; - void* data; - }; - - struct XAnimNotifyInfo - { - scr_string_t name; - float time; - }; - - union XAnimDynamicFrames - { - char(*_1)[3]; - unsigned short(*_2)[3]; - }; - - union XAnimDynamicIndices - { - char _1[1]; - unsigned short _2[1]; - }; - - struct XAnimPartTransFrames - { - vec3_t mins; - vec3_t size; - XAnimDynamicFrames frames; - XAnimDynamicIndices indices; - }; - - union XAnimPartTransData - { - XAnimPartTransFrames frames; - vec3_t frame0; - }; - - struct XAnimPartTrans - { - unsigned short size; - char smallTrans; - XAnimPartTransData u; - }; - - struct XAnimDeltaPartQuatDataFrames2 - { - short(*frames)[2]; - XAnimDynamicIndices indices; - }; - - union XAnimDeltaPartQuatData2 - { - XAnimDeltaPartQuatDataFrames2 frames; - short frame0[2]; - }; - - struct XAnimDeltaPartQuat2 - { - unsigned short size; - XAnimDeltaPartQuatData2 u; - }; - - struct XAnimDeltaPartQuatDataFrames - { - short(*frames)[4]; - XAnimDynamicIndices indices; - }; - - union XAnimDeltaPartQuatData - { - XAnimDeltaPartQuatDataFrames frames; - short frame0[4]; - }; - - struct XAnimDeltaPartQuat - { - unsigned short size; - XAnimDeltaPartQuatData u; - }; - - struct XAnimDeltaPart - { - XAnimPartTrans* trans; - XAnimDeltaPartQuat2* quat2; - XAnimDeltaPartQuat* quat; - }; - - struct XAnimParts - { - const char* name; // 0 - scr_string_t* names; // 8 - char* dataByte; // 16 - short* dataShort; // 24 - int* dataInt; // 32 - short* randomDataShort; // 40 - char* randomDataByte; // 48 - int* randomDataInt; // 56 - XAnimIndices indices; // 64 - XAnimNotifyInfo* notify; // 72 - XAnimDeltaPart* deltaPart; // 80 - unsigned int randomDataShortCount; // 88 - unsigned int randomDataByteCount; // 92 - unsigned int indexCount; // 96 - float framerate; // 100 - float frequency; // 104 - unsigned short dataByteCount; // 108 - unsigned short dataShortCount; // 110 - unsigned short dataIntCount; // 112 - unsigned short randomDataIntCount; // 114 - unsigned short numframes; // 116 - char flags; // 118 - char boneCount[10]; // 119 - char notifyCount; // 129 - char assetType; // 130 - char ikType; // 131 - }; static_assert(sizeof(XAnimParts) == 0x88); - - struct XSurfaceCollisionAabb - { - unsigned short mins[3]; - unsigned short maxs[3]; - }; - - struct XSurfaceCollisionNode - { - XSurfaceCollisionAabb aabb; - unsigned short childBeginIndex; - unsigned short childCount; - }; - - struct XSurfaceCollisionLeaf - { - unsigned short triangleBeginIndex; - }; - - struct XSurfaceCollisionTree - { - float trans[3]; - float scale[3]; - unsigned int nodeCount; - XSurfaceCollisionNode* nodes; - unsigned int leafCount; - XSurfaceCollisionLeaf* leafs; - }; - - struct XRigidVertList - { - unsigned short boneOffset; - unsigned short vertCount; - unsigned short triOffset; - unsigned short triCount; - XSurfaceCollisionTree* collisionTree; - }; - - union PackedUnitVec - { - unsigned int packed; - }; - - union PackedTexCoords - { - unsigned int packed; - }; - - union GfxColor - { - unsigned char array[4]; - unsigned int packed; - }; - - struct GfxPackedVertex - { - float xyz[3]; - float binormalSign; - GfxColor color; - PackedTexCoords texCoord; - PackedUnitVec normal; - PackedUnitVec tangent; - }; - - struct GfxPackedMotionVertex - { - float xyz[3]; - float binormalSignAndHeight; - GfxColor pieceIndex; - PackedTexCoords texCoord; - PackedUnitVec normal; - PackedUnitVec tangent; - }; - - union GfxVertexUnion0 - { - GfxPackedVertex* packedVerts0; - GfxPackedMotionVertex* packedMotionVerts0; - void* verts0; - }; - - struct Face - { - unsigned short v1; - unsigned short v2; - unsigned short v3; - }; - - typedef unsigned int XBlendInfo; - - struct XSubdivRigidVertList - { - unsigned int firstFace; - unsigned int faceCount; - unsigned int firstRegularPatch; - unsigned int regularPatchCount; - }; - - struct XSurfaceSubdivLevel - { - XSubdivRigidVertList* rigidVertLists; - unsigned int faceCount; - unsigned int regularPatchCount; - unsigned int regularPatchOffset; - unsigned int facePointCount; - unsigned int facePointValence4Count; - unsigned int facePointBufferSize; - unsigned int edgePointCount; - unsigned int edgePointSmoothEnd; - unsigned int edgePointUVBorderEnd; - unsigned int vertexPointCount; - unsigned int vertexPointValence4Count; - unsigned int vertexPointBufferSize; - unsigned int normalCount; - unsigned int normalBufferSize; - unsigned int transitionPointCount; - unsigned int vertCount; - unsigned int vertOffset; - unsigned short* faceIndices; - unsigned short* regularPatchIndices; - unsigned int* regularPatchFlags; - unsigned int* facePoints; - unsigned int* edgePoints; - unsigned int* vertexPoints; - unsigned int* normals; - unsigned int* transitionPoints; - float* regularPatchCones; - ID3D11Buffer* regularPatchIndexBuffer; - ID3D11Buffer* faceIndexBuffer; - ID3D11ShaderResourceView* regularPatchIndexBufferView; - ID3D11ShaderResourceView* regularPatchFlagsView; - ID3D11ShaderResourceView* facePointsView; - ID3D11ShaderResourceView* edgePointsView; - ID3D11ShaderResourceView* vertexPointsView; - ID3D11ShaderResourceView* normalsView; - ID3D11ShaderResourceView* transitionPointsView; - ID3D11ShaderResourceView* regularPatchConesView; - }; static_assert(sizeof(XSurfaceSubdivLevel) == 0xE8); - - struct GfxSubdivCache - { - unsigned int size; - ID3D11Buffer* subdivCacheBuffer; - ID3D11ShaderResourceView* subdivCacheView; - }; static_assert(sizeof(GfxSubdivCache) == 0x18); - - struct XSurfaceSubdivInfo - { - XSurfaceSubdivLevel* levels; - char __pad0[24]; - GfxSubdivCache cache; - }; static_assert(sizeof(XSurfaceSubdivInfo) == 0x38); - - struct TensionData - { - char __pad0[32]; - }; - - struct SHProbeSimplexData0 - { - char __pad0[32]; - }; - - struct SHProbeSimplexData1 - { - char __pad0[8]; - }; - - struct SHProbeSimplexData2 - { - char __pad0[24]; - }; - - union SHProbeSimplexDataUnion - { - SHProbeSimplexData0 data0; - SHProbeSimplexData1 data1; - SHProbeSimplexData2 data2; - }; - - struct XSurface - { - unsigned short flags; // 0 - unsigned short vertCount; // 2 - unsigned short triCount; // 4 - char rigidVertListCount; // 6 - char subdivLevelCount; // 7 - short blendVertCounts[8]; // 8 - int unkBlendCount; // 24 - char __pad0[4]; // 28 - GfxVertexUnion0 verts0; // 32 - Face* triIndices; // 40 - ID3D11Buffer* vb0; // 48 - ID3D11ShaderResourceView* vb0View; // 56 - ID3D11Buffer* indexBuffer; // 64 - XRigidVertList* rigidVertLists; // 72 - char* unkBlend; // 80 - ID3D11Buffer* blendVertsBuffer; // 88 - ID3D11ShaderResourceView* blendVertsView; // 96 - __int64* lmapUnwrap; // 104 - ID3D11Buffer* vblmapBuffer; // 112 - ID3D11ShaderResourceView* vblmapView; // 120 - XSurfaceSubdivInfo* subdiv; // 128 - XBlendInfo* blendVerts; // 136 - TensionData* tensionData; // 144 - void* tensionAccumTable; // 152 - ID3D11Buffer* tensionAccumTableBuffer; // 160 - ID3D11ShaderResourceView* tensionAccumTableView; // 168 - ID3D11Buffer* tensionDataBuffer; // 176 - ID3D11ShaderResourceView* tensionDataView; // 184 - ID3D11ShaderResourceView* indexBufferView; // 192 - SHProbeSimplexDataUnion* shProbeSimplexVertData; // 200 - ID3D11Buffer* shProbeSimplexVertBuffer; // 208 - ID3D11ShaderResourceView* shProbeSimplexVertBufferView; // 216 - int partBits[8]; // 224 - }; static_assert(sizeof(XSurface) == 0x100); - - struct XModelSurfs - { - const char* name; - XSurface* surfs; - unsigned short numsurfs; - int partBits[8]; - }; static_assert(sizeof(XModelSurfs) == 0x38); - - struct XModelAngle - { - short x; - short y; - short z; - short base; - }; - - struct XModelTagPos - { - float x; - float y; - float z; - }; - - struct DObjAnimMat - { - float quat[4]; - float trans[3]; - float transWeight; - }; - - struct ReactiveMotionModelPart - { - float center[3]; - float stiffness; - }; - - struct ReactiveMotionModelTweaks - { - float scale[4]; - }; - - struct XModelLodInfo - { - float dist; - unsigned short numsurfs; - unsigned short surfIndex; - XModelSurfs* modelSurfs; - int partBits[8]; - XSurface* surfs; - int unk; // subdivLodValidMask? - char flags; - char pad[3]; - }; static_assert(sizeof(XModelLodInfo) == 0x40); - - struct XModelCollSurf_s - { - Bounds bounds; - int boneIdx; - int contents; - int surfFlags; - }; static_assert(sizeof(XModelCollSurf_s) == 36); - - struct XBoneInfo - { - Bounds bounds; - union - { - float radiusSquared; - unsigned int radiusSquaredAsInt; - }; - }; - - struct XModel - { - const char* name; // 0 - char __pad0[12]; - unsigned char numBones; // 20 - unsigned char numRootBones; // 21 - unsigned char numReactiveMotionParts; // 22 - char b; - unsigned short numClientBones; // 24 - unsigned char numsurfs; // 26 - char __pad1[5]; - unsigned short numCollSurfs; // 32 - char __pad2[38]; - unsigned char unk1Count; // 72 - char __pad3[15]; - ScriptableDef* scriptableMoverDef; // 88 - XAnimProceduralBones* proceduralBones; // 96 - scr_string_t* aimAssistBones; // 104 - unsigned char numAimAssistBones; // 112 - char __pad4[39]; - scr_string_t* boneNames; // 152 - unsigned char* parentList; // 160 - XModelAngle* tagAngles; // 168 - XModelTagPos* tagPositions; // 176 - unsigned char* partClassification; // 184 - DObjAnimMat* baseMat; // 192 - ReactiveMotionModelPart* reactiveMotionParts; // 200 - ReactiveMotionModelTweaks* reactiveMotionTweaks; // 208 - Material** materialHandles; // 216 - XModelLodInfo lodInfo[6]; // 224 - XModelCollSurf_s* collSurfs; // 608 - XBoneInfo* boneInfo; // 616 - unsigned short* invHighMipRadius; // 624 - PhysicsAsset* physAsset; // 632 - PhysicsFXShape* physFxShape; // 640 - char* unk1; // 648 - unsigned short unkNamesCount; // 656 - char __pad5[6]; - scr_string_t* unkNames; // 664 - char __pad6[16]; - int unk2Count; - char __pad8[4]; - char* unk2; - char __pad9[1]; - unsigned char unkVec3Count; - char __pad10[6]; - vec3_t* unkVec3; - char __pad11[1]; - char unk3Count; - char __pad12[6]; - void* unk3; - }; static_assert(sizeof(XModel) == 0x2E0); // 736 - - struct MayhemModel - { - XModel* model; - }; - - struct MayhemTransBounds - { - vec3_t mins; - vec3_t size; - }; - - struct GfxWrappedBaseBuffer - { - ID3D11Resource* buffer; - //ID3D11ShaderResourceView* view; - }; - - struct MayhemAnimFramesSplineCompressed - { - char* diskQuat; - char* diskPos; - unsigned __int16* diskQuatFrames; - unsigned __int16* diskPosFrames; - unsigned __int16* numDiskQuatFrames; - unsigned __int16* numDiskPosFrames; - MayhemTransBounds* transBounds; - GfxWrappedBaseBuffer quat; - GfxWrappedBaseBuffer pos; - unsigned int quatStride; - unsigned int posStride; - unsigned int totalCompressedQuatFrames; - unsigned int totalCompressedPosFrames; - }; static_assert(sizeof(MayhemAnimFramesSplineCompressed) == 0x58); - - struct MayhemAnimFramesUncompressed - { - char* diskQuat; - char* diskPos; - MayhemTransBounds* transBounds; - GfxWrappedBaseBuffer quat; - GfxWrappedBaseBuffer pos; - unsigned int quatStride; - unsigned int posStride; - }; static_assert(sizeof(MayhemAnimFramesUncompressed) == 0x30); - - union MayhemFramesUnion - { - void* data; - MayhemAnimFramesSplineCompressed* splineCompressedFrames; - MayhemAnimFramesUncompressed* uncompressedFrames; - }; - - struct MayhemDataKeysSplineCompressed - { - char* keys; - unsigned __int16* numKeys; - unsigned __int16* keyFrames; - unsigned int totalCompressedKeyFrames; - unsigned int numStreams; - unsigned int keyStride; - }; static_assert(sizeof(MayhemDataKeysSplineCompressed) == 0x28); - - struct MayhemDataKeysUncompressed - { - char* keys; - unsigned int numStreams; - unsigned int keyStride; - }; static_assert(sizeof(MayhemDataKeysUncompressed) == 0x10); - - union MayhemDataChannelsUnion - { - void* data; - MayhemDataKeysSplineCompressed* splineCompressedKeys; - MayhemDataKeysUncompressed* uncompressedKeys; - }; - - struct MayhemAnim - { - MayhemFramesUnion frames; - unsigned int numBones; - unsigned int numFrames; - float framerate; - float frequency; - unsigned int notifyCount; - unsigned int dataChannelCount; - bool isLooping; - bool isSplineCompressed; - bool quantizeQuats; - bool quantizeTrans; - bool quantizeChannels; - XAnimNotifyInfo* notify; - MayhemDataChannelsUnion dataChannels; - }; static_assert(sizeof(MayhemAnim) == 56); - - struct MayhemObject - { - unsigned int modelIndex; - unsigned int animIndex; - }; - - struct MayhemData - { - const char* name; - XModel* preModel; - XModel* postModel; - unsigned int numModels; - MayhemModel* models; - unsigned int numAnims; - MayhemAnim* anims; - unsigned int numObjects; - MayhemObject* objects; - float length; - }; static_assert(sizeof(MayhemData) == 0x50); - - enum MaterialTechniqueType - { - TECHNIQUE_UNLIT = 8, - TECHNIQUE_EMISSIVE = 10, - TECHNIQUE_LIT = 13, - }; - - struct Packed128 - { - std::uint64_t p0; - std::uint64_t p1; - }; - - struct GfxDrawSurfFields - { - - }; - - union GfxDrawSurf - { - GfxDrawSurfFields fields; - Packed128 packed; - }; - - enum SurfaceTypeBits : std::uint64_t - { - - }; - - struct MaterialInfo - { - const char* name; - unsigned char gameFlags; - unsigned char sortKey; - unsigned char textureAtlasRowCount; - unsigned char textureAtlasColumnCount; - unsigned char textureAtlasFrameBlend; - unsigned char textureAtlasAsArray; - unsigned char renderFlags; - GfxDrawSurf drawSurf; - SurfaceTypeBits surfaceTypeBits; - unsigned int hashIndex; - }; static_assert(sizeof(MaterialInfo) == 48); - - struct MaterialTextureDef - { - unsigned int nameHash; - char nameStart; - char nameEnd; - unsigned char samplerState; - unsigned char semantic; - GfxImage* image; - }; static_assert(sizeof(MaterialTextureDef) == 16); - - struct MaterialConstantDef - { - unsigned int nameHash; - char name[12]; - float literal[4]; - }; static_assert(sizeof(MaterialConstantDef) == 32); - - struct GfxStateBits - { - unsigned int loadBits[6]; // loadbits[3], blendstatebits[3] - unsigned short zone; - unsigned char depthStencilState[14]; - unsigned char blendState; - unsigned char rasterizerState; - }; static_assert(sizeof(GfxStateBits) == 44); - static_assert(offsetof(GfxStateBits, zone) == 24); - - struct MaterialConstantBufferDef - { - unsigned int vsDataSize; - unsigned int hsDataSize; - unsigned int dsDataSize; - unsigned int psDataSize; - unsigned int vsOffsetDataSize; - unsigned int hsOffsetDataSize; - unsigned int dsOffsetDataSize; - unsigned int psOffsetDataSize; - unsigned char* vsData; - unsigned char* hsData; - unsigned char* dsData; - unsigned char* psData; - unsigned short* vsOffsetData; - unsigned short* hsOffsetData; - unsigned short* dsOffsetData; - unsigned short* psOffsetData; - ID3D11Buffer* vsConstantBuffer; - ID3D11Buffer* hsConstantBuffer; - ID3D11Buffer* dsConstantBuffer; - ID3D11Buffer* psConstantBuffer; - }; - - struct Material - { - union - { - const char* name; - MaterialInfo info; - }; - unsigned char textureCount; - unsigned char constantCount; - unsigned char stateFlags; - unsigned char cameraRegion; - unsigned char materialType; - unsigned char layerCount; - unsigned char assetFlags; - char pad; - MaterialTechniqueSet* techniqueSet; - MaterialTextureDef* textureTable; - MaterialConstantDef* constantTable; - unsigned char stateBitsEntry[152]; - unsigned char stateBitsCount; - GfxStateBits* stateBitsTable; - unsigned char* constantBufferIndex; // [152] - MaterialConstantBufferDef* constantBufferTable; - unsigned char constantBufferCount; - const char** subMaterials; - }; static_assert(sizeof(Material) == 0x118); - static_assert(offsetof(Material, techniqueSet) == 56); - static_assert(offsetof(Material, textureCount) == 48); - static_assert(offsetof(Material, constantCount) == 49); - static_assert(offsetof(Material, stateBitsTable) == 240); - static_assert(offsetof(Material, constantBufferIndex) == 248); - static_assert(offsetof(Material, stateBitsCount) == 232); - static_assert(offsetof(Material, constantBufferTable) == 256); - static_assert(offsetof(Material, constantBufferCount) == 264); - static_assert(offsetof(Material, subMaterials) == 272); - static_assert(offsetof(Material, layerCount) == 53); - - struct GfxComputeShaderLoadDef - { - unsigned char* program; - unsigned int programSize; - }; - - struct ComputeShaderProgram - { - ID3D11ComputeShader* cs; - GfxComputeShaderLoadDef loadDef; - }; - - struct ComputeShader - { - const char* name; - const char* debugName; - ComputeShaderProgram prog; - }; static_assert(sizeof(ComputeShader) == 0x28); - - struct GfxPixelShaderLoadDef - { - unsigned char* program; - unsigned int programSize; - unsigned int microCodeCrc; - }; - - struct MaterialPixelShaderProgram - { - ID3D11PixelShader* ps; - GfxPixelShaderLoadDef loadDef; - }; - - struct MaterialPixelShader - { - const char* name; - const char* debugName; - MaterialPixelShaderProgram prog; - }; static_assert(sizeof(MaterialPixelShader) == 0x28); - - struct GfxVertexShaderLoadDef - { - unsigned char* program; - unsigned int programSize; - unsigned int microCodeCrc; - }; - - struct MaterialVertexShaderProgram - { - ID3D11VertexShader* vs; - GfxVertexShaderLoadDef loadDef; - }; - - struct MaterialVertexShader - { - const char* name; - const char* debugName; - MaterialVertexShaderProgram prog; - }; static_assert(sizeof(MaterialVertexShader) == 0x28); - - struct GfxHullShaderLoadDef - { - unsigned char* program; - unsigned int programSize; - }; - - struct MaterialHullShaderProgram - { - ID3D11HullShader* hs; - GfxHullShaderLoadDef loadDef; - }; - - struct MaterialHullShader - { - const char* name; - const char* debugName; - MaterialHullShaderProgram prog; - }; static_assert(sizeof(MaterialHullShader) == 0x28); - - struct GfxDomainShaderLoadDef - { - unsigned char* program; - unsigned int programSize; - }; - - struct MaterialDomainShaderProgram - { - ID3D11DomainShader* ds; - GfxDomainShaderLoadDef loadDef; - }; - - struct MaterialDomainShader - { - const char* name; - const char* debugName; - MaterialDomainShaderProgram prog; - }; static_assert(sizeof(MaterialDomainShader) == 0x28); - - struct MaterialStreamRouting - { - unsigned char source; - unsigned char dest; - unsigned char mask; - }; - - struct MaterialVertexStreamRouting - { - MaterialStreamRouting data[34]; - ID3D11InputLayout* decl[270]; - }; - - struct MaterialVertexDeclaration - { - const char* name; - unsigned char streamCount; - bool hasOptionalSource; - MaterialVertexStreamRouting routing; - }; static_assert(sizeof(MaterialVertexDeclaration) == 0x8E8); - - struct MaterialArgumentCodeConst - { - unsigned short index; - unsigned char firstRow; - unsigned char rowCount; - }; - - union MaterialArgumentDef - { - const float* literalConst; - MaterialArgumentCodeConst codeConst; - unsigned int codeSampler; - unsigned int nameHash; - }; - - enum MaterialShaderArgumentType : std::uint8_t - { - - }; - - struct MaterialShaderArgument - { - unsigned char type; - unsigned char shader; - unsigned short dest; - MaterialArgumentDef u; - }; static_assert(sizeof(MaterialShaderArgument) == 0x10); - - struct MaterialPass - { - MaterialVertexShader* vertexShader; - MaterialVertexDeclaration* vertexDecl; - MaterialHullShader* hullShader; - MaterialDomainShader* domainShader; - MaterialPixelShader* pixelShader; - unsigned char pixelOutputMask; - unsigned char perPrimArgCount; - unsigned char perObjArgCount; - unsigned char stableArgCount; - unsigned short perPrimArgSize; - unsigned short perObjArgSize; - unsigned short stableArgSize; - unsigned short zone; - char __pad0[44]; - MaterialShaderArgument* args; - }; static_assert(sizeof(MaterialPass) == 104); - static_assert(offsetof(MaterialPass, zone) == 50); - static_assert(offsetof(MaterialPass, args) == 96); - - struct MaterialTechniqueHeader - { - const char* name; - unsigned short flags; - unsigned short passCount; - }; static_assert(sizeof(MaterialTechniqueHeader) == 0x10); - - struct MaterialTechnique - { - MaterialTechniqueHeader hdr; - MaterialPass passArray[1]; - }; - - struct MaterialTechniqueSet - { - const char* name; - unsigned short flags; - unsigned char worldVertFormat; - unsigned char preDisplacementOnlyCount; - unsigned int techniqueCount; - char __pad0[32]; - MaterialTechnique* techniques; - }; static_assert(sizeof(MaterialTechniqueSet) == 0x38); - static_assert(offsetof(MaterialTechniqueSet, techniqueCount) == 12); - static_assert(offsetof(MaterialTechniqueSet, techniques) == 48); - - enum MapType : std::uint8_t - { - MAPTYPE_NONE = 0x0, - MAPTYPE_INVALID1 = 0x1, - MAPTYPE_1D = 0x2, - MAPTYPE_2D = 0x3, - MAPTYPE_3D = 0x4, - MAPTYPE_CUBE = 0x5, - MAPTYPE_ARRAY = 0x6, - MAPTYPE_CUBE_ARRAY = 0x7, - MAPTYPE_COUNT = 0x8, - }; - - struct GfxImageStreamData - { - unsigned short width; - unsigned short height; - unsigned int pixelSize; - }; - - struct GfxImage - { - char __pad0[24]; - DXGI_FORMAT imageFormat; // 24 - char flags; // 28 - char __pad1[3]; - MapType mapType; // 32 - char sematic; // 33 - char category; // 34 - unsigned char platform[2]; // 35 - char __pad2[3]; - unsigned int dataLen1; // 40 - unsigned int dataLen2; // 44 - unsigned short width; // 48 - unsigned short height; // 50 - unsigned short depth; // 52 - unsigned short numElements; // 54 - unsigned char mipmapCount; // 56 - bool streamed; // 57 - char __pad3[6]; - char* pixelData; // 64 - GfxImageStreamData streams[4]; // 72 - const char* name; // 104 - }; static_assert(sizeof(GfxImage) == 0x70); - - struct snd_volmod_info_t - { - char name[64]; - unsigned int id; - float value; - }; - - struct SndGlobals - { - const char* name; - float globalVolumeModifier; - float binkVolumeModifier; - unsigned int volmodinfoCount; - snd_volmod_info_t* volmodinfo; - char __pad0[176]; - }; static_assert(sizeof(SndGlobals) == 0xD0); - - struct SndAlias - { - const char* aliasName; - char __pad0[192]; - }; static_assert(sizeof(SndAlias) == 200); - - struct SndAliasList - { - const char* aliasName; - unsigned int id; - SndAlias* head; - int count; - int sequence; - }; static_assert(sizeof(SndAliasList) == 32); - static_assert(offsetof(SndAliasList, head) == 16); - static_assert(offsetof(SndAliasList, count) == 24); - - struct SndIndexEntry - { - unsigned short value; - unsigned short next; - }; static_assert(sizeof(SndIndexEntry) == 4); - - struct SndSendEffectParams - { - char __pad0[136]; - }; static_assert(sizeof(SndSendEffectParams) == 136); - - struct SoundTable - { - char __pad0[248]; - }; static_assert(sizeof(SoundTable) == 248); - - struct SndDuck - { - char __pad0[144]; - }; static_assert(sizeof(SndDuck) == 144); - - struct SndMusicState - { - char __pad0[384]; - }; static_assert(sizeof(SndMusicState) == 384); - - struct SndMusicSet - { - char name[64]; - unsigned int id; - unsigned int stateCount; - SndMusicState* states; - }; static_assert(sizeof(SndMusicSet) == 80); - static_assert(offsetof(SndMusicSet, stateCount) == 68); - static_assert(offsetof(SndMusicSet, states) == 72); - - struct SndBank - { - const char* name; - const char* zone; - const char* gameLanguage; - const char* soundLanguage; - unsigned int aliasCount; - SndAliasList* alias; - SndIndexEntry* aliasIndex; - unsigned int sendEffectCount; - SndSendEffectParams* sendEffects; - SoundTable soundTable; - unsigned int duckCount; - SndDuck* ducks; - unsigned int musicSetCount; - SndMusicSet* musicSets; - }; - static_assert(offsetof(SndBank, name) == 0); - static_assert(offsetof(SndBank, zone) == 8); - static_assert(offsetof(SndBank, gameLanguage) == 16); - static_assert(offsetof(SndBank, soundLanguage) == 24); - static_assert(offsetof(SndBank, aliasCount) == 32); - static_assert(offsetof(SndBank, aliasIndex) == 48); - static_assert(offsetof(SndBank, sendEffectCount) == 56); - static_assert(offsetof(SndBank, sendEffects) == 64); - static_assert(offsetof(SndBank, soundTable) == 72); - static_assert(offsetof(SndBank, duckCount) == 320); - static_assert(offsetof(SndBank, ducks) == 328); - static_assert(offsetof(SndBank, musicSetCount) == 336); - static_assert(offsetof(SndBank, musicSets) == 344); - - struct SndBankResident - { - SndBank bank; - }; static_assert(sizeof(SndBankResident) == 0x160); - - struct SndBankTransient - { - const char* name; - unsigned int elementCount; - unsigned int* elements; - }; static_assert(sizeof(SndBankTransient) == 0x18); - - struct LocalizeEntry - { - const char* name; - const char* value; - }; static_assert(sizeof(LocalizeEntry) == 0x10); - - struct RawFile - { - const char* name; - int compressedLen; - int len; - const char* buffer; - }; static_assert(sizeof(RawFile) == 0x18); - - struct ScriptFile - { - const char* name; - int compressedLen; - int len; - int bytecodeLen; - const char* buffer; - char* bytecode; - }; static_assert(sizeof(ScriptFile) == 0x28); - - struct StringTableCell - { - const char* string; - int hash; - }; - - struct StringTable - { - const char* name; - int columnCount; - int rowCount; - StringTableCell* values; - }; static_assert(sizeof(StringTable) == 0x18); - - enum NetConstStringType - { - }; - - enum NetConstStringSource - { - }; - - struct NetConstStrings - { - const char* name; - NetConstStringType stringType; - NetConstStringSource sourceType; - unsigned int flags; - unsigned int entryCount; - const char** stringList; - }; static_assert(sizeof(NetConstStrings) == 0x20); - - struct LuaFile - { - const char* name; - int len; - char strippingType; - const char* buffer; - }; static_assert(sizeof(LuaFile) == 0x18); - - struct TTFDef - { - const char* name; - int fileLen; - const char* file; - void* ftFace; - }; static_assert(sizeof(TTFDef) == 0x20); - - union XAssetHeader - { - PhysicsLibrary* physicsLibrary; - PhysicsSFXEventAsset* physicsSFXEventAsset; - PhysicsVFXEventAsset* physicsVFXEventAsset; - PhysicsAsset* physicsAsset; - PhysicsFXPipeline* physicsFXPipeline; - PhysicsFXShape* physicsFXShape; - XAnimParts* parts; - XModelSurfs* modelSurfs; - XModel* model; - MayhemData* mayhem; - Material* material; - //ComputeShader* computeShader; - //MaterialVertexShader* vertexShader; - //MaterialHullShader* hullShader; - //MaterialDomainShader* domainShader; - //MaterialPixelShader* pixelShader; - //MaterialVertexDeclaration* vertexDecl; - //MaterialTechniqueSet* techniqueSet; - GfxImage* image; - SndGlobals* soundGlobals; - SndBankResident* soundBankResident; - SndBankTransient* soundBankTransient; - //clipMap_t* clipMap; - //ComWorld* comWorld; - //GlassWorld* glassWorld; - //PathData* pathData; - //NavMeshData* navMeshData; - //MapEnts* mapEnts; - //FxWorld* fxWorld; - //GfxWorld* gfxWorld; - //GfxWorldTransientZone* gfxWorldTransientZone; - //GfxIESProfile* iesProfile; - //GfxLightDef* lightDef; - //void* uiMap; - //AnimationClass* animClass; - //PlayerAnimScript* playerAnim; - //Gesture* gesture; - LocalizeEntry* localize; - //WeaponAttachment* attachment; - //WeaponCompleteDef* weapon; - //ParticleSystemDef* vfx; - //FxImpactTable* impactFx; - //SurfaceFxTable* surfaceFx; - //void* aiType; - //void* mpType; - //void* character; - //void* modelAlias; - //void* unknown; - RawFile* rawfile; - ScriptFile* scriptfile; - StringTable* stringTable; - //LeaderboardDef* leaderboardDef; - //VirtualLeaderboardDef* virtualLeaderboardDef; - //DDLFile* ddlFile; - //TracerDef* tracerDef; - //VehicleDef* vehDef; - //AddonMapEnts* addonMapEnts; - NetConstStrings* netConstStrings; - LuaFile* luaFile; - //ScriptableDef* scriptable; - //EquipmentSoundTable* equipSndTable; - //VectorField* vectorField; - //FxParticleSimAnimation* particleSimAnimation; - //StreamingInfo* streamingInfo; - //LaserDef* laserDef; - TTFDef* ttfDef; - //SuitDef* suitDef; - //SuitAnimPackage* suitAnimPackage; - //SpaceshipTargetDef* spaceshipTargetDef; - //RumbleInfo* rumble; - //RumbleGraph* rumbleGraph; - //WeaponAnimPackage* weaponAnimPackage; - //WeaponSFXPackage* weaponSFXPackage; - //WeaponVFXPackage* weaponVFXPackage; - //BehaviorTree* behaviorTree; - //XAnimArcheType* archeType; - //XAnimProceduralBones *proceduralBones; - //ReticleDef* reticleDef; - //GfxLightMap* lightMap; - void* data; - }; - - struct XAsset - { - XAssetType type; - XAssetHeader header; - }; - - struct DBFile - { - char name[64]; - void* handle; - char __pad[0x60]; - }; - static_assert(sizeof(DBFile) == 0xA8); - } - using namespace assets; - - struct GfxFont - { - const char* fontName; - int pixelHeight; - TTFDef* ttfDef; - }; - - enum GfxDrawSceneMethod - { - GFX_DRAW_SCENE_STANDARD = 0x1, - }; - - struct GfxDrawMethod - { - int drawScene; - int baseTechType; - int emissiveTechType; - int forceTechType; - }; - namespace demonware { enum DWOnlineStatus : std::int32_t @@ -3239,4 +1460,294 @@ namespace game #pragma pack(pop) } using namespace demonware; + + namespace pmem + { + enum PMem_Stack : __int32 + { + PMEM_STACK_GAME = 0x0, + PMEM_STACK_RENDER_TARGETS = 0x1, + PMEM_STACK_MEM_VIRTUAL = 0x2, + PMEM_STACK_MEMCARD_LARGE_BUFFER = 0x3, + PMEM_STACK_SOUND = 0x4, + PMEM_STACK_STASHED_MEMORY = 0x5, + PMEM_STACK_CINEMATIC = 0x6, + PMEM_STACK_COUNT = 0x7, + }; + + enum PMem_Source + { + PMEM_SOURCE_EXTERNAL = 0x0, + PMEM_SOURCE_SCRIPT = 0x1, + }; + + enum PMem_Direction : __int32 + { + PHYS_ALLOC_LOW = 0x0, + PHYS_ALLOC_HIGH = 0x1, + PHYS_ALLOC_COUNT = 0x2, + }; + + enum Mem_PageID + { + }; + + struct Mem_PageRange + { + Mem_PageID firstPageID; + Mem_PageID lastPageID; + }; + + struct PhysicalMemoryAllocation + { + const char* name; + char* prev_buffer; + char* next_buffer; + unsigned __int64 pos; + Mem_PageRange pageRange; + }; + + struct PhysicalMemoryPrim + { + const char* name; + unsigned int allocListCount; + char __pad0[4]; + unsigned __int8* buf; + unsigned __int64 unk_pos; + int unk1; + char __pad2[4]; + unsigned __int64 pos; + PhysicalMemoryAllocation allocList[32]; + }; + + struct PhysicalMemory + { + PhysicalMemoryPrim prim[2]; + }; + } + using namespace pmem; + + struct GfxFont + { + const char* fontName; + int pixelHeight; + TTFDef* ttfDef; + }; + + enum GfxDrawSceneMethod + { + GFX_DRAW_SCENE_STANDARD = 0x1, + }; + + struct GfxDrawMethod + { + int drawScene; + int baseTechType; + int emissiveTechType; + int forceTechType; + }; + + struct directory_t + { + char path[256]; + char gamedir[256]; + }; + + struct searchpath_s + { + searchpath_s* next; + directory_t* dir; + int bLocalized; + int playersFolder; + int language; + int pad; + }; + + enum VariableType + { + VAR_UNDEFINED = 0x0, + VAR_BEGIN_REF = 0x1, + VAR_POINTER = 0x1, + VAR_STRING = 0x2, + VAR_ISTRING = 0x3, + VAR_VECTOR = 0x4, + VAR_END_REF = 0x5, + VAR_FLOAT = 0x5, + VAR_INTEGER = 0x6, + VAR_CODEPOS = 0x7, + VAR_PRECODEPOS = 0x8, + VAR_FUNCTION = 0x9, + VAR_BUILTIN_FUNCTION = 0xA, + VAR_BUILTIN_METHOD = 0xB, + VAR_STACK = 0xC, + VAR_ANIMATION = 0xD, + VAR_PRE_ANIMATION = 0xE, + VAR_THREAD = 0xF, + VAR_NOTIFY_THREAD = 0x10, + VAR_TIME_THREAD = 0x11, + VAR_CHILD_THREAD = 0x12, + VAR_OBJECT = 0x13, + VAR_DEAD_ENTITY = 0x14, + VAR_ENTITY = 0x15, + VAR_ARRAY = 0x16, + VAR_DEAD_THREAD = 0x17, + VAR_COUNT = 0x18, + VAR_FREE = 0x18, + VAR_THREAD_LIST = 0x19, + VAR_ENDON_LIST = 0x1A, + VAR_TOTAL_COUNT = 0x1B, + }; + + struct scr_entref_t + { + unsigned short entnum; + unsigned short classnum; + }; + + struct VariableStackBuffer + { + const char* pos; + unsigned __int16 size; + unsigned __int16 bufLen; + unsigned __int16 localId; + char time; + char buf[1]; + }; + + union VariableUnion + { + int intValue; + unsigned int uintValue; + float floatValue; + unsigned int stringValue; + const float* vectorValue; + const char* codePosValue; + unsigned int pointerValue; + VariableStackBuffer* stackValue; + unsigned int entityOffset; + }; + + struct VariableValue + { + VariableUnion u; + int type; + }; + + struct function_stack_t + { + const char* pos; + unsigned int localId; + unsigned int localVarCount; + VariableValue* top; + VariableValue* startTop; + }; + + struct function_frame_t + { + function_stack_t fs; + int topType; + }; + + struct scrVmPub_t + { + unsigned int* localVars; + VariableValue* maxstack; + int function_count; + function_frame_t* function_frame; + VariableValue* top; + unsigned int inparamcount; + unsigned int outparamcount; + function_frame_t function_frame_start[32]; + VariableValue stack[2048]; + }; + + struct ObjectVariableChildren + { + unsigned __int16 firstChild; + unsigned __int16 lastChild; + }; + + struct ObjectVariableValue_u_f + { + unsigned __int16 prev; + unsigned __int16 next; + }; + + union ObjectVariableValue_u_o_u + { + unsigned __int16 size; + unsigned __int16 entnum; + unsigned __int16 nextEntId; + unsigned __int16 self; + }; + + struct ObjectVariableValue_u_o + { + unsigned __int16 refCount; + ObjectVariableValue_u_o_u u; + }; + + union ObjectVariableValue_w + { + unsigned int type; + unsigned int classnum; + unsigned int notifyName; + unsigned int waitTime; + unsigned int parentLocalId; + }; + + struct ChildVariableValue_u_f + { + unsigned __int16 prev; + unsigned __int16 next; + }; + + union ChildVariableValue_u + { + ChildVariableValue_u_f f; + VariableUnion u; + }; + + struct ChildBucketMatchKeys_keys + { + unsigned __int16 name_hi; + unsigned __int16 parentId; + }; + + union ChildBucketMatchKeys + { + ChildBucketMatchKeys_keys keys; + unsigned int match; + }; + + struct ChildVariableValue + { + ChildVariableValue_u u; + unsigned __int16 next; + char type; + char name_lo; + ChildBucketMatchKeys k; + unsigned __int16 nextSibling; + unsigned __int16 prevSibling; + }; + + union ObjectVariableValue_u + { + ObjectVariableValue_u_f f; + ObjectVariableValue_u_o o; + }; + + struct ObjectVariableValue + { + ObjectVariableValue_u u; + ObjectVariableValue_w w; + }; + + struct scrVarGlob_t + { + ObjectVariableValue objectVariableValue[40960]; + ObjectVariableChildren objectVariableChildren[40960]; + unsigned __int16 childVariableBucket[65536]; + ChildVariableValue childVariableValue[384000]; + }; } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 8b30df2c..06d19095 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -8,6 +8,16 @@ namespace game * Functions **************************************************************/ + WEAK symbol AddRefToValue{ 0xC04360 }; + WEAK symbol RemoveRefToValue{ 0xC05DB0 }; + WEAK symbol AddRefToObject{ 0xC04350 }; + WEAK symbol RemoveRefToObject{ 0xC05CA0 }; + WEAK symbol AllocThread{ 0xC04580 }; + WEAK symbol AllocVariable{ 0xC04650 }; + WEAK symbol Scr_LoadScript{ 0xBFD900 }; + WEAK symbol Scr_GetFunctionHandle{ 0xBFD780 }; + WEAK symbol Scr_ExecThread{ 0xC0ACD0 }; + WEAK symbol BG_GetGravity{ 0x68DD0 }; WEAK symbol Com_Error{ 0xB8D830 }; @@ -52,18 +62,26 @@ namespace game WEAK symbol Cmd_TokenizeString{ 0xB7D850 }; WEAK symbol 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 DB_Zip_InitThreadMemory{ 0xE78290 }; + WEAK symbol DB_Zip_ShutdownThreadMemory{ 0xE782D0 }; + + WEAK symbol DB_LoadXAssets{ 0xA78630 }; + WEAK symbol DB_XAssetExists{ 0xA7C3A0 }; + WEAK symbol DB_GetRawBuffer{ 0xA77AB0 }; + WEAK symbol DB_GetXAssetHeaderName{ 0x9E5BA0 }; WEAK symbol DB_EnumXAssets_FastFile{ 0xA76CE0 }; WEAK symbol DB_IsXAssetDefault{ 0xA780D0 }; WEAK symbol DB_FindXAssetHeader{ 0xA76E00 }; WEAK symbol DB_IsLocalized{ 0x3BC500 }; WEAK symbol DB_ReadRawFile{ 0xA79E30 }; + WEAK symbol DB_GetRawFileLen{ 0xF20AF0 }; - namespace DDL - { - WEAK symbol DDL_Lookup_GetEnumString{ 0x30430 }; - } - + WEAK symbol DDL_Lookup_GetEnumString{ 0x30430 }; WEAK symbol DDL_StateIsLeaf{ 0x2E3C0 }; WEAK symbol DDL_GetType{ 0x2E5A0 }; WEAK symbol DDL_GetValue{ 0x2F5E0 }; @@ -104,7 +122,20 @@ namespace game WEAK symbol FS_FreeFile{ 0xCDE1F0 }; WEAK symbol FS_Printf{ 0xCDD1C0 }; + WEAK symbol GetObjectType{ 0xC059E0 }; + WEAK symbol GetVariable{ 0xC05A90 }; + WEAK symbol GetNewVariable{ 0xC05660 }; + WEAK symbol GetNewArrayVariable{ 0xC054E0 }; + WEAK symbol FindVariable{ 0xC05100 }; + WEAK symbol FindEntityId{ 0xC05000 }; + WEAK symbol RemoveVariableValue{ 0xC05E90 }; + WEAK symbol GetEntityFieldValue{ 0xC09CC0 }; + WEAK symbol G_MainMP_GetClientScore{ 0xB20550 }; + WEAK symbol G_GetWeaponForName { 0x733D40 }; + WEAK symbol G_GivePlayerWeapon{ 0x733D40 }; + WEAK symbol G_InitializeAmmo{ 0x733D40 }; WEAK symbol 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) \ 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 Sys_Cwd{ 0xCFE5A0 }; + WEAK symbol Sys_Milliseconds{ 0xD58110 }; WEAK symbol Sys_CreateFile{ 0xCFDF50 }; @@ -154,14 +187,40 @@ namespace game WEAK symbol Sys_SendPacket{ 0xD57DE0 }; WEAK symbol Sys_GetPacket{ 0xD57D50 }; + WEAK symbol Sys_IsDatabaseThread{ 0xBB7B30 }; + + WEAK symbol SEH_GetCurrentLanguageCode{ 0xCBAF50 }; + WEAK symbol SEH_GetCurrentLanguageName{ 0xCBB090 }; + WEAK symbol SEH_GetLanguageName{ 0xCBB140 }; + + WEAK symbol PMem_BeginAlloc{ 0xCF0E10 }; + WEAK symbol PMem_EndAlloc{ 0xCF1070 }; + WEAK symbol PMem_AllocFromSource_NoDebug{ 0xCF0A90 }; + WEAK symbol PMem_Free{ 0xCF10D0 }; + + WEAK symbol VM_Execute{ 0xC0CDB0 }; + + WEAK symbol Scr_NotifyId{ 0xC0C2B0 }; + WEAK symbol Scr_AllocVector{ 0xC06960 }; WEAK symbol Scr_AddInt{ 0xC0A580 }; + WEAK symbol Scr_CastString{ 0xC06AE0 }; + WEAK symbol Scr_ClearOutParams{ 0xC0ABC0 }; + WEAK symbol Scr_GetEntityIdRef{ 0xC09050 }; + WEAK symbol Scr_SetObjectField{ 0x40B6E0 }; WEAK symbol Scr_GetInt{ 0xC0B950 }; + WEAK symbol Scr_ErrorInternal{ 0xC0AC30 }; WEAK symbol ScrPlace_GetViewPlacement{ 0x9E4090 }; WEAK symbol StringTable_Lookup{ 0xCE7950 }; WEAK symbol StringTable_GetColumnValueForRow{ 0xCE78E0 }; + WEAK symbol SL_GetString{ 0xC037E0 }; + WEAK symbol SL_ConvertToString{ 0xC03300 }; + WEAK symbol SL_GetCanonicalString{ 0xBFD340 }; + WEAK symbol SV_Cmd_TokenizeString{ 0xB7DD00 }; WEAK symbol SV_Cmd_EndTokenizedString{ 0xB7DCC0 }; WEAK symbol SV_Loaded{ 0xC114C0 }; WEAK symbol SV_MapExists{ 0xCDB620 }; WEAK symbol SV_BotIsBot{ 0xC3BC90 }; + WEAK symbol SV_GetPlayerstateForClientNum{ 0xC123A0 }; WEAK symbol SND_StopSounds{ 0xCA06E0 }; WEAK symbol SND_SetMusicState{ 0xC9E110 }; + WEAK symbol longjmp{ 0x12C0758 }; + WEAK symbol _setjmp{ 0x1423110 }; + /*************************************************************** * Variables **************************************************************/ + WEAK symbol g_script_error_level{ 0x6B16298 }; + WEAK symbol g_script_error{ 0x6B162A0 }; + + WEAK symbol levelEntityId{ 0x665A120 }; + WEAK symbol sv_cmd_args{ 0x5D65C20 }; WEAK symbol cmd_args{ 0x5D65B70 }; WEAK symbol cmd_functions{ 0x5D65CC8 }; @@ -216,4 +284,21 @@ namespace game WEAK symbol query_socket{ 0x779FDC8 }; WEAK symbol threadIds{ 0x602BAB0 }; + + WEAK symbol scr_VarGlob{ 0x6691180 }; + WEAK symbol scr_VmPub{ 0x6B183B0 }; + WEAK symbol scr_function_stack{ 0x6B22908 }; + + WEAK symbol g_mem{ 0x7685560 }; + WEAK symbol g_scriptmem{ 0x7685FC0 }; + + WEAK symbol fs_searchpaths{ 0x756DEE0 }; + + WEAK symbol g_load{ 0x52A8010 }; + WEAK symbol g_authLoad_isSecure{ 0x529DD90 }; + WEAK symbol db_stream{ 0x52A8050 }; + WEAK symbol db_zip_stream{ 0x529DD30 }; + WEAK symbol db_zip_memory{ 0x525B500 }; + + WEAK symbol g_streamPos{ 0x5687E30 }; } diff --git a/src/client/loader/component_interface.hpp b/src/client/loader/component_interface.hpp index 7c6854a1..3fac3f2d 100644 --- a/src/client/loader/component_interface.hpp +++ b/src/client/loader/component_interface.hpp @@ -4,6 +4,7 @@ enum class component_priority { min = 0, dvars, + ranked, steam_proxy, arxan, }; diff --git a/src/client/loader/component_loader.hpp b/src/client/loader/component_loader.hpp index ba28c2e9..bda7570c 100644 --- a/src/client/loader/component_loader.hpp +++ b/src/client/loader/component_loader.hpp @@ -25,11 +25,25 @@ public: 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( std::make_unique(), std::string( component_name.begin(), - component_name.begin() + component_name.find("::component") + component_name.begin() + last ) ); } diff --git a/src/client/main.cpp b/src/client/main.cpp index 02d65ee2..d070dcee 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -107,7 +107,9 @@ void limit_parallel_dll_loading() int main() { - ShowWindow(GetConsoleWindow(), SW_HIDE); + if (!game::environment::is_dedi()) + ShowWindow(GetConsoleWindow(), SW_HIDE); + console::init(); FARPROC entry_point; diff --git a/src/common/utils/hook.cpp b/src/common/utils/hook.cpp index 76236588..b58b34d3 100644 --- a/src/common/utils/hook.cpp +++ b/src/common/utils/hook.cpp @@ -15,27 +15,45 @@ #undef min #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 { - 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(base_address) - (1ull << 31); + target_address &= ~(granularity - 1); + while (true) { - offset += size; - auto* target_address = static_cast(base_address) - offset; - if (is_relatively_far(base_address, target_address)) + target_address += granularity; + + auto* target_ptr = reinterpret_cast(target_address); + if (is_relatively_far(base_address, target_ptr)) { return nullptr; } - const auto res = VirtualAlloc(const_cast(target_address), size, MEM_RESERVE | MEM_COMMIT, + const auto res = VirtualAlloc(target_ptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (res) { - if (is_relatively_far(base_address, target_address)) + if (is_relatively_far(base_address, target_ptr)) { VirtualFree(res, 0, MEM_RELEASE); return nullptr; @@ -54,8 +72,10 @@ namespace utils::hook memory(const void* ptr) : memory() { - this->length_ = 0x1000; - this->buffer_ = allocate_somewhere_near(ptr, this->length_); + static const auto allocation_granularity = get_allocation_granularity(); + this->length_ = allocation_granularity; + + this->buffer_ = allocate_somewhere_near(ptr, allocation_granularity, this->length_); if (!this->buffer_) { throw std::runtime_error("Failed to allocate"); @@ -144,28 +164,6 @@ namespace utils::hook }); } - concurrency::container>& get_original_data_map() - { - static concurrency::container> og_data{}; - return og_data; - } - - void store_original_data(const void* /*data*/, size_t /*length*/) - { - /*get_original_data_map().access([data, length](std::map& og_map) - { - const auto data_ptr = static_cast(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() { static class min_hook_init @@ -199,12 +197,26 @@ namespace utils::hook this->push(rsi); 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() { - 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(rsi); @@ -250,12 +262,12 @@ namespace utils::hook asmjit::Error assembler::call(void* target) { - return Assembler::call(size_t(target)); + return Assembler::call(reinterpret_cast(target)); } asmjit::Error assembler::jmp(void* target) { - return Assembler::jmp(size_t(target)); + return Assembler::jmp(reinterpret_cast(target)); } detour::detour() @@ -299,7 +311,6 @@ namespace utils::hook { this->clear(); this->place_ = place; - store_original_data(place, 14); if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) { @@ -332,6 +343,11 @@ namespace utils::hook this->moved_data_ = move_hook(this->place_); } + void* detour::get_place() const + { + return this->place_; + } + void* detour::get_original() const { return this->original_; @@ -345,15 +361,14 @@ namespace utils::hook } } - std::optional> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub) + std::optional> iat(const nt::library& library, const std::string& target_library, + const std::string& process, void* stub) { if (!library.is_valid()) return {}; auto* const ptr = library.get_iat_entry(target_library, process); if (!ptr) return {}; - store_original_data(ptr, sizeof(*ptr)); - DWORD protect; VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); @@ -365,8 +380,6 @@ namespace utils::hook void nop(void* place, const size_t length) { - store_original_data(place, length); - DWORD 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) { - store_original_data(place, length); - DWORD 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) { - const int64_t diff = size_t(data) - (size_t(pointer) + offset); - const auto small_diff = int32_t(diff); - return diff != int64_t(small_diff); + return is_relatively_far(reinterpret_cast(pointer), reinterpret_cast(data), offset); + } + + bool is_relatively_far(const size_t pointer, const size_t data, const int offset) + { + const auto diff = static_cast(data - (pointer + offset)); + const auto small_diff = static_cast(diff); + return diff != static_cast(small_diff); } void call(void* pointer, void* data) @@ -433,9 +449,10 @@ namespace utils::hook uint8_t copy_data[5]; copy_data[0] = 0xE8; - *reinterpret_cast(©_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5)); + *reinterpret_cast(©_data[1]) = static_cast(reinterpret_cast(data) - ( + reinterpret_cast(pointer) + 5)); - auto* patch_pointer = PBYTE(pointer); + auto* patch_pointer = static_cast(pointer); copy(patch_pointer, copy_data, sizeof(copy_data)); } @@ -524,24 +541,35 @@ namespace utils::hook asm_function(a); 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; } - void inject(void* pointer, const void* data) + void inject(size_t pointer, size_t data) { if (is_relatively_far(pointer, data, 4)) { throw std::runtime_error("Too far away to create 32bit relative branch"); } - set(pointer, int32_t(size_t(data) - (size_t(pointer) + 4))); + set( + pointer, static_cast(data - (pointer + 4))); + } + + void inject(void* pointer, const void* data) + { + return inject(reinterpret_cast(pointer), reinterpret_cast(data)); } void inject(const size_t pointer, const void* data) { - return inject(reinterpret_cast(pointer), data); + return inject(pointer, reinterpret_cast(data)); } std::vector move_hook(void* pointer) @@ -589,26 +617,4 @@ namespace utils::hook return extract(data + 1); } - - std::vector query_original_data(const void* data, const size_t length) - { - std::vector og_data{}; - og_data.resize(length); - memcpy(og_data.data(), data, length); - - get_original_data_map().access([data, length, &og_data](const std::map& og_map) - { - auto* ptr = static_cast(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; - } -} +} \ No newline at end of file diff --git a/src/common/utils/hook.hpp b/src/common/utils/hook.hpp index dec15029..6c8ba52b 100644 --- a/src/common/utils/hook.hpp +++ b/src/common/utils/hook.hpp @@ -1,11 +1,14 @@ #pragma once #include "signature.hpp" +#include "memory.hpp" #include #include using namespace asmjit::x86; +Mem seg_ptr(const SReg& segment, const uint64_t off); + namespace utils::hook { namespace detail @@ -125,6 +128,8 @@ namespace utils::hook void move(); + void* get_place() const; + template T* get() const { @@ -147,7 +152,8 @@ namespace utils::hook void un_move(); }; - std::optional> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); + std::optional> 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(size_t place, size_t length); @@ -159,6 +165,7 @@ namespace utils::hook 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(size_t pointer, size_t data, int offset = 5); void call(void* 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(size_t pointer, const void* data); + void inject(size_t pointer, size_t data); std::vector move_hook(void* pointer); std::vector move_hook(size_t pointer); @@ -210,5 +218,51 @@ namespace utils::hook return static_cast(func)(args...); } - std::vector query_original_data(const void* data, size_t length); -} + template + void* allocate_far_jump() + { + constexpr auto alloc_size = 0x1000; + constexpr auto far_jmp_size = 0xC; + + const auto alloc_jump_table = [] + { + return reinterpret_cast( + 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 + void* create_far_jump(const T dest) + { + static std::unordered_map allocated_jumps; + if (const auto iter = allocated_jumps.find(reinterpret_cast(dest)); iter != allocated_jumps.end()) + { + return iter->second; + } + + const auto pos = allocate_far_jump(); + jump(pos, dest, true); + allocated_jumps.insert(std::make_pair(dest, pos)); + return pos; + } + + template + void far_jump(const size_t address, const T dest) + { + const auto pos = create_far_jump(dest); + jump(address, pos, false); + } +} \ No newline at end of file diff --git a/src/common/utils/memory.cpp b/src/common/utils/memory.cpp index 99a03916..3ab26666 100644 --- a/src/common/utils/memory.cpp +++ b/src/common/utils/memory.cpp @@ -166,6 +166,33 @@ namespace utils 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(system_info.lpMinimumApplicationAddress)) + { + return nullptr; + } + + const auto result = VirtualAlloc(reinterpret_cast(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() { return &memory::mem_allocator_; diff --git a/src/common/utils/memory.hpp b/src/common/utils/memory.hpp index 01f9554f..9887e308 100644 --- a/src/common/utils/memory.hpp +++ b/src/common/utils/memory.hpp @@ -69,6 +69,8 @@ namespace utils static bool is_bad_code_ptr(const 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(); private: diff --git a/update_quak_zt.bat b/update_quak_zt.bat new file mode 100644 index 00000000..93d2ab82 --- /dev/null +++ b/update_quak_zt.bat @@ -0,0 +1,2 @@ +cd deps/gsc-tool +git pull origin iw7 \ No newline at end of file