From b91b43d26a12f9eb9c35479106d41f5b3b8c248d Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:56:12 +0100 Subject: [PATCH 01/81] fix .gitmodules common_cmake url. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index de5a95e..188ce2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "common_cmake"] path = common_cmake - url = git@github.com:bl-sdk/common_cmake.git + url = https://github.com/bl-sdk/common_cmake From 626daef39f9f07f2f5c80399e7efab2914854672 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:56:27 +0100 Subject: [PATCH 02/81] Add .idea to the .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1948346..eb698b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vs .vscode +.idea # C/C++ excludes .cache/clangd From fda92b93ca46c1e19c29e69f4baefc4ea71e95bb Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:05:37 +0100 Subject: [PATCH 03/81] dirty bl1 AbstractHook impl --- src/unrealsdk/game/bl1/antidebug.cpp | 117 ++++ src/unrealsdk/game/bl1/bl1.cpp | 941 +++++++++++++++++++++++++++ src/unrealsdk/game/bl1/bl1.h | 144 ++++ 3 files changed, 1202 insertions(+) create mode 100644 src/unrealsdk/game/bl1/antidebug.cpp create mode 100644 src/unrealsdk/game/bl1/bl1.cpp create mode 100644 src/unrealsdk/game/bl1/bl1.h diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp new file mode 100644 index 0000000..191c81e --- /dev/null +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -0,0 +1,117 @@ + +// - NOTE - +// Copied from bl2/antidebug.cpp +// + +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game { + + namespace { + +// NOLINTBEGIN(readability-identifier-naming) +// NOLINTNEXTLINE(modernize-use-using) - need a typedef for calling conventions in msvc + typedef NTSTATUS(WINAPI* NtSetInformationThread_func)( + HANDLE ThreadHandle, + THREAD_INFORMATION_CLASS ThreadInformationClass, + PVOID ThreadInformation, + ULONG ThreadInformationLength); +// NOLINTNEXTLINE(modernize-use-using) + typedef NTSTATUS(WINAPI* NtQueryInformationProcess_func)(HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength); + +#if defined(__clang__) + #pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + + constexpr auto ThreadHideFromDebugger = static_cast(17); + constexpr auto ProcessDebugObjectHandle = static_cast(30); + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// NOLINTEND(readability-identifier-naming) + +// NOLINTBEGIN(readability-identifier-naming) + NtSetInformationThread_func NtSetInformationThread_ptr; + NTSTATUS NTAPI NtSetInformationThread_hook(HANDLE ThreadHandle, + THREAD_INFORMATION_CLASS ThreadInformationClass, + PVOID ThreadInformation, + ULONG ThreadInformationLength) { + // NOLINTEND(readability-identifier-naming) + if (ThreadInformationClass == ThreadHideFromDebugger) { + return STATUS_SUCCESS; + } + + return NtSetInformationThread_ptr(ThreadHandle, ThreadInformationClass, ThreadInformation, + ThreadInformationLength); + } + static_assert(std::is_same_v, + "NtSetInformationThread signature is incorrect"); + +// NOLINTBEGIN(readability-identifier-naming) + NtQueryInformationProcess_func NtQueryInformationProcess_ptr; + NTSTATUS WINAPI NtQueryInformationProcess_hook(HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength) { + // NOLINTEND(readability-identifier-naming) + if (ProcessInformationClass == ProcessDebugObjectHandle) { + return STATUS_PORT_NOT_SET; + } + + return NtQueryInformationProcess_ptr(ProcessHandle, ProcessInformationClass, ProcessInformation, + ProcessInformationLength, ReturnLength); + } + static_assert( + std::is_same_v, + "NtQueryInformationProcess signature is incorrect"); + + } // namespace + + void BL1Hook::hook_antidebug(void) { + MH_STATUS status = MH_OK; + + LPVOID target = nullptr; + status = MH_CreateHookApiEx(L"ntdll", "NtSetInformationThread", + reinterpret_cast(NtSetInformationThread_hook), + reinterpret_cast(&NtSetInformationThread_ptr), &target); + if (status != MH_OK) { + LOG(ERROR, "Failed to create NtSetInformationThread hook: {:x}", + static_cast(status)); + } else { + status = MH_EnableHook(target); + if (status != MH_OK) { + LOG(ERROR, "Failed to enable NtSetInformationThread hook: {:x}", + static_cast(status)); + } + } + + status = MH_CreateHookApiEx(L"ntdll", "NtQueryInformationProcess", + reinterpret_cast(NtQueryInformationProcess_hook), + reinterpret_cast(&NtQueryInformationProcess_ptr), &target); + if (status != MH_OK) { + LOG(ERROR, "Failed to create NtQueryInformationProcess hook: {:x}", + static_cast(status)); + } else { + status = MH_EnableHook(target); + if (status != MH_OK) { + LOG(ERROR, "Failed to enable NtQueryInformationProcess hook: {:x}", + static_cast(status)); + } + } + } + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp new file mode 100644 index 0000000..d98e797 --- /dev/null +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -0,0 +1,941 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "unrealsdk/memory.h" +#include "unrealsdk/logging.h" +#include "unrealsdk/unreal/alignment.h" +#include "unrealsdk/unreal/structs/fframe.h" +#include "unrealsdk/unreal/wrappers/gnames.h" + +#include "unrealsdk/unreal/prop_traits.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/copyable_property.h" + +using namespace unrealsdk::memory; +using namespace unrealsdk::unreal; + +//############################################################################// +// | GMALLOC | +//############################################################################// + +namespace { + struct FMalloc; + + struct FMallocVFtable { + void* exec; + void* (__thiscall* u_malloc)(FMalloc* self, uint32_t len, uint32_t align); + void* (__thiscall* u_realloc)(FMalloc* self, void* original, uint32_t len, uint32_t align); + void* (__thiscall* u_free)(FMalloc* self, void* data); + }; + + struct FMalloc { + FMallocVFtable* vftable; + }; + + FMalloc* gmalloc; + +// 8B0D ???????? 8B01 8B50 04 +// 00445911 | 8B0D B43BF701 | mov ecx,dword ptr ds:[1F73BB4] +// 00445917 | 8B01 | mov eax,dword ptr ds:[ecx] +// 00445919 | 8B50 04 | mov edx,dword ptr ds:[eax+4] +// 0044591C | 6A 08 | push 8 +// 0044591E | 68 50010000 | push 150 +// 00445923 | FFD2 | call edx +// First address is constant +// 1F73BB4 -> 069A77B0 -> 01CC3AD8 -> [007B6EB0 013A8F50 013A8F70 013A8FC0] +// in vtable[1] we get +// 013A8F50 | 8B4424 04 | mov eax,dword ptr ss:[esp+4] +// 013A8F54 | 6A 10 | push 10 +// 013A8F56 | 50 | push eax +// 013A8F57 | FF15 B824A201 | call dword ptr ds:[<_aligned_malloc>] +// 013A8F5D | 83C4 08 | add esp,8 +// 013A8F60 | C2 0800 | ret 8 + +// 8B 0D ?? ?? ?? ?? 8B 01 8B 50 04 + +// TODO: This pattern is not unique to GMalloc + const constinit + Pattern<11> GMALLOC_PATTERN{ + "8B 0D {????????}" + "8B 01" + "8B 50 04" + }; + + GObjects gobjects_wrapper{}; + + const constinit + Pattern<15> GOBJECTS_SIG{ + "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] + "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] + "8B50 0C" // mov edx,dword ptr ds:[eax+C] + "2158 08" // and dword ptr ds:[eax+8],ebx + }; + + GNames gnames_wrapper{}; + + const constinit + Pattern<15> GNAMES_SIG{ + "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] + "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] + "68 00100000" // push 1000 + "6A 00" // push 0 + }; + + + //############################################################################// + // | LOAD PACKAGE | + //############################################################################// + +// +// Candidate Function: 005e1770 +// 005E1770 | 55 | push ebp +// 005E1771 | 8BEC | mov ebp,esp +// 005E1773 | 6A FF | push FFFFFFFF +// 005E1775 | 68 B0CD8E01 | push borderlands.18ECDB0 +// 005E177A | 64:A1 00000000 | mov eax,dword ptr fs:[0] +// 005E1780 | 50 | push eax +// 005E1781 | 83EC 28 | sub esp,28 +// 005E1784 | 53 | push ebx +// 005E1785 | 56 | push esi +// 005E1786 | 57 | push edi +// 005E1787 | A1 8069F101 | mov eax,dword ptr ds:[1F16980] +// 005E178C | 33C5 | xor eax,ebp +// 005E178E | 50 | push eax + + using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); + load_package_func load_package_ptr; + +// Pattern as a single string for easy lookups +// 55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 83 EC 28 53 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D 45 F4 + + const constinit + Pattern<34> LOAD_PACKAGE_PATTERN{ + "55" // push ebp + "8B EC" // mov ebp,esp + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18ECDB0 + "64 A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 28" // sub esp,28 + "53 56 57" // push ebx + "A1 ????????" // push esi + "33C5" // push edi + "50" // mov eax,dword ptr ds:[1F16980] + "8D45 F4" // xor eax,ebp + }; + + + //############################################################################// + // | FNAME INIT | + //############################################################################// + +// Candidate function: 005cef40 +// 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81EC 98 0C 00 00 A1 ?? ?? ?? ?? 33 C4 898424 940C0000 + const constinit + Pattern<34> FNAME_INIT_SIG{ + "6A FF" // push FFFFFFFF + "68 ?? ?? ?? ??" // push borderlands.18EB45B + "64 A1 00 00 00 00" // mov eax,dword ptr fs:[0] + "50" // push eax + "81EC 98 0C 00 00" // sub esp,C98 + "A1 ?? ?? ?? ??" // mov eax,dword ptr ds:[1F16980] + "33 C4" // xor eax,esp + "898424 940C0000" // mov dword ptr ss:[esp+C94],eax + }; + + typedef void(__thiscall* fname_init_func)( + FName* name, + const wchar_t* str, + int32_t number, + int32_t find_type, + int32_t split_name + ); + + fname_init_func fname_init_ptr = nullptr; + + // Usage of this function is inlined so we have to manually grab the pointer. + const constinit + Pattern<36> FFRAME_STEP_SIG{ + "74 ??" // 74 22 | je borderlands.59CEA4 + "8B45 D4" // 8B45 D4 | mov eax,dword ptr ss:[ebp-2C] + "830D ???????? 02" // 830D E83BF701 02 | or dword ptr ds:[1F73BE8],2 + "0FB610" // 0FB610 | movzx edx,byte ptr ds:[eax] + "8B1495 {????????}" // 8B1495 807AF901 | mov edx,dword ptr ds:[edx*4+1F97A80] + "57" // 57 | push edi + "8D4D BC" // 8D4D BC | lea ecx,dword ptr ss:[ebp-44] + "40" // 40 | inc eax + "51" // 51 | push ecx + "8B4D EC" // 8B4D EC | mov ecx,dword ptr ss:[ebp-14] + "8945 D4" // 8945 D4 | mov dword ptr ss:[ebp-2C],eax + "FFD2" // FFD2 | call edx + }; + + // RET 0x8; Callee cleans up the stack (8 bytes) + typedef void(__stdcall* fframe_step_func)(FFrame*, void*); + fframe_step_func** fframe_step_gnatives; + +// This one was basically identical to BL2. +// pattern: 8B4424 04 56 8BF1 3BF0 7476 85F6 7472 8B4E 28 53 + typedef void(__thiscall* get_path_name_func)(const UObject* self, const UObject* stop_outer, ManagedFString* str); + get_path_name_func get_path_name_ptr; + const constinit + Pattern<19> GET_PATH_NAME_PATTERN{ + "8B4424 04" // mov eax,dword ptr ss:[esp+4] + "56" // push esi + "8BF1" // mov esi,ecx + "3BF0" // cmp esi,eax + "74 76" // je borderlands.5CF501 + "85F6" // test esi,esi + "74 72" // je borderlands.5CF501 + "8B4E 28" // mov ecx,dword ptr ds:[esi+28] + "53" // push ebx + }; + + typedef UObject* (__cdecl* construct_obj_func)( + UClass* cls, + UObject* outer, + FName name, + uint64_t flags, + UObject* template_obj, + void* error_output_device, + void* instance_graph, + uint32_t assume_template_is_archetype + ); + construct_obj_func construct_obj_ptr; + +// Also similar to bl2: 6AFF68????????64A1000000005083EC0C53555657A1????????33C4508D44242064A3000000008B6C2454896C241485ED + const constinit + Pattern<49> CONSTRUCT_OBJECT_PATTERN{ + "6A FF" // 6A FF | push FFFFFFFF + "68 ????????" // 68 93D68E01 | push borderlands.18ED693 + "64A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83EC 0C" // 83EC 0C | sub esp,C + "53" // 53 | push ebx + "55" // 55 | push ebp + "56" // 56 | push esi + "57" // 57 | push edi + "A1 ????????" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] + "33C4" // 33C4 | xor eax,esp + "50" // 50 | push eax + "8D4424 20" // 8D4424 20 | lea eax,dword ptr ss:[esp+20] + "64 A3 00000000" // 64:A3 00000000 | mov dword ptr fs:[0],eax + "8B6C24 54" // 8B6C24 54 | mov ebp,dword ptr ss:[esp+54] + "896C24 14" // 896C24 14 | mov dword ptr ss:[esp+14],ebp + "85ED" // 85ED | test ebp,ebp + }; + + + // NOLINTNEXTLINE(modernize-use-using) + typedef UObject* (__cdecl* static_find_object_func)( + const UClass* cls, + const UObject* package, + const wchar_t* str, + uint32_t exact_class + ); + + static_find_object_func static_find_object_ptr; + const constinit + Pattern<50> STATIC_FIND_OBJECT_PATTERN{ + "6A FF" // 6A FF | push FFFFFFFF + "68 ?? ?? ?? ??" // 68 90C18E01 | push borderlands.18EC190 + "64 A1 00 00 00 00" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83 EC 24" // 83EC 24 | sub esp,24 + "53" // 53 | push ebx + "55" // 55 | push ebp + "56" // 56 | push esi + "57" // 57 | push edi + "A1 ?? ?? ?? ??" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] + "33 C4" // 33C4 | xor eax,esp + "50" // 50 | push eax + "8D 44 24 38" // 8D4424 38 | lea eax,dword ptr ss:[esp+38] + "64 A3 00 00 00 00" // 64:A3 00000000 | mov dword ptr fs:[0],eax + "8B 74 24 4C" // 8B7424 4C | mov esi,dword ptr ss:[esp+4C] + "8B 7C 24 50" // 8B7C24 50 | mov edi,dword ptr ss:[esp+50] + "8B C6" // 8BC6 | mov eax,esi + "40" // 40 | inc eax + }; + + + typedef void(__fastcall* process_event_func)( + UObject* obj, + void* /*edx*/, + UFunction* func, + void* params, + void* /*null*/); + process_event_func process_event_ptr; + + const constinit + Pattern<43> PROCESS_EVENT_SIG{ + "55" + "8B EC" + "6A FF" + "68 ?? ?? ?? ??" + "64 A1 00 00 00 00" + "50" + "83 EC 40" + "A1 ?? ?? ?? ??" + "33 C5" + "89 45 F0" + "53" + "56" + "57" + "50" + "8D 45 F4" + "64 A3 00 00 00 00" + }; + + + typedef void(__fastcall* call_function_func)( + UObject* obj, + void* /*edx*/, + FFrame* stack, + void* params, + UFunction* func + ); + call_function_func call_function_ptr; + + const constinit + Pattern<31> CALL_FUNCTION_SIG{ + "55" // 55 | push ebp + "8DAC24 FCFBFFFF" // 8DAC24 FCFBFFFF | lea ebp,dword ptr ss:[esp-404] + "81EC 04040000" // 81EC 04040000 | sub esp,404 + "6A FF" // 6A FF | push FFFFFFFF + "68 ????????" // 68 38968E01 | push borderlands.18E9638 + "64 A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83EC 40" // 83EC 40 | sub esp,40 + }; + +} + +//############################################################################// +// | BL1Hook | +//############################################################################// + +namespace unrealsdk::game { + + void BL1Hook::hook(void) { + hook_antidebug(); + + hook_process_event(); + hook_call_function(); + + find_gobjects(); + find_gnames(); + find_fname_init(); + find_fframe_step(); + find_gmalloc(); + find_construct_object(); + find_get_path_name(); + find_static_find_object(); + find_load_package(); + + // hexedit_set_command(); + // hexedit_array_limit(); + // hexedit_array_limit_message(); + + } + + namespace { + + bool Hook_StartMenuUp(hook_manager::Details& details) { + LOG(MISC, "[Hook_StartMenuUp] Invoked!"); + return false; + } + + bool Hook_StartMenuDown(hook_manager::Details& details) { + LOG(MISC, "[Hook_StartMenuDown] Invoked!"); + return false; + } + + bool Hook_AlwaysSaveOnQuit(hook_manager::Details& in) { + + auto dlg_prop = in.args->type->find_prop_and_validate(L"Dlg"_fn); + auto dlg = in.args->get(dlg_prop); + + dlg->get(L"Close"_fn).call(); + + in.obj->get(L"QuitToMenu"_fn) + .call(true); + + in.ret.set(true); + + return true; + } + + } + + void BL1Hook::post_init(void) { + + LOG(MISC, "Attaching Hooks!"); + inject_console(); + + hook_manager::add_hook( + L"WillowGame.WillowGFxMenuScreen:Nav_Up", + hook_manager::Type::PRE, + L"Hook_StartMenuUp", + &Hook_StartMenuUp); + + hook_manager::add_hook( + L"WillowGame.WillowGFxMenuScreen:Nav_Down", + hook_manager::Type::PRE, + L"Hook_StartMenuDown", + &Hook_StartMenuDown); + + hook_manager::add_hook( + L"WillowGame.WillowGFxMenuPause:PromptQuit_Ok", + hook_manager::Type::PRE, + L"Hook_AlwaysSaveOnQuit", + &Hook_AlwaysSaveOnQuit); + + } + + //############################################################################// + // | FIND METHODS | CALLED IN BL1Hook::hook() + //############################################################################// + + void BL1Hook::find_gobjects(void) { + auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); + LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); + gobjects_wrapper = GObjects(gobjects_ptr); + } + + void BL1Hook::find_gnames(void) { + auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); + LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); + gnames_wrapper = GNames(gnames_ptr); + } + + void BL1Hook::find_fname_init(void) { + fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); + LOG(MISC, "FName::Init: {:p}", (void*) fname_init_ptr); + } + + void BL1Hook::find_fframe_step(void) { + fframe_step_gnatives = FFRAME_STEP_SIG.sigscan_nullable(); + + if (fframe_step_gnatives == nullptr) { + LOG(MISC, "FFrame::Step(...), GNatives was null."); + return; + } + + LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); + } + + void BL1Hook::find_gmalloc(void) { + gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); + LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); + } + + void BL1Hook::find_construct_object(void) { + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); + } + + namespace { + void __fastcall Hook_PathName(UObject* obj, void*, UObject* outer, ManagedFString* fstr) { + LOG(MISC, "[Hook_PathName] ~ {:p}, {:p}, {:p}", (void*) obj, (void*) outer, (void*) fstr); + get_path_name_ptr(obj, outer, fstr); + } + } + + void BL1Hook::find_get_path_name(void) { + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan("get_path_name_func"); + LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); + } + + void BL1Hook::find_static_find_object(void) { + static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); + } + + void BL1Hook::find_load_package(void) { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); + } + + //############################################################################// + // | PROCESS EVENT | + //############################################################################// + + void __fastcall Hook_ProcessEvent( + UObject* obj, + void* edx, + UFunction* func, + void* params, + void* null + ) { + try { + // This arg seems to be in the process of being deprecated, no usage in ghidra, always seems + // to be null, and it's gone in later ue versions. Gathering some extra info just in case. + if (null != nullptr) { + LOG( + DEV_WARNING, + L"Null param had a value in process event during func {} on obj {}", + func->get_path_name(), + obj->get_path_name() + ); + } + + auto data = hook_manager::impl::preprocess_hook("ProcessEvent", func, obj); + if (data != nullptr) { + // Copy args so that hooks can't modify them, for parity with call function + const WrappedStruct args_base{func, params}; + WrappedStruct args = args_base.copy_params_only(); + hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; + + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + + if (!block_execution) { + process_event_ptr(obj, edx, func, params, null); + } + + if (hook.ret.has_value()) { + hook.ret.copy_to(reinterpret_cast(params)); + } + + if (!hook_manager::impl::has_post_hooks(*data)) { + return; + } + + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from(reinterpret_cast(params)); + } + + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } + + hook_manager::impl::run_hooks_of_type( + *data, hook_manager::Type::POST_UNCONDITIONAL, + hook); + + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the ProcessEvent hook: {}", ex.what()); + } + + process_event_ptr(obj, edx, func, params, null); + } + + void BL1Hook::hook_process_event(void) { + detour(PROCESS_EVENT_SIG, &Hook_ProcessEvent, &process_event_ptr, "ProcessEvent"); + } + + //############################################################################// + // | CALL FUNCTION | + //############################################################################// + + void __fastcall Hook_CallFunction( + UObject* obj, + void* edx, + FFrame* stack, + void* result, + UFunction* func + ) { + try { + auto data = hook_manager::impl::preprocess_hook("CallFunction", func, obj); + if (data != nullptr) { + WrappedStruct args{func}; + auto original_code = stack->extract_current_args(args); + + hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; + + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + + if (block_execution) { + stack->Code++; + } else { + stack->Code = original_code; + call_function_ptr(obj, edx, stack, result, func); + } + + if (hook.ret.has_value()) { + // Result is a pointer directly to where the property should go, remove the offset + hook.ret.copy_to( + reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } + + if (!hook_manager::impl::has_post_hooks(*data)) { + return; + } + + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from( + reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } + + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } + + hook_manager::impl::run_hooks_of_type( + *data, hook_manager::Type::POST_UNCONDITIONAL, + hook); + + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the CallFunction hook: {}", ex.what()); + } + + call_function_ptr(obj, edx, stack, result, func); + } + + void BL1Hook::hook_call_function(void) { + detour(CALL_FUNCTION_SIG, &Hook_CallFunction, &call_function_ptr, "CallFunction"); + } + + //############################################################################// + // | BL2 IMPL SAYS NOT IMPLEMENTED | + //############################################################################// + + void BL1Hook::flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const { + throw_version_error("Lazy object pointers are not implemented in UE3"); + } + + void BL1Hook::fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, const unreal::UObject* obj) const { + throw_version_error("Lazy object pointers are not implemented in UE3"); + } + + void BL1Hook::ftext_as_culture_invariant(unreal::FText* text, TemporaryFString&& str) const { + throw_version_error("Not implemented in UE3"); + } + + //############################################################################// + // | | + //############################################################################// + + std::wstring BL1Hook::uobject_path_name(const unreal::UObject* obj) const { + ManagedFString str{}; + get_path_name_ptr(obj, nullptr, &str); + return str; + } + + void BL1Hook::process_event(unreal::UObject* object, unreal::UFunction* func, void* params) const { + Hook_ProcessEvent(object, nullptr, func, params, nullptr); + } + + void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject* obj, void* param) const { + uint8_t index = *frame->Code++; + ((*fframe_step_gnatives)[index])(frame, param); + } + + void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const { + fname_init_ptr(name, str, number, 1, 1); + } + + unreal::UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { + LOG(MISC, "LoadPackage"); + return load_package_ptr(nullptr, name.data(), flags); + } + + unreal::UObject* BL1Hook::find_object(unreal::UClass* cls, const std::wstring& name) const { + return static_find_object_ptr(cls, nullptr, name.c_str(), 0 /* false */); + } + + UObject* BL1Hook::construct_object( + UClass* cls, + UObject* outer, + const FName& name, + decltype(UObject::ObjectFlags) flags, + UObject* template_obj + ) const { + LOG(MISC, "ConstructObject"); + return construct_obj_ptr( + cls, + outer, + name, + flags, + template_obj, + nullptr, + nullptr, + 0 /* false */ + ); + } + + const unreal::GNames& BL1Hook::gnames(void) const { + return gnames_wrapper; + } + + const unreal::GObjects& BL1Hook::gobjects(void) const { + return gobjects_wrapper; + } + + void* BL1Hook::u_malloc(size_t len) const { + void* ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); + memset(ret, 0, len); + return ret; + } + + void* BL1Hook::u_realloc(void* original, size_t len) const { + return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); + } + + void BL1Hook::u_free(void* data) const { + gmalloc->vftable->u_free(gmalloc, data); + } + + namespace { + // Match initial jump offset + const constinit + Pattern<28> SET_COMMAND_SIG{ + "74 1A" // 74 1A | je borderlands.5E5505 + "8B5424 34" // 8B5424 34 | mov edx,dword ptr ss:[esp+34] + "53" // 53 | push ebx + "52" // 52 | push edx + "8BFE" // 8BFE | mov edi,esi + "E8 ????????" // E8 68F3FFFF | call borderlands.5E4860 + "83C4 08" // 83C4 08 | add esp,8 + "B8 01000000" // B8 01000000 | mov eax,1 + "E9 ????????" // E9 66110000 | jmp borderlands.5E666B + }; + } + + void BL1Hook::hexedit_set_command(void) { + + // TODO: Not even sure what this is and I really didn't put much effort into finding out. + // Regardless the usage here is one that gets called on console command submission. + uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); + + if (set_command_msg == nullptr) { + LOG(ERROR, "Failed to find set command message signature."); + return; + } + + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(set_command_msg, 1); + set_command_msg[0] = 0xEB; + // NOLINTEND(readability-magic-numbers) + + } + + namespace { + + // Just like in BL2 the jump offset is preserved for the initial jump since otherwise we would + // jump into arbitrary space. + const constinit + Pattern<29> ARRAY_LIMIT_MESSAGE{ + "0F8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 + "8B4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // 83C7 9D | add edi,FFFFFF9D + "57" // 57 | push edi + "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C + "E8 ????????" // E8 A83CF3FF | call borderlands.51BAA0 + "E9 ????????" // E9 67000000 | jmp borderlands.5E7E64 + }; + } + + void BL1Hook::hexedit_array_limit_message(void) const { + + // TODO: Verify that this works as expected. + uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); + + if (array_limit_msg == nullptr) { + LOG(ERROR, "Failed to find array limit message signature."); + return; + } + + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(array_limit_msg, 6); + array_limit_msg[0] = 0xE9; + array_limit_msg[1] = 0x7F; + array_limit_msg[2] = 0x00; + array_limit_msg[3] = 0x00; + array_limit_msg[4] = 0x00; + array_limit_msg[5] = 0x90; + // NOLINTEND(readability-magic-numbers) + + } + + namespace { + const constinit + Pattern<19> ARRAY_LIMIT_SIG{ + "0F 8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 + "8b4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // 83C7 9D | add edi,FFFFFF9D + "57" // 57 | push edi + "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C + }; + } + + void BL1Hook::hexedit_array_limit(void) { + auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); + if (array_limit == nullptr) { + LOG(ERROR, "Couldn't find array limit signature"); + } else { + LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); + + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(array_limit, 1); + array_limit[0] = 0xEB; + // NOLINTEND(readability-magic-numbers) + } + } + + //############################################################################// + // | CONSOLE STUFF | + //############################################################################// + + namespace { + + bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { + LOG(MISC, "[Hook_RemoveSayFromConsoleCommand] ~ Executing"); + + static const auto console_command_func = in.obj->Class + ->find_func_and_validate(L"ConsoleCommand"_fn); + + static const auto command_property = in.args->type + ->find_prop_and_validate(L"Command"_fn); + + auto param = in.args->get(command_property); + auto func = in.obj->get(console_command_func); + func.call(param); + + return true; + + } + + BoundFunction s_ConsoleOutputText{}; + + bool Hook_InjectConsole(hook_manager::Details& hook) { + LOG(MISC, "[Hook_InjectConsole] ~ Executing"); + hook_manager::remove_hook( + L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, + L"Hook_InjectConsole" + ); + + auto console = hook.obj->get(L"ViewportConsole"_fn); + LOG(MISC, "[Hook_InjectConsole] ~ {:p}, '{}'", (void*) console, console->get_path_name()); + s_ConsoleOutputText = console->get(L"OutputText"_fn); + + return false; + } + + bool Hook_ConsoleCommandImpl(hook_manager::Details& hook) { + LOG(MISC, "[Hook_ConsoleCommandImpl] ~ Executing"); + + const auto command_property = + hook.args->type->find_prop_and_validate(L"Command"_fn); + + const auto history_prop = + hook.obj->Class->find_prop_and_validate(L"History"_fn); + const auto history_top_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); + const auto history_bot_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); + const auto history_cur_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); + + const UFunction* purge_command_func = + hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + const UFunction* save_config_func = + hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); + + auto line = hook.args->get(command_property); + + auto [callback, cmd_len] = commands::impl::find_matching_command(line); + if (callback == nullptr) { + return false; + } + + // Add to the history buffer + { + // History is a ring buffer of recent commands + // HistoryBot points to the oldest entry, or -1 if history is empty. + // HistoryTop points to the next entry to fill. If history is empty it's 0. The value it + // points at is *not* shown in the history if we've wrapped. + // HistoryCur is always set to the same as top after running a command - presumably it + // changes while scrolling, but we can't really check that + + // First remove it from history + hook.obj->get(purge_command_func).call(line); + + // Insert this line at top + auto history_top = hook.obj->get(history_top_prop); + hook.obj->set(history_prop, history_top, line); + + // Increment top + history_top = (history_top + 1) % history_prop->ArrayDim; + hook.obj->set(history_top_prop, history_top); + // And set current + hook.obj->set(history_cur_prop, history_top); + + // Increment bottom if needed + auto history_bot = hook.obj->get(history_bot_prop); + if ((history_bot == -1) || history_bot == history_top) { + hook.obj->set( + history_bot_prop, + (history_bot + 1) % history_prop->ArrayDim); + } + + hook.obj->get(save_config_func).call(); + } + + LOG(INFO, L">>> {} <<<", line); + + try { + callback->operator ()(line.c_str(), line.size(), cmd_len); + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); + } + + return true; + } + } + + bool BL1Hook::is_console_ready(void) const { + return s_ConsoleOutputText.func != nullptr; + } + + void BL1Hook::uconsole_output_text(const std::wstring& str) const { + if (!is_console_ready()) return; + s_ConsoleOutputText.call(str); + } + + void BL1Hook::inject_console(void) { + + LOG(MISC, "Hooking Engine.Console:ShippingConsoleCommand"); + hook_manager::add_hook( + L"Engine.Console:ShippingConsoleCommand", + unrealsdk::hook_manager::Type::PRE, + L"Hook_RemoveSayFromConsoleCommand", + &Hook_RemoveSayFromConsoleCommand + ); + + LOG(MISC, "Hooking Engine.Console:ConsoleCommand"); + hook_manager::add_hook( + L"Engine.Console:ConsoleCommand", + hook_manager::Type::PRE, + L"Hook_ConsoleCommandImpl", + &Hook_ConsoleCommandImpl + ); + + LOG(MISC, "Hooking WillowGame.WillowGameViewportClient:PostRender"); + hook_manager::add_hook( + L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, + L"Hook_InjectConsole", + &Hook_InjectConsole + ); + } + +} // namespace + diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h new file mode 100644 index 0000000..43c897a --- /dev/null +++ b/src/unrealsdk/game/bl1/bl1.h @@ -0,0 +1,144 @@ +#ifndef UNREALSDK_BL1_H +#define UNREALSDK_BL1_H + +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/abstract_hook.h" +#include "unrealsdk/game/selector.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game { + + class BL1Hook : public AbstractHook { + protected: + /** + * @brief Hex edits out the `obj dump` array limit message. + */ + virtual void hexedit_array_limit_message(void) const; + + /** + * @brief Finds `FName::Init`, and sets up such that `fname_init` may be called. + */ + void find_fname_init(void); + + /** + * @brief Hooks the antidebug functions and disables them. + */ + static void hook_antidebug(void); + + /** + * @brief Hex edits out the protection on the set command. + */ + static void hexedit_set_command(void); + + /** + * @brief Hex edits out the `obj dump` array limit. + */ + static void hexedit_array_limit(void); + + /** + * @brief Hooks `UObject::ProcessEvent` and points it at the hook manager. + */ + static void hook_process_event(void); + + /** + * @brief Hooks `UObject::CallFunction` and points it at the hook manager. + */ + static void hook_call_function(void); + + /** + * @brief Finds GObjects, and populates the wrapper member. + */ + static void find_gobjects(void); + + /** + * @brief Finds GNames, and sets up such that `gnames` may be called. + */ + static void find_gnames(void); + + /** + * @brief Finds `FFrame::Step`, and sets up such that `fframe_step` may be called. + */ + static void find_fframe_step(void); + + /** + * @brief Finds `GMalloc`, and sets up such that `malloc`, `realloc`, and `free` may be called. + */ + static void find_gmalloc(void); + + /** + * @brief Finds `StaticConstructObject`, and sets up such that `construct_object` may be called. + */ + static void find_construct_object(void); + + /** + * @brief Finds `UObject::GetPathName`, and sets up such that `uobject_path_name` may be called. + */ + static void find_get_path_name(void); + + /** + * @brief Finds `StaticFindObject`, and sets up such that `find_object` may be called. + */ + static void find_static_find_object(void); + + /** + * @brief Finds `LoadPackage`, and sets up such that `load_package` may be called. + */ + static void find_load_package(void); + + /** + * @brief Creates a console and sets the bind (if required), and hooks logging onto it. + */ + static void inject_console(void); + + public: + void hook(void) override; + void post_init(void) override; + + [[nodiscard]] bool is_console_ready(void) const override; + + [[nodiscard]] const unreal::GObjects& gobjects(void) const override; + [[nodiscard]] const unreal::GNames& gnames(void) const override; + [[nodiscard]] void* u_malloc(size_t len) const override; + [[nodiscard]] void* u_realloc(void* original, size_t len) const override; + void u_free(void* data) const override; + [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, + unreal::UObject* outer, + const unreal::FName& name, + decltype(unreal::UObject::ObjectFlags) flags, + unreal::UObject* template_obj) const override; + [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, + const std::wstring& name) const override; + [[nodiscard]] unreal::UObject* load_package(const std::wstring& name, + uint32_t flags) const override; + + void fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const override; + void fframe_step(unreal::FFrame* frame, unreal::UObject* obj, void* param) const override; + void process_event(unreal::UObject* object, + unreal::UFunction* func, + void* params) const override; + void uconsole_output_text(const std::wstring& str) const override; + [[nodiscard]] std::wstring uobject_path_name(const unreal::UObject* obj) const override; + void ftext_as_culture_invariant(unreal::FText* text, + unreal::TemporaryFString&& str) const override; + void fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, + const unreal::UObject* obj) const override; + void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, + const unreal::UObject* obj) const override; + }; + + template<> + struct GameTraits { + static constexpr auto NAME = "Borderlands"; + + static bool matches_executable(std::string_view executable) { + return executable == "Borderlands.exe" || executable == "borderlands.exe"; + } + }; + +} // namespace unrealsdk::game + +#endif + +#endif From 940f9a6fe1162d2a653dc55d0eb78626f03752ad Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:07:27 +0100 Subject: [PATCH 04/81] rename include guard to match bl2 --- src/unrealsdk/game/bl1/bl1.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 43c897a..7f44fc7 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -1,5 +1,5 @@ -#ifndef UNREALSDK_BL1_H -#define UNREALSDK_BL1_H +#ifndef UNREALSDK_GAME_BL1_BL1_H +#define UNREALSDK_GAME_BL1_BL1_H #include "unrealsdk/pch.h" From 3f7a40309923d901764a58677e67b0d947cd21d0 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:27:47 +0100 Subject: [PATCH 05/81] Updating CMake & CMakePresets for BL1 standalone. --- CMakeLists.txt | 8 ++++++++ CMakePresets.json | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd1d641..90c5c4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ set(UNREALSDK_UE_VERSION "UE4" CACHE STRING "The unreal engine version to build set(UNREALSDK_ARCH "x64" CACHE STRING "The architecture to build the sdk for. One of 'x86' or 'x64'.") set(UNREALSDK_SHARED False CACHE BOOL "If set, compiles as a shared library instead of as an object.") +# NOTE: Temporary +set(UNREALSDK_GAME "NONE" CACHE STRING "Game to build for if any") + add_library(_unrealsdk_interface INTERFACE) target_compile_features(_unrealsdk_interface INTERFACE cxx_std_20) @@ -14,6 +17,11 @@ set_target_properties(_unrealsdk_interface PROPERTIES INTERPROCEDURAL_OPTIMIZATION True ) +# NOTE: This is temporary +if (UNREALSDK_GAME STREQUAL "BL1") + target_compile_definitions(_unrealsdk_interface INTERFACE -DUNREALSDK_GAME_BL1=1) +endif () + if(MSVC) # Under MSVC, enable edit and continue in debug - which conflicts with LTO set_target_properties(${target_name} PROPERTIES diff --git a/CMakePresets.json b/CMakePresets.json index 17affae..1b30589 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -372,6 +372,34 @@ "_x64", "_release" ] + }, + { + "name": "msvc-bl1-x86-debug", + "displayName": "BL1 x86 Debug (MSVC)", + "inherits": [ + "_base", + "_msvc", + "_ue3", + "_x86", + "_debug" + ], + "cacheVariables": { + "UNREALSDK_GAME": "BL1" + } + }, + { + "name": "msvc-bl1-x86-release", + "displayName": "BL1 x86 Release (MSVC)", + "inherits": [ + "_base", + "_msvc", + "_ue3", + "_x86", + "_release" + ], + "cacheVariables": { + "UNREALSDK_GAME": "BL1" + } } ] } From 921e2cc6c1aeaf9d78398fb17b553a0f6996fbc6 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:28:06 +0100 Subject: [PATCH 06/81] Additional guard for BL1 --- src/unrealsdk/game/bl1/bl1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 7f44fc7..b8011c3 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -6,7 +6,7 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) namespace unrealsdk::game { From 719f44d4afc03a3d076977067321e500ab0283e8 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:49:08 +0100 Subject: [PATCH 07/81] Offsets for UField, UProperty, UStruct, and UClass; Potentially invalid offsets. --- src/unrealsdk/unreal/classes/uclass.h | 15 +++++++++++++++ src/unrealsdk/unreal/classes/ufield.h | 10 ++++++++++ src/unrealsdk/unreal/classes/uproperty.h | 14 ++++++++++++++ src/unrealsdk/unreal/classes/ustruct.h | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index b16da99..6d4eac8 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -43,6 +43,9 @@ class UClass : public UStruct { public: TArray Interfaces; #else + +#if !defined(UNREALSDK_GAME_BL1) + private: // Misc Fields I found within this block in BL2, but which we don't care about enough for me to // find in UE4, or to want to increase the compile times by including @@ -62,6 +65,18 @@ class UClass : public UStruct { public: TArray Interfaces; + +#else // defined(UNREALSDK_GAME_BL1) + + public: + uint8_t UnknownData00[0xC0]; + UObject* ClassDefaultObject; // 340b + uint8_t UnknownData01[0x54]; + TArray Interfaces; // 428b + + +#endif + #endif // NOLINTEND(readability-magic-numbers, readability-identifier-naming) diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index 11a20d8..c40f746 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -19,8 +19,18 @@ class UField : public UObject { UField& operator=(UField&&) = delete; ~UField() = delete; +#if !defined(UNREALSDK_GAME_BL1) + // NOLINTNEXTLINE(readability-identifier-naming) UField* Next; + +#else // defined(UNREALSDK_GAME_BL1) + + public: + UStruct* SuperField; + UField* Next; + +#endif }; template <> diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index b4dc6b9..88c6c40 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -83,6 +83,7 @@ class UProperty : public UField { UProperty* PostConstructLinkNext; // 0x0030(0x0040) MISSED OFFSET #else +#if !defined(UNREALSDK_GAME_BL1) int32_t ArrayDim; int32_t ElementSize; uint32_t PropertyFlags; @@ -97,6 +98,19 @@ class UProperty : public UField { private: uint8_t UnknownData01[0x18]; +#else // defined(UNREALSDK_GAME_BL1) + + public: // Total Size: 140b + int32_t ArrayDim; // 68b + int32_t ElementSize; // 72b + uint32_t PropertyFlags; // 76b + uint8_t UnknownData00[0x14]; + int32_t Offset_Internal; // 100b + UProperty* PropertyLinkNext; // 104b + uint8_t UnknownData01[0x20]; + +#endif + /** * @brief Gets the size of this class. * diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index 5a44a2b..e45f567 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -62,6 +62,9 @@ class UStruct : public UField { * garbage collection code */ TArray ScriptObjectReferences; #else + +#if !defined(UNREALSDK_GAME_BL1) + private: uint8_t UnknownData00[0x8]; @@ -80,6 +83,21 @@ class UStruct : public UField { uint8_t UnknownData02[0x10]; TArray ScriptObjectReferences; + +#else // defined(UNREALSDK_GAME_BL1) + + public: + uint8_t UnknownData00[0x08]; + UField* Children; // 76b + uint16_t PropertySize; // 80b + uint8_t UnknownData01[0x1C + 0x02]; // +2 explicit padding + UProperty* PropertyLink; // 112b + uint8_t UnknownData02[0x10]; + TArray ScriptObjectReferences; // 132b + uint8_t UnknownData03[0x04]; + +#endif + #endif // NOLINTEND(readability-magic-numbers, readability-identifier-naming) From b3316eb4348c930032278d8a5ce13e2cfd25c094 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:06:40 +0100 Subject: [PATCH 08/81] Reformat, Comments, and removal of unused function variable names. --- src/unrealsdk/game/bl1/bl1.cpp | 1470 +++++++++++++++----------------- 1 file changed, 710 insertions(+), 760 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index d98e797..ac3d0f7 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -2,46 +2,55 @@ #include "unrealsdk/game/bl1/bl1.h" -#include -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include -#include "unrealsdk/memory.h" #include "unrealsdk/logging.h" +#include "unrealsdk/memory.h" #include "unrealsdk/unreal/alignment.h" #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/unreal/wrappers/gnames.h" -#include "unrealsdk/unreal/prop_traits.h" -#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/properties/copyable_property.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/prop_traits.h" using namespace unrealsdk::memory; using namespace unrealsdk::unreal; -//############################################################################// -// | GMALLOC | -//############################################################################// +// - NOTE - +// Currently everything is in a single file mostly for testing purposes. The patterns that have +// been found are not minimal and may contain jump offsets. Additionally, the offsets are for the +// BL1 1.4.1 UDK version of the game. I did a quick scan for the ProccessEvent pattern in the Steam +// version and it did find the correct function however I didn't check anymore than that; other +// than to see if FFrame::Step was also inlined, it is. +// namespace { - struct FMalloc; - struct FMallocVFtable { - void* exec; - void* (__thiscall* u_malloc)(FMalloc* self, uint32_t len, uint32_t align); - void* (__thiscall* u_realloc)(FMalloc* self, void* original, uint32_t len, uint32_t align); - void* (__thiscall* u_free)(FMalloc* self, void* data); - }; +// ############################################################################// +// | GMALLOC | +// ############################################################################// + +struct FMalloc; - struct FMalloc { - FMallocVFtable* vftable; - }; +struct FMallocVFtable { + void* exec; + void*(__thiscall* u_malloc)(FMalloc* self, uint32_t len, uint32_t align); + void*(__thiscall* u_realloc)(FMalloc* self, void* original, uint32_t len, uint32_t align); + void*(__thiscall* u_free)(FMalloc* self, void* data); +}; - FMalloc* gmalloc; +struct FMalloc { + FMallocVFtable* vftable; +}; + +FMalloc* gmalloc; // 8B0D ???????? 8B01 8B50 04 // 00445911 | 8B0D B43BF701 | mov ecx,dword ptr ds:[1F73BB4] @@ -50,7 +59,6 @@ namespace { // 0044591C | 6A 08 | push 8 // 0044591E | 68 50010000 | push 150 // 00445923 | FFD2 | call edx -// First address is constant // 1F73BB4 -> 069A77B0 -> 01CC3AD8 -> [007B6EB0 013A8F50 013A8F70 013A8FC0] // in vtable[1] we get // 013A8F50 | 8B4424 04 | mov eax,dword ptr ss:[esp+4] @@ -63,37 +71,32 @@ namespace { // 8B 0D ?? ?? ?? ?? 8B 01 8B 50 04 // TODO: This pattern is not unique to GMalloc - const constinit - Pattern<11> GMALLOC_PATTERN{ - "8B 0D {????????}" - "8B 01" - "8B 50 04" - }; - - GObjects gobjects_wrapper{}; - - const constinit - Pattern<15> GOBJECTS_SIG{ - "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] - "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] - "8B50 0C" // mov edx,dword ptr ds:[eax+C] - "2158 08" // and dword ptr ds:[eax+8],ebx - }; - - GNames gnames_wrapper{}; - - const constinit - Pattern<15> GNAMES_SIG{ - "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] - "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] - "68 00100000" // push 1000 - "6A 00" // push 0 - }; - - - //############################################################################// - // | LOAD PACKAGE | - //############################################################################// +const constinit Pattern<11> GMALLOC_PATTERN{ + "8B 0D {????????}" + "8B 01" + "8B 50 04"}; + +GObjects gobjects_wrapper{}; + +const constinit Pattern<15> GOBJECTS_SIG{ + "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] + "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] + "8B50 0C" // mov edx,dword ptr ds:[eax+C] + "2158 08" // and dword ptr ds:[eax+8],ebx +}; + +GNames gnames_wrapper{}; + +const constinit Pattern<15> GNAMES_SIG{ + "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] + "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] + "68 00100000" // push 1000 + "6A 00" // push 0 +}; + +// ############################################################################// +// | LOAD PACKAGE | +// ############################################################################// // // Candidate Function: 005e1770 @@ -111,831 +114,778 @@ namespace { // 005E178C | 33C5 | xor eax,ebp // 005E178E | 50 | push eax - using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); - load_package_func load_package_ptr; +using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); +load_package_func load_package_ptr; // Pattern as a single string for easy lookups -// 55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 83 EC 28 53 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D 45 F4 - - const constinit - Pattern<34> LOAD_PACKAGE_PATTERN{ - "55" // push ebp - "8B EC" // mov ebp,esp - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18ECDB0 - "64 A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 28" // sub esp,28 - "53 56 57" // push ebx - "A1 ????????" // push esi - "33C5" // push edi - "50" // mov eax,dword ptr ds:[1F16980] - "8D45 F4" // xor eax,ebp - }; - - - //############################################################################// - // | FNAME INIT | - //############################################################################// +// 55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 83 EC 28 53 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D +// 45 F4 + +const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ + "55" // push ebp + "8B EC" // mov ebp,esp + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18ECDB0 + "64 A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 28" // sub esp,28 + "53 56 57" // push ebx + "A1 ????????" // push esi + "33C5" // push edi + "50" // mov eax,dword ptr ds:[1F16980] + "8D45 F4" // xor eax,ebp +}; + +// ############################################################################// +// | FNAME INIT | +// ############################################################################// // Candidate function: 005cef40 // 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81EC 98 0C 00 00 A1 ?? ?? ?? ?? 33 C4 898424 940C0000 - const constinit - Pattern<34> FNAME_INIT_SIG{ - "6A FF" // push FFFFFFFF - "68 ?? ?? ?? ??" // push borderlands.18EB45B - "64 A1 00 00 00 00" // mov eax,dword ptr fs:[0] - "50" // push eax - "81EC 98 0C 00 00" // sub esp,C98 - "A1 ?? ?? ?? ??" // mov eax,dword ptr ds:[1F16980] - "33 C4" // xor eax,esp - "898424 940C0000" // mov dword ptr ss:[esp+C94],eax - }; - - typedef void(__thiscall* fname_init_func)( - FName* name, - const wchar_t* str, - int32_t number, - int32_t find_type, - int32_t split_name - ); - - fname_init_func fname_init_ptr = nullptr; - - // Usage of this function is inlined so we have to manually grab the pointer. - const constinit - Pattern<36> FFRAME_STEP_SIG{ - "74 ??" // 74 22 | je borderlands.59CEA4 - "8B45 D4" // 8B45 D4 | mov eax,dword ptr ss:[ebp-2C] - "830D ???????? 02" // 830D E83BF701 02 | or dword ptr ds:[1F73BE8],2 - "0FB610" // 0FB610 | movzx edx,byte ptr ds:[eax] - "8B1495 {????????}" // 8B1495 807AF901 | mov edx,dword ptr ds:[edx*4+1F97A80] - "57" // 57 | push edi - "8D4D BC" // 8D4D BC | lea ecx,dword ptr ss:[ebp-44] - "40" // 40 | inc eax - "51" // 51 | push ecx - "8B4D EC" // 8B4D EC | mov ecx,dword ptr ss:[ebp-14] - "8945 D4" // 8945 D4 | mov dword ptr ss:[ebp-2C],eax - "FFD2" // FFD2 | call edx - }; - - // RET 0x8; Callee cleans up the stack (8 bytes) - typedef void(__stdcall* fframe_step_func)(FFrame*, void*); - fframe_step_func** fframe_step_gnatives; +const constinit Pattern<34> FNAME_INIT_SIG{ + "6A FF" // push FFFFFFFF + "68 ?? ?? ?? ??" // push borderlands.18EB45B + "64 A1 00 00 00 00" // mov eax,dword ptr fs:[0] + "50" // push eax + "81EC 98 0C 00 00" // sub esp,C98 + "A1 ?? ?? ?? ??" // mov eax,dword ptr ds:[1F16980] + "33 C4" // xor eax,esp + "898424 940C0000" // mov dword ptr ss:[esp+C94],eax +}; + +typedef void(__thiscall* fname_init_func)(FName* name, + const wchar_t* str, + int32_t number, + int32_t find_type, + int32_t split_name); + +fname_init_func fname_init_ptr = nullptr; + +// ############################################################################// +// | FFRAME STEP | +// ############################################################################// + +// - NOTE - +// This is inlined so we have to manually re-implement FFrame::Step using GNatives. +const constinit Pattern<36> FFRAME_STEP_SIG{ + "74 ??" // 74 22 | je borderlands.59CEA4 + "8B45 D4" // 8B45 D4 | mov eax,dword ptr ss:[ebp-2C] + "830D ???????? 02" // 830D E83BF701 02 | or dword ptr ds:[1F73BE8],2 + "0FB610" // 0FB610 | movzx edx,byte ptr ds:[eax] + "8B1495 {????????}" // 8B1495 807AF901 | mov edx,dword ptr ds:[edx*4+1F97A80] + "57" // 57 | push edi + "8D4D BC" // 8D4D BC | lea ecx,dword ptr ss:[ebp-44] + "40" // 40 | inc eax + "51" // 51 | push ecx + "8B4D EC" // 8B4D EC | mov ecx,dword ptr ss:[ebp-14] + "8945 D4" // 8945 D4 | mov dword ptr ss:[ebp-2C],eax + "FFD2" // FFD2 | call edx +}; + +// RET 0x8; Callee cleans up the stack (8 bytes) +typedef void(__stdcall* fframe_step_func)(FFrame*, void*); +fframe_step_func** fframe_step_gnatives; + +// ############################################################################// +// | GET PATH NAME | +// ############################################################################// // This one was basically identical to BL2. // pattern: 8B4424 04 56 8BF1 3BF0 7476 85F6 7472 8B4E 28 53 - typedef void(__thiscall* get_path_name_func)(const UObject* self, const UObject* stop_outer, ManagedFString* str); - get_path_name_func get_path_name_ptr; - const constinit - Pattern<19> GET_PATH_NAME_PATTERN{ - "8B4424 04" // mov eax,dword ptr ss:[esp+4] - "56" // push esi - "8BF1" // mov esi,ecx - "3BF0" // cmp esi,eax - "74 76" // je borderlands.5CF501 - "85F6" // test esi,esi - "74 72" // je borderlands.5CF501 - "8B4E 28" // mov ecx,dword ptr ds:[esi+28] - "53" // push ebx - }; - - typedef UObject* (__cdecl* construct_obj_func)( - UClass* cls, - UObject* outer, - FName name, - uint64_t flags, - UObject* template_obj, - void* error_output_device, - void* instance_graph, - uint32_t assume_template_is_archetype - ); - construct_obj_func construct_obj_ptr; - -// Also similar to bl2: 6AFF68????????64A1000000005083EC0C53555657A1????????33C4508D44242064A3000000008B6C2454896C241485ED - const constinit - Pattern<49> CONSTRUCT_OBJECT_PATTERN{ - "6A FF" // 6A FF | push FFFFFFFF - "68 ????????" // 68 93D68E01 | push borderlands.18ED693 - "64A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83EC 0C" // 83EC 0C | sub esp,C - "53" // 53 | push ebx - "55" // 55 | push ebp - "56" // 56 | push esi - "57" // 57 | push edi - "A1 ????????" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] - "33C4" // 33C4 | xor eax,esp - "50" // 50 | push eax - "8D4424 20" // 8D4424 20 | lea eax,dword ptr ss:[esp+20] - "64 A3 00000000" // 64:A3 00000000 | mov dword ptr fs:[0],eax - "8B6C24 54" // 8B6C24 54 | mov ebp,dword ptr ss:[esp+54] - "896C24 14" // 896C24 14 | mov dword ptr ss:[esp+14],ebp - "85ED" // 85ED | test ebp,ebp - }; - - - // NOLINTNEXTLINE(modernize-use-using) - typedef UObject* (__cdecl* static_find_object_func)( - const UClass* cls, - const UObject* package, - const wchar_t* str, - uint32_t exact_class - ); +typedef void(__thiscall* get_path_name_func)(const UObject* self, + const UObject* stop_outer, + ManagedFString* str); +get_path_name_func get_path_name_ptr; +const constinit Pattern<19> GET_PATH_NAME_PATTERN{ + "8B4424 04" // mov eax,dword ptr ss:[esp+4] + "56" // push esi + "8BF1" // mov esi,ecx + "3BF0" // cmp esi,eax + "74 76" // je borderlands.5CF501 + "85F6" // test esi,esi + "74 72" // je borderlands.5CF501 + "8B4E 28" // mov ecx,dword ptr ds:[esi+28] + "53" // push ebx +}; + +// ############################################################################// +// | CONSTRUCT OBJECT | +// ############################################################################// + +typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, + UObject* outer, + FName name, + uint64_t flags, + UObject* template_obj, + void* error_output_device, + void* instance_graph, + uint32_t assume_template_is_archetype); +construct_obj_func construct_obj_ptr; + +const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ + "6A FF" // 6A FF | push FFFFFFFF + "68 ????????" // 68 93D68E01 | push borderlands.18ED693 + "64A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83EC 0C" // 83EC 0C | sub esp,C + "53" // 53 | push ebx + "55" // 55 | push ebp + "56" // 56 | push esi + "57" // 57 | push edi + "A1 ????????" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] + "33C4" // 33C4 | xor eax,esp + "50" // 50 | push eax + "8D4424 20" // 8D4424 20 | lea eax,dword ptr ss:[esp+20] + "64 A3 00000000" // 64:A3 00000000 | mov dword ptr fs:[0],eax + "8B6C24 54" // 8B6C24 54 | mov ebp,dword ptr ss:[esp+54] + "896C24 14" // 896C24 14 | mov dword ptr ss:[esp+14],ebp + "85ED" // 85ED | test ebp,ebp +}; + +// ############################################################################// +// | STATIC FIND OBJECT | +// ############################################################################// + +// NOLINTNEXTLINE(modernize-use-using) +typedef UObject*(__cdecl* static_find_object_func)(const UClass* cls, + const UObject* package, + const wchar_t* str, + uint32_t exact_class); + +static_find_object_func static_find_object_ptr; +const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ + "6A FF" // 6A FF | push FFFFFFFF + "68 ?? ?? ?? ??" // 68 90C18E01 | push borderlands.18EC190 + "64 A1 00 00 00 00" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83 EC 24" // 83EC 24 | sub esp,24 + "53" // 53 | push ebx + "55" // 55 | push ebp + "56" // 56 | push esi + "57" // 57 | push edi + "A1 ?? ?? ?? ??" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] + "33 C4" // 33C4 | xor eax,esp + "50" // 50 | push eax + "8D 44 24 38" // 8D4424 38 | lea eax,dword ptr ss:[esp+38] + "64 A3 00 00 00 00" // 64:A3 00000000 | mov dword ptr fs:[0],eax + "8B 74 24 4C" // 8B7424 4C | mov esi,dword ptr ss:[esp+4C] + "8B 7C 24 50" // 8B7C24 50 | mov edi,dword ptr ss:[esp+50] + "8B C6" // 8BC6 | mov eax,esi + "40" // 40 | inc eax +}; + +// ############################################################################// +// | PROCESS EVENT | +// ############################################################################// + +typedef void(__fastcall* process_event_func)(UObject* obj, + void* /*edx*/, + UFunction* func, + void* params, + void* /*null*/); +process_event_func process_event_ptr; + +const constinit Pattern<43> PROCESS_EVENT_SIG{ + "55" + "8B EC" + "6A FF" + "68 ?? ?? ?? ??" + "64 A1 00 00 00 00" + "50" + "83 EC 40" + "A1 ?? ?? ?? ??" + "33 C5" + "89 45 F0" + "53" + "56" + "57" + "50" + "8D 45 F4" + "64 A3 00 00 00 00"}; + +// ############################################################################// +// | CALL FUNCTION | +// ############################################################################// + +typedef void(__fastcall* call_function_func)(UObject* obj, + void* /*edx*/, + FFrame* stack, + void* params, + UFunction* func); +call_function_func call_function_ptr; + +const constinit Pattern<31> CALL_FUNCTION_SIG{ + "55" // 55 | push ebp + "8DAC24 FCFBFFFF" // 8DAC24 FCFBFFFF | lea ebp,dword ptr ss:[esp-404] + "81EC 04040000" // 81EC 04040000 | sub esp,404 + "6A FF" // 6A FF | push FFFFFFFF + "68 ????????" // 68 38968E01 | push borderlands.18E9638 + "64 A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] + "50" // 50 | push eax + "83EC 40" // 83EC 40 | sub esp,40 +}; - static_find_object_func static_find_object_ptr; - const constinit - Pattern<50> STATIC_FIND_OBJECT_PATTERN{ - "6A FF" // 6A FF | push FFFFFFFF - "68 ?? ?? ?? ??" // 68 90C18E01 | push borderlands.18EC190 - "64 A1 00 00 00 00" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83 EC 24" // 83EC 24 | sub esp,24 - "53" // 53 | push ebx - "55" // 55 | push ebp - "56" // 56 | push esi - "57" // 57 | push edi - "A1 ?? ?? ?? ??" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] - "33 C4" // 33C4 | xor eax,esp - "50" // 50 | push eax - "8D 44 24 38" // 8D4424 38 | lea eax,dword ptr ss:[esp+38] - "64 A3 00 00 00 00" // 64:A3 00000000 | mov dword ptr fs:[0],eax - "8B 74 24 4C" // 8B7424 4C | mov esi,dword ptr ss:[esp+4C] - "8B 7C 24 50" // 8B7C24 50 | mov edi,dword ptr ss:[esp+50] - "8B C6" // 8BC6 | mov eax,esi - "40" // 40 | inc eax - }; - - - typedef void(__fastcall* process_event_func)( - UObject* obj, - void* /*edx*/, - UFunction* func, - void* params, - void* /*null*/); - process_event_func process_event_ptr; - - const constinit - Pattern<43> PROCESS_EVENT_SIG{ - "55" - "8B EC" - "6A FF" - "68 ?? ?? ?? ??" - "64 A1 00 00 00 00" - "50" - "83 EC 40" - "A1 ?? ?? ?? ??" - "33 C5" - "89 45 F0" - "53" - "56" - "57" - "50" - "8D 45 F4" - "64 A3 00 00 00 00" - }; - - - typedef void(__fastcall* call_function_func)( - UObject* obj, - void* /*edx*/, - FFrame* stack, - void* params, - UFunction* func - ); - call_function_func call_function_ptr; - - const constinit - Pattern<31> CALL_FUNCTION_SIG{ - "55" // 55 | push ebp - "8DAC24 FCFBFFFF" // 8DAC24 FCFBFFFF | lea ebp,dword ptr ss:[esp-404] - "81EC 04040000" // 81EC 04040000 | sub esp,404 - "6A FF" // 6A FF | push FFFFFFFF - "68 ????????" // 68 38968E01 | push borderlands.18E9638 - "64 A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83EC 40" // 83EC 40 | sub esp,40 - }; - -} +} // namespace -//############################################################################// -// | BL1Hook | -//############################################################################// +// ############################################################################// +// | BL1Hook | +// ############################################################################// namespace unrealsdk::game { - void BL1Hook::hook(void) { - hook_antidebug(); - - hook_process_event(); - hook_call_function(); +void BL1Hook::hook(void) { + hook_antidebug(); + + hook_process_event(); + hook_call_function(); + + find_gobjects(); + find_gnames(); + find_fname_init(); + find_fframe_step(); + find_gmalloc(); + find_construct_object(); + find_get_path_name(); + find_static_find_object(); + find_load_package(); + + // I did a minor search for these for now disabled as they don't seem important rn + // hexedit_set_command(); + // hexedit_array_limit(); + // hexedit_array_limit_message(); +} - find_gobjects(); - find_gnames(); - find_fname_init(); - find_fframe_step(); - find_gmalloc(); - find_construct_object(); - find_get_path_name(); - find_static_find_object(); - find_load_package(); +namespace { - // hexedit_set_command(); - // hexedit_array_limit(); - // hexedit_array_limit_message(); +// TODO: These are purely for testing. - } +bool Hook_StartMenuUp(hook_manager::Details&) { + LOG(MISC, "[Hook_StartMenuUp] Invoked!"); + return false; +} - namespace { +bool Hook_StartMenuDown(hook_manager::Details&) { + LOG(MISC, "[Hook_StartMenuDown] Invoked!"); + return false; +} - bool Hook_StartMenuUp(hook_manager::Details& details) { - LOG(MISC, "[Hook_StartMenuUp] Invoked!"); - return false; - } +bool Hook_AlwaysSaveOnQuit(hook_manager::Details& in) { + auto dlg_prop = in.args->type->find_prop_and_validate(L"Dlg"_fn); + auto dlg = in.args->get(dlg_prop); - bool Hook_StartMenuDown(hook_manager::Details& details) { - LOG(MISC, "[Hook_StartMenuDown] Invoked!"); - return false; - } + dlg->get(L"Close"_fn).call(); - bool Hook_AlwaysSaveOnQuit(hook_manager::Details& in) { + in.obj->get(L"QuitToMenu"_fn).call(true); - auto dlg_prop = in.args->type->find_prop_and_validate(L"Dlg"_fn); - auto dlg = in.args->get(dlg_prop); + in.ret.set(true); - dlg->get(L"Close"_fn).call(); + return true; +} - in.obj->get(L"QuitToMenu"_fn) - .call(true); +} // namespace - in.ret.set(true); +void BL1Hook::post_init(void) { + LOG(MISC, "Attaching Hooks!"); + inject_console(); - return true; - } + hook_manager::add_hook(L"WillowGame.WillowGFxMenuScreen:Nav_Up", hook_manager::Type::PRE, + L"Hook_StartMenuUp", &Hook_StartMenuUp); - } + hook_manager::add_hook(L"WillowGame.WillowGFxMenuScreen:Nav_Down", hook_manager::Type::PRE, + L"Hook_StartMenuDown", &Hook_StartMenuDown); - void BL1Hook::post_init(void) { - - LOG(MISC, "Attaching Hooks!"); - inject_console(); + hook_manager::add_hook(L"WillowGame.WillowGFxMenuPause:PromptQuit_Ok", hook_manager::Type::PRE, + L"Hook_AlwaysSaveOnQuit", &Hook_AlwaysSaveOnQuit); +} - hook_manager::add_hook( - L"WillowGame.WillowGFxMenuScreen:Nav_Up", - hook_manager::Type::PRE, - L"Hook_StartMenuUp", - &Hook_StartMenuUp); +// ############################################################################// +// | FIND METHODS | CALLED IN BL1Hook::hook() +// ############################################################################// - hook_manager::add_hook( - L"WillowGame.WillowGFxMenuScreen:Nav_Down", - hook_manager::Type::PRE, - L"Hook_StartMenuDown", - &Hook_StartMenuDown); +void BL1Hook::find_gobjects(void) { + auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); + LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); + gobjects_wrapper = GObjects(gobjects_ptr); +} - hook_manager::add_hook( - L"WillowGame.WillowGFxMenuPause:PromptQuit_Ok", - hook_manager::Type::PRE, - L"Hook_AlwaysSaveOnQuit", - &Hook_AlwaysSaveOnQuit); +void BL1Hook::find_gnames(void) { + auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); + LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); + gnames_wrapper = GNames(gnames_ptr); +} - } +void BL1Hook::find_fname_init(void) { + fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); + LOG(MISC, "FName::Init: {:p}", (void*)fname_init_ptr); +} - //############################################################################// - // | FIND METHODS | CALLED IN BL1Hook::hook() - //############################################################################// +void BL1Hook::find_fframe_step(void) { + fframe_step_gnatives = FFRAME_STEP_SIG.sigscan_nullable(); - void BL1Hook::find_gobjects(void) { - auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); - LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); - gobjects_wrapper = GObjects(gobjects_ptr); + if (fframe_step_gnatives == nullptr) { + LOG(MISC, "FFrame::Step(...), GNatives was null."); + return; } - void BL1Hook::find_gnames(void) { - auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); - LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); - gnames_wrapper = GNames(gnames_ptr); - } + LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); +} - void BL1Hook::find_fname_init(void) { - fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); - LOG(MISC, "FName::Init: {:p}", (void*) fname_init_ptr); - } +void BL1Hook::find_gmalloc(void) { + gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); + LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); +} - void BL1Hook::find_fframe_step(void) { - fframe_step_gnatives = FFRAME_STEP_SIG.sigscan_nullable(); +void BL1Hook::find_construct_object(void) { + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); +} - if (fframe_step_gnatives == nullptr) { - LOG(MISC, "FFrame::Step(...), GNatives was null."); - return; - } +namespace { +void __fastcall Hook_PathName(UObject* obj, void*, UObject* outer, ManagedFString* fstr) { + LOG(MISC, "[Hook_PathName] ~ {:p}, {:p}, {:p}", (void*)obj, (void*)outer, (void*)fstr); + get_path_name_ptr(obj, outer, fstr); +} +} // namespace - LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); - } +void BL1Hook::find_get_path_name(void) { + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan("get_path_name_func"); + LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); +} - void BL1Hook::find_gmalloc(void) { - gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); - LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); - } +void BL1Hook::find_static_find_object(void) { + static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); +} - void BL1Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); - LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); - } +void BL1Hook::find_load_package(void) { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); +} - namespace { - void __fastcall Hook_PathName(UObject* obj, void*, UObject* outer, ManagedFString* fstr) { - LOG(MISC, "[Hook_PathName] ~ {:p}, {:p}, {:p}", (void*) obj, (void*) outer, (void*) fstr); - get_path_name_ptr(obj, outer, fstr); +// ############################################################################// +// | PROCESS EVENT | +// ############################################################################// + +void __fastcall Hook_ProcessEvent(UObject* obj, + void* edx, + UFunction* func, + void* params, + void* null) { + try { + // This arg seems to be in the process of being deprecated, no usage in ghidra, always seems + // to be null, and it's gone in later ue versions. Gathering some extra info just in case. + if (null != nullptr) { + LOG(DEV_WARNING, L"Null param had a value in process event during func {} on obj {}", + func->get_path_name(), obj->get_path_name()); } - } - - void BL1Hook::find_get_path_name(void) { - get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan("get_path_name_func"); - LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); - } - void BL1Hook::find_static_find_object(void) { - static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); - LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); - } + auto data = hook_manager::impl::preprocess_hook("ProcessEvent", func, obj); + if (data != nullptr) { + // Copy args so that hooks can't modify them, for parity with call function + const WrappedStruct args_base{func, params}; + WrappedStruct args = args_base.copy_params_only(); + hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; - void BL1Hook::find_load_package(void) { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); - LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); - } + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); - //############################################################################// - // | PROCESS EVENT | - //############################################################################// - - void __fastcall Hook_ProcessEvent( - UObject* obj, - void* edx, - UFunction* func, - void* params, - void* null - ) { - try { - // This arg seems to be in the process of being deprecated, no usage in ghidra, always seems - // to be null, and it's gone in later ue versions. Gathering some extra info just in case. - if (null != nullptr) { - LOG( - DEV_WARNING, - L"Null param had a value in process event during func {} on obj {}", - func->get_path_name(), - obj->get_path_name() - ); + if (!block_execution) { + process_event_ptr(obj, edx, func, params, null); } - auto data = hook_manager::impl::preprocess_hook("ProcessEvent", func, obj); - if (data != nullptr) { - // Copy args so that hooks can't modify them, for parity with call function - const WrappedStruct args_base{func, params}; - WrappedStruct args = args_base.copy_params_only(); - hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; + if (hook.ret.has_value()) { + hook.ret.copy_to(reinterpret_cast(params)); + } - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + if (!hook_manager::impl::has_post_hooks(*data)) { + return; + } - if (!block_execution) { - process_event_ptr(obj, edx, func, params, null); - } + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from(reinterpret_cast(params)); + } - if (hook.ret.has_value()) { - hook.ret.copy_to(reinterpret_cast(params)); - } + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } - if (!hook_manager::impl::has_post_hooks(*data)) { - return; - } + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook); - if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { - hook.ret.copy_from(reinterpret_cast(params)); - } + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the ProcessEvent hook: {}", ex.what()); + } - if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); - } + process_event_ptr(obj, edx, func, params, null); +} - hook_manager::impl::run_hooks_of_type( - *data, hook_manager::Type::POST_UNCONDITIONAL, - hook); +void BL1Hook::hook_process_event(void) { + detour(PROCESS_EVENT_SIG, &Hook_ProcessEvent, &process_event_ptr, "ProcessEvent"); +} - return; +// ############################################################################// +// | CALL FUNCTION | +// ############################################################################// + +void __fastcall Hook_CallFunction(UObject* obj, + void* edx, + FFrame* stack, + void* result, + UFunction* func) { + try { + auto data = hook_manager::impl::preprocess_hook("CallFunction", func, obj); + if (data != nullptr) { + WrappedStruct args{func}; + auto original_code = stack->extract_current_args(args); + + hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; + + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + + if (block_execution) { + stack->Code++; + } else { + stack->Code = original_code; + call_function_ptr(obj, edx, stack, result, func); } - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred during the ProcessEvent hook: {}", ex.what()); - } - process_event_ptr(obj, edx, func, params, null); - } - - void BL1Hook::hook_process_event(void) { - detour(PROCESS_EVENT_SIG, &Hook_ProcessEvent, &process_event_ptr, "ProcessEvent"); - } - - //############################################################################// - // | CALL FUNCTION | - //############################################################################// - - void __fastcall Hook_CallFunction( - UObject* obj, - void* edx, - FFrame* stack, - void* result, - UFunction* func - ) { - try { - auto data = hook_manager::impl::preprocess_hook("CallFunction", func, obj); - if (data != nullptr) { - WrappedStruct args{func}; - auto original_code = stack->extract_current_args(args); - - hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; - - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); - - if (block_execution) { - stack->Code++; - } else { - stack->Code = original_code; - call_function_ptr(obj, edx, stack, result, func); - } - - if (hook.ret.has_value()) { - // Result is a pointer directly to where the property should go, remove the offset - hook.ret.copy_to( - reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); - } - - if (!hook_manager::impl::has_post_hooks(*data)) { - return; - } - - if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { - hook.ret.copy_from( - reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); - } - - if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); - } - - hook_manager::impl::run_hooks_of_type( - *data, hook_manager::Type::POST_UNCONDITIONAL, - hook); + if (hook.ret.has_value()) { + // Result is a pointer directly to where the property should go, remove the offset + hook.ret.copy_to(reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } + if (!hook_manager::impl::has_post_hooks(*data)) { return; } - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred during the CallFunction hook: {}", ex.what()); - } - call_function_ptr(obj, edx, stack, result, func); - } + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from(reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } - void BL1Hook::hook_call_function(void) { - detour(CALL_FUNCTION_SIG, &Hook_CallFunction, &call_function_ptr, "CallFunction"); - } + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } - //############################################################################// - // | BL2 IMPL SAYS NOT IMPLEMENTED | - //############################################################################// + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook); - void BL1Hook::flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const { - throw_version_error("Lazy object pointers are not implemented in UE3"); + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the CallFunction hook: {}", ex.what()); } - void BL1Hook::fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, const unreal::UObject* obj) const { - throw_version_error("Lazy object pointers are not implemented in UE3"); - } + call_function_ptr(obj, edx, stack, result, func); +} - void BL1Hook::ftext_as_culture_invariant(unreal::FText* text, TemporaryFString&& str) const { - throw_version_error("Not implemented in UE3"); - } +void BL1Hook::hook_call_function(void) { + detour(CALL_FUNCTION_SIG, &Hook_CallFunction, &call_function_ptr, "CallFunction"); +} - //############################################################################// - // | | - //############################################################################// +// ############################################################################// +// | BL2 IMPL SAYS NOT IMPLEMENTED | +// ############################################################################// - std::wstring BL1Hook::uobject_path_name(const unreal::UObject* obj) const { - ManagedFString str{}; - get_path_name_ptr(obj, nullptr, &str); - return str; - } +void BL1Hook::flazyobjectptr_assign(unreal::FLazyObjectPtr*, const unreal::UObject*) const { + throw_version_error("Lazy object pointers are not implemented in UE3"); +} - void BL1Hook::process_event(unreal::UObject* object, unreal::UFunction* func, void* params) const { - Hook_ProcessEvent(object, nullptr, func, params, nullptr); - } +void BL1Hook::fsoftobjectptr_assign(unreal::FSoftObjectPtr*, const unreal::UObject*) const { + throw_version_error("Lazy object pointers are not implemented in UE3"); +} - void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject* obj, void* param) const { - uint8_t index = *frame->Code++; - ((*fframe_step_gnatives)[index])(frame, param); - } +void BL1Hook::ftext_as_culture_invariant(unreal::FText*, TemporaryFString&&) const { + throw_version_error("Not implemented in UE3"); +} - void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const { - fname_init_ptr(name, str, number, 1, 1); - } +// ############################################################################// +// | | +// ############################################################################// - unreal::UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { - LOG(MISC, "LoadPackage"); - return load_package_ptr(nullptr, name.data(), flags); - } +std::wstring BL1Hook::uobject_path_name(const unreal::UObject* obj) const { + ManagedFString str{}; + get_path_name_ptr(obj, nullptr, &str); + return str; +} - unreal::UObject* BL1Hook::find_object(unreal::UClass* cls, const std::wstring& name) const { - return static_find_object_ptr(cls, nullptr, name.c_str(), 0 /* false */); - } +void BL1Hook::process_event(unreal::UObject* object, unreal::UFunction* func, void* params) const { + Hook_ProcessEvent(object, nullptr, func, params, nullptr); +} - UObject* BL1Hook::construct_object( - UClass* cls, - UObject* outer, - const FName& name, - decltype(UObject::ObjectFlags) flags, - UObject* template_obj - ) const { - LOG(MISC, "ConstructObject"); - return construct_obj_ptr( - cls, - outer, - name, - flags, - template_obj, - nullptr, - nullptr, - 0 /* false */ - ); - } +void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject*, void* param) const { + uint8_t index = *frame->Code++; + ((*fframe_step_gnatives)[index])(frame, param); +} - const unreal::GNames& BL1Hook::gnames(void) const { - return gnames_wrapper; - } +void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const { + fname_init_ptr(name, str, number, 1, 1); +} - const unreal::GObjects& BL1Hook::gobjects(void) const { - return gobjects_wrapper; - } +unreal::UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { + LOG(MISC, "LoadPackage"); + return load_package_ptr(nullptr, name.data(), flags); +} - void* BL1Hook::u_malloc(size_t len) const { - void* ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); - memset(ret, 0, len); - return ret; - } +unreal::UObject* BL1Hook::find_object(unreal::UClass* cls, const std::wstring& name) const { + return static_find_object_ptr(cls, nullptr, name.c_str(), 0 /* false */); +} - void* BL1Hook::u_realloc(void* original, size_t len) const { - return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); - } +UObject* BL1Hook::construct_object(UClass* cls, + UObject* outer, + const FName& name, + decltype(UObject::ObjectFlags) flags, + UObject* template_obj) const { + LOG(MISC, "ConstructObject"); + return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0 /* false */ + ); +} - void BL1Hook::u_free(void* data) const { - gmalloc->vftable->u_free(gmalloc, data); - } +const unreal::GNames& BL1Hook::gnames(void) const { + return gnames_wrapper; +} - namespace { - // Match initial jump offset - const constinit - Pattern<28> SET_COMMAND_SIG{ - "74 1A" // 74 1A | je borderlands.5E5505 - "8B5424 34" // 8B5424 34 | mov edx,dword ptr ss:[esp+34] - "53" // 53 | push ebx - "52" // 52 | push edx - "8BFE" // 8BFE | mov edi,esi - "E8 ????????" // E8 68F3FFFF | call borderlands.5E4860 - "83C4 08" // 83C4 08 | add esp,8 - "B8 01000000" // B8 01000000 | mov eax,1 - "E9 ????????" // E9 66110000 | jmp borderlands.5E666B - }; - } +const unreal::GObjects& BL1Hook::gobjects(void) const { + return gobjects_wrapper; +} - void BL1Hook::hexedit_set_command(void) { +void* BL1Hook::u_malloc(size_t len) const { + void* ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); + memset(ret, 0, len); + return ret; +} - // TODO: Not even sure what this is and I really didn't put much effort into finding out. - // Regardless the usage here is one that gets called on console command submission. - uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); +void* BL1Hook::u_realloc(void* original, size_t len) const { + return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); +} - if (set_command_msg == nullptr) { - LOG(ERROR, "Failed to find set command message signature."); - return; - } +void BL1Hook::u_free(void* data) const { + gmalloc->vftable->u_free(gmalloc, data); +} - // NOLINTBEGIN(readability-magic-numbers) - unlock_range(set_command_msg, 1); - set_command_msg[0] = 0xEB; - // NOLINTEND(readability-magic-numbers) +namespace { +// Match initial jump offset +const constinit Pattern<28> SET_COMMAND_SIG{ + "74 1A" // 74 1A | je borderlands.5E5505 + "8B5424 34" // 8B5424 34 | mov edx,dword ptr ss:[esp+34] + "53" // 53 | push ebx + "52" // 52 | push edx + "8BFE" // 8BFE | mov edi,esi + "E8 ????????" // E8 68F3FFFF | call borderlands.5E4860 + "83C4 08" // 83C4 08 | add esp,8 + "B8 01000000" // B8 01000000 | mov eax,1 + "E9 ????????" // E9 66110000 | jmp borderlands.5E666B +}; +} // namespace - } +void BL1Hook::hexedit_set_command(void) { + // TODO: Not even sure what this is and I really didn't put much effort into finding out. + // Regardless the usage here is one that gets called on console command submission. + uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); - namespace { - - // Just like in BL2 the jump offset is preserved for the initial jump since otherwise we would - // jump into arbitrary space. - const constinit - Pattern<29> ARRAY_LIMIT_MESSAGE{ - "0F8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 - "8B4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // 83C7 9D | add edi,FFFFFF9D - "57" // 57 | push edi - "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C - "E8 ????????" // E8 A83CF3FF | call borderlands.51BAA0 - "E9 ????????" // E9 67000000 | jmp borderlands.5E7E64 - }; + if (set_command_msg == nullptr) { + LOG(ERROR, "Failed to find set command message signature."); + return; } - void BL1Hook::hexedit_array_limit_message(void) const { - - // TODO: Verify that this works as expected. - uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); - - if (array_limit_msg == nullptr) { - LOG(ERROR, "Failed to find array limit message signature."); - return; - } + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(set_command_msg, 1); + set_command_msg[0] = 0xEB; + // NOLINTEND(readability-magic-numbers) +} - // NOLINTBEGIN(readability-magic-numbers) - unlock_range(array_limit_msg, 6); - array_limit_msg[0] = 0xE9; - array_limit_msg[1] = 0x7F; - array_limit_msg[2] = 0x00; - array_limit_msg[3] = 0x00; - array_limit_msg[4] = 0x00; - array_limit_msg[5] = 0x90; - // NOLINTEND(readability-magic-numbers) +namespace { - } +// Just like in BL2 the jump offset is preserved for the initial jump since otherwise we would +// jump into arbitrary space. +const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ + "0F8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 + "8B4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // 83C7 9D | add edi,FFFFFF9D + "57" // 57 | push edi + "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C + "E8 ????????" // E8 A83CF3FF | call borderlands.51BAA0 + "E9 ????????" // E9 67000000 | jmp borderlands.5E7E64 +}; +} // namespace - namespace { - const constinit - Pattern<19> ARRAY_LIMIT_SIG{ - "0F 8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 - "8b4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // 83C7 9D | add edi,FFFFFF9D - "57" // 57 | push edi - "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C - }; - } +void BL1Hook::hexedit_array_limit_message(void) const { + // TODO: Verify that this works as expected. + uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); - void BL1Hook::hexedit_array_limit(void) { - auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); - if (array_limit == nullptr) { - LOG(ERROR, "Couldn't find array limit signature"); - } else { - LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); - - // NOLINTBEGIN(readability-magic-numbers) - unlock_range(array_limit, 1); - array_limit[0] = 0xEB; - // NOLINTEND(readability-magic-numbers) - } + if (array_limit_msg == nullptr) { + LOG(ERROR, "Failed to find array limit message signature."); + return; } - //############################################################################// - // | CONSOLE STUFF | - //############################################################################// - - namespace { + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(array_limit_msg, 6); + array_limit_msg[0] = 0xE9; + array_limit_msg[1] = 0x7F; + array_limit_msg[2] = 0x00; + array_limit_msg[3] = 0x00; + array_limit_msg[4] = 0x00; + array_limit_msg[5] = 0x90; + // NOLINTEND(readability-magic-numbers) +} - bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { - LOG(MISC, "[Hook_RemoveSayFromConsoleCommand] ~ Executing"); +namespace { +const constinit Pattern<19> ARRAY_LIMIT_SIG{ + "0F 8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 + "8b4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // 83C7 9D | add edi,FFFFFF9D + "57" // 57 | push edi + "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C +}; +} - static const auto console_command_func = in.obj->Class - ->find_func_and_validate(L"ConsoleCommand"_fn); +void BL1Hook::hexedit_array_limit(void) { + auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); + if (array_limit == nullptr) { + LOG(ERROR, "Couldn't find array limit signature"); + } else { + LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); - static const auto command_property = in.args->type - ->find_prop_and_validate(L"Command"_fn); + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(array_limit, 1); + array_limit[0] = 0xEB; + // NOLINTEND(readability-magic-numbers) + } +} - auto param = in.args->get(command_property); - auto func = in.obj->get(console_command_func); - func.call(param); +// ############################################################################// +// | CONSOLE STUFF | +// ############################################################################// - return true; +namespace { - } +bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { + LOG(MISC, "[Hook_RemoveSayFromConsoleCommand] ~ Executing"); - BoundFunction s_ConsoleOutputText{}; + static const auto console_command_func = + in.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); - bool Hook_InjectConsole(hook_manager::Details& hook) { - LOG(MISC, "[Hook_InjectConsole] ~ Executing"); - hook_manager::remove_hook( - L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, - L"Hook_InjectConsole" - ); + static const auto command_property = + in.args->type->find_prop_and_validate(L"Command"_fn); - auto console = hook.obj->get(L"ViewportConsole"_fn); - LOG(MISC, "[Hook_InjectConsole] ~ {:p}, '{}'", (void*) console, console->get_path_name()); - s_ConsoleOutputText = console->get(L"OutputText"_fn); + auto param = in.args->get(command_property); + auto func = in.obj->get(console_command_func); + func.call(param); - return false; - } + return true; +} - bool Hook_ConsoleCommandImpl(hook_manager::Details& hook) { - LOG(MISC, "[Hook_ConsoleCommandImpl] ~ Executing"); +BoundFunction s_ConsoleOutputText{}; - const auto command_property = - hook.args->type->find_prop_and_validate(L"Command"_fn); +bool Hook_InjectConsole(hook_manager::Details& hook) { + LOG(MISC, "[Hook_InjectConsole] ~ Executing"); + hook_manager::remove_hook(L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, L"Hook_InjectConsole"); - const auto history_prop = - hook.obj->Class->find_prop_and_validate(L"History"_fn); - const auto history_top_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); - const auto history_bot_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); - const auto history_cur_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); + auto console = hook.obj->get(L"ViewportConsole"_fn); + LOG(MISC, "[Hook_InjectConsole] ~ {:p}, '{}'", (void*)console, console->get_path_name()); + s_ConsoleOutputText = console->get(L"OutputText"_fn); - const UFunction* purge_command_func = - hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); - const UFunction* save_config_func = - hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); + return false; +} - auto line = hook.args->get(command_property); +bool Hook_ConsoleCommandImpl(hook_manager::Details& hook) { + LOG(MISC, "[Hook_ConsoleCommandImpl] ~ Executing"); + + const auto command_property = + hook.args->type->find_prop_and_validate(L"Command"_fn); + + const auto history_prop = hook.obj->Class->find_prop_and_validate(L"History"_fn); + const auto history_top_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); + const auto history_bot_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); + const auto history_cur_prop = + hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); + + const UFunction* purge_command_func = + hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + const UFunction* save_config_func = hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); + + auto line = hook.args->get(command_property); + + auto [callback, cmd_len] = commands::impl::find_matching_command(line); + if (callback == nullptr) { + return false; + } + + // Add to the history buffer + { + // History is a ring buffer of recent commands + // HistoryBot points to the oldest entry, or -1 if history is empty. + // HistoryTop points to the next entry to fill. If history is empty it's 0. The value it + // points at is *not* shown in the history if we've wrapped. + // HistoryCur is always set to the same as top after running a command - presumably it + // changes while scrolling, but we can't really check that + + // First remove it from history + hook.obj->get(purge_command_func).call(line); + + // Insert this line at top + auto history_top = hook.obj->get(history_top_prop); + hook.obj->set(history_prop, history_top, line); + + // Increment top + history_top = (history_top + 1) % history_prop->ArrayDim; + hook.obj->set(history_top_prop, history_top); + // And set current + hook.obj->set(history_cur_prop, history_top); + + // Increment bottom if needed + auto history_bot = hook.obj->get(history_bot_prop); + if ((history_bot == -1) || history_bot == history_top) { + hook.obj->set(history_bot_prop, + (history_bot + 1) % history_prop->ArrayDim); + } - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { - return false; - } + hook.obj->get(save_config_func).call(); + } - // Add to the history buffer - { - // History is a ring buffer of recent commands - // HistoryBot points to the oldest entry, or -1 if history is empty. - // HistoryTop points to the next entry to fill. If history is empty it's 0. The value it - // points at is *not* shown in the history if we've wrapped. - // HistoryCur is always set to the same as top after running a command - presumably it - // changes while scrolling, but we can't really check that - - // First remove it from history - hook.obj->get(purge_command_func).call(line); - - // Insert this line at top - auto history_top = hook.obj->get(history_top_prop); - hook.obj->set(history_prop, history_top, line); - - // Increment top - history_top = (history_top + 1) % history_prop->ArrayDim; - hook.obj->set(history_top_prop, history_top); - // And set current - hook.obj->set(history_cur_prop, history_top); - - // Increment bottom if needed - auto history_bot = hook.obj->get(history_bot_prop); - if ((history_bot == -1) || history_bot == history_top) { - hook.obj->set( - history_bot_prop, - (history_bot + 1) % history_prop->ArrayDim); - } - - hook.obj->get(save_config_func).call(); - } + LOG(INFO, L">>> {} <<<", line); - LOG(INFO, L">>> {} <<<", line); + try { + callback->operator()(line.c_str(), line.size(), cmd_len); + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); + } - try { - callback->operator ()(line.c_str(), line.size(), cmd_len); - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); - } + return true; +} +} // namespace - return true; - } - } +bool BL1Hook::is_console_ready(void) const { + return s_ConsoleOutputText.func != nullptr; +} - bool BL1Hook::is_console_ready(void) const { - return s_ConsoleOutputText.func != nullptr; +void BL1Hook::uconsole_output_text(const std::wstring& str) const { + if (!is_console_ready()) { + return; } + s_ConsoleOutputText.call(str); +} - void BL1Hook::uconsole_output_text(const std::wstring& str) const { - if (!is_console_ready()) return; - s_ConsoleOutputText.call(str); - } +void BL1Hook::inject_console(void) { + LOG(MISC, "Hooking Engine.Console:ShippingConsoleCommand"); + hook_manager::add_hook(L"Engine.Console:ShippingConsoleCommand", + unrealsdk::hook_manager::Type::PRE, L"Hook_RemoveSayFromConsoleCommand", + &Hook_RemoveSayFromConsoleCommand); - void BL1Hook::inject_console(void) { - - LOG(MISC, "Hooking Engine.Console:ShippingConsoleCommand"); - hook_manager::add_hook( - L"Engine.Console:ShippingConsoleCommand", - unrealsdk::hook_manager::Type::PRE, - L"Hook_RemoveSayFromConsoleCommand", - &Hook_RemoveSayFromConsoleCommand - ); - - LOG(MISC, "Hooking Engine.Console:ConsoleCommand"); - hook_manager::add_hook( - L"Engine.Console:ConsoleCommand", - hook_manager::Type::PRE, - L"Hook_ConsoleCommandImpl", - &Hook_ConsoleCommandImpl - ); - - LOG(MISC, "Hooking WillowGame.WillowGameViewportClient:PostRender"); - hook_manager::add_hook( - L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, - L"Hook_InjectConsole", - &Hook_InjectConsole - ); - } + LOG(MISC, "Hooking Engine.Console:ConsoleCommand"); + hook_manager::add_hook(L"Engine.Console:ConsoleCommand", hook_manager::Type::PRE, + L"Hook_ConsoleCommandImpl", &Hook_ConsoleCommandImpl); -} // namespace + LOG(MISC, "Hooking WillowGame.WillowGameViewportClient:PostRender"); + hook_manager::add_hook(L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, L"Hook_InjectConsole", &Hook_InjectConsole); +} +} // namespace unrealsdk::game From 6aa1d7d0979e1f78e04f4f6d4694369ae044bf82 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:49:38 +0100 Subject: [PATCH 09/81] Revert "fix .gitmodules common_cmake url." This reverts commit b91b43d26a12f9eb9c35479106d41f5b3b8c248d. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 188ce2b..de5a95e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "common_cmake"] path = common_cmake - url = https://github.com/bl-sdk/common_cmake + url = git@github.com:bl-sdk/common_cmake.git From 24c9bdf4510a3a2fba4cf06dd7558999bde7b3cc Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 2 Oct 2024 18:37:37 +1300 Subject: [PATCH 10/81] Drop unnecessary commit revert. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 188ce2b..53c17ce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "common_cmake"] path = common_cmake - url = https://github.com/bl-sdk/common_cmake + url = ../../bl-sdk/common_cmake.git From ba14b2a70ae915aab741073765c79edc5c4cca74 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:04:55 +0100 Subject: [PATCH 11/81] manually reapply submodule url fix. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 188ce2b..53c17ce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "common_cmake"] path = common_cmake - url = https://github.com/bl-sdk/common_cmake + url = ../../bl-sdk/common_cmake.git From 2bb5d29d49dbe930762e4257e7e7867e71882601 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:14:12 +0100 Subject: [PATCH 12/81] Fix compilation errors on standard cmake configurations. --- src/unrealsdk/game/bl1/antidebug.cpp | 2 +- src/unrealsdk/game/bl1/bl1.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp index 191c81e..10da82b 100644 --- a/src/unrealsdk/game/bl1/antidebug.cpp +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -7,7 +7,7 @@ #include "unrealsdk/game/bl1/bl1.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index ac3d0f7..f993497 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -1,3 +1,4 @@ + #include "unrealsdk/pch.h" #include "unrealsdk/game/bl1/bl1.h" @@ -20,6 +21,8 @@ #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/prop_traits.h" +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) + using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -889,3 +892,5 @@ void BL1Hook::inject_console(void) { } } // namespace unrealsdk::game + +#endif \ No newline at end of file From d1f881f3d6eeb18f494b1b03fbcd6e7fb673ec2d Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:58:41 +0100 Subject: [PATCH 13/81] add BL1Hook to selector.cpp --- src/unrealsdk/game/selector.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/selector.cpp b/src/unrealsdk/game/selector.cpp index f1cbc94..0bee2ba 100644 --- a/src/unrealsdk/game/selector.cpp +++ b/src/unrealsdk/game/selector.cpp @@ -2,6 +2,7 @@ #include "unrealsdk/env.h" #include "unrealsdk/game/abstract_hook.h" +#include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/game/tps/tps.h" @@ -27,9 +28,15 @@ using all_known_games = std::tuple; #ifdef UE4 #error No known games for UE4 x86 #else + +#if !defined(UNREALSDK_GAME_BL1) using all_known_games = std::tuple; -#endif -#endif +#else +using all_known_games = std::tuple; +#endif // else defined(UNREALSDK_GAME_BL1) + +#endif // else !UE4 +#endif // else !ARCH_X64 /** * @brief Recursive helper function to find the right game hook. From 0e56eebe920631a73094f73a259ea186d0c60321 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:15:12 +0100 Subject: [PATCH 14/81] Realign UClass::Interfaces --- src/unrealsdk/unreal/classes/uclass.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index 6d4eac8..83e63c6 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -68,12 +68,13 @@ class UClass : public UStruct { #else // defined(UNREALSDK_GAME_BL1) + // NOTE: I don't know if the class has changed size; Will do a scan in ghidra for 1AC + public: uint8_t UnknownData00[0xC0]; UObject* ClassDefaultObject; // 340b - uint8_t UnknownData01[0x54]; - TArray Interfaces; // 428b - + uint8_t UnknownData01[0x48]; + TArray Interfaces; // 416b #endif From 261b3b38ea76e341e9c2ee6fc2f175cf1809c7f0 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:16:35 +0100 Subject: [PATCH 15/81] Remove old temporary hooks and go back to using `OutputTextLine` unsure if required. Also provides a hook to remove the 30 timer stopping you from saving. --- src/unrealsdk/game/bl1/bl1.cpp | 42 ++++++++-------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index f993497..c9853da 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -21,7 +21,8 @@ #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/prop_traits.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -360,45 +361,22 @@ void BL1Hook::hook(void) { namespace { -// TODO: These are purely for testing. - -bool Hook_StartMenuUp(hook_manager::Details&) { - LOG(MISC, "[Hook_StartMenuUp] Invoked!"); - return false; -} - -bool Hook_StartMenuDown(hook_manager::Details&) { - LOG(MISC, "[Hook_StartMenuDown] Invoked!"); +bool Hook_InstantlyLoadProfile(hook_manager::Details& in) { + // bIsProfileLoaded is set to true after 30 seconds; This sets it to true once the warp-tunnel + // has finished. + in.obj->get(L"ClientSetProfileLoaded"_fn).call(); return false; } -bool Hook_AlwaysSaveOnQuit(hook_manager::Details& in) { - auto dlg_prop = in.args->type->find_prop_and_validate(L"Dlg"_fn); - auto dlg = in.args->get(dlg_prop); - - dlg->get(L"Close"_fn).call(); - - in.obj->get(L"QuitToMenu"_fn).call(true); - - in.ret.set(true); - - return true; -} - } // namespace void BL1Hook::post_init(void) { LOG(MISC, "Attaching Hooks!"); inject_console(); - hook_manager::add_hook(L"WillowGame.WillowGFxMenuScreen:Nav_Up", hook_manager::Type::PRE, - L"Hook_StartMenuUp", &Hook_StartMenuUp); - - hook_manager::add_hook(L"WillowGame.WillowGFxMenuScreen:Nav_Down", hook_manager::Type::PRE, - L"Hook_StartMenuDown", &Hook_StartMenuDown); - - hook_manager::add_hook(L"WillowGame.WillowGFxMenuPause:PromptQuit_Ok", hook_manager::Type::PRE, - L"Hook_AlwaysSaveOnQuit", &Hook_AlwaysSaveOnQuit); + hook_manager::add_hook(L"WillowGame.WillowPlayerController:SpawningProcessComplete", + hook_manager::Type::POST, L"Hook_InstantlyLoadProfile", + &Hook_InstantlyLoadProfile); } // ############################################################################// @@ -791,7 +769,7 @@ bool Hook_InjectConsole(hook_manager::Details& hook) { auto console = hook.obj->get(L"ViewportConsole"_fn); LOG(MISC, "[Hook_InjectConsole] ~ {:p}, '{}'", (void*)console, console->get_path_name()); - s_ConsoleOutputText = console->get(L"OutputText"_fn); + s_ConsoleOutputText = console->get(L"OutputTextLine"_fn); return false; } From 47b62d892a13e9e587ebb002adb861540a9e4f93 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:16:21 +0100 Subject: [PATCH 16/81] Reformat, add SavePackage hook, and re-implement the hex-edits since they were wrong (still unsure). --- src/unrealsdk/game/bl1/bl1.cpp | 236 +++++++++++++++++++++++---------- src/unrealsdk/game/bl1/bl1.h | 5 + 2 files changed, 168 insertions(+), 73 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index c9853da..2998942 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -104,27 +104,23 @@ const constinit Pattern<15> GNAMES_SIG{ // // Candidate Function: 005e1770 -// 005E1770 | 55 | push ebp -// 005E1771 | 8BEC | mov ebp,esp -// 005E1773 | 6A FF | push FFFFFFFF -// 005E1775 | 68 B0CD8E01 | push borderlands.18ECDB0 -// 005E177A | 64:A1 00000000 | mov eax,dword ptr fs:[0] -// 005E1780 | 50 | push eax -// 005E1781 | 83EC 28 | sub esp,28 -// 005E1784 | 53 | push ebx -// 005E1785 | 56 | push esi -// 005E1786 | 57 | push edi -// 005E1787 | A1 8069F101 | mov eax,dword ptr ds:[1F16980] -// 005E178C | 33C5 | xor eax,ebp -// 005E178E | 50 | push eax +// 005E1770 | 55 | push ebp +// 005E1771 | 8BEC | mov ebp,esp +// 005E1773 | 6A FF | push FFFFFFFF +// 005E1775 | 68 B0CD8E01 | push borderlands.18ECDB0 +// 005E177A | 64:A1 00000000 | mov eax,dword ptr fs:[0] +// 005E1780 | 50 | push eax +// 005E1781 | 83EC 28 | sub esp,28 +// 005E1784 | 53 | push ebx +// 005E1785 | 56 | push esi +// 005E1786 | 57 | push edi +// 005E1787 | A1 8069F101 | mov eax,dword ptr ds:[1F16980] +// 005E178C | 33C5 | xor eax,ebp +// 005E178E | 50 | push eax using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); load_package_func load_package_ptr; -// Pattern as a single string for easy lookups -// 55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 83 EC 28 53 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D -// 45 F4 - const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ "55" // push ebp "8B EC" // mov ebp,esp @@ -144,8 +140,6 @@ const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ // | FNAME INIT | // ############################################################################// -// Candidate function: 005cef40 -// 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81EC 98 0C 00 00 A1 ?? ?? ?? ?? 33 C4 898424 940C0000 const constinit Pattern<34> FNAME_INIT_SIG{ "6A FF" // push FFFFFFFF "68 ?? ?? ?? ??" // push borderlands.18EB45B @@ -157,6 +151,7 @@ const constinit Pattern<34> FNAME_INIT_SIG{ "898424 940C0000" // mov dword ptr ss:[esp+C94],eax }; +// NOLINTNEXTLINE(modernize-use-using) typedef void(__thiscall* fname_init_func)(FName* name, const wchar_t* str, int32_t number, @@ -187,6 +182,7 @@ const constinit Pattern<36> FFRAME_STEP_SIG{ }; // RET 0x8; Callee cleans up the stack (8 bytes) +// NOLINTNEXTLINE(modernize-use-using) typedef void(__stdcall* fframe_step_func)(FFrame*, void*); fframe_step_func** fframe_step_gnatives; @@ -216,6 +212,7 @@ const constinit Pattern<19> GET_PATH_NAME_PATTERN{ // | CONSTRUCT OBJECT | // ############################################################################// +// NOLINTNEXTLINE(modernize-use-using) typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, UObject* outer, FName name, @@ -282,6 +279,7 @@ const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ // | PROCESS EVENT | // ############################################################################// +// NOLINTNEXTLINE(modernize-use-using) typedef void(__fastcall* process_event_func)(UObject* obj, void* /*edx*/, UFunction* func, @@ -311,6 +309,7 @@ const constinit Pattern<43> PROCESS_EVENT_SIG{ // | CALL FUNCTION | // ############################################################################// +// NOLINTNEXTLINE(modernize-use-using) typedef void(__fastcall* call_function_func)(UObject* obj, void* /*edx*/, FFrame* stack, @@ -329,6 +328,32 @@ const constinit Pattern<31> CALL_FUNCTION_SIG{ "83EC 40" // 83EC 40 | sub esp,40 }; +// ############################################################################// +// | SAVE PACKAGE FUNCTION | +// ############################################################################// + +// TODO: This is purely for the Unreal Editor; Also not sure on the parameters. + +// NOLINTNEXTLINE(modernize-use-using) +typedef UObject* (*save_package)(UObject* InOuter, + UObject* InPackage, + wchar_t* Filename, + UObject* InTemplate, + uint32_t Unknown_00, + uint32_t Unknown_01, + uint32_t Unknown_02, + void* ErrorMessage, + wchar_t* FileExtension, + int32_t bSaveAsBinary, + int32_t bOnlyIfDirty); + +save_package save_package_ptr = nullptr; + +const constinit Pattern<80> SAVE_PACKAGE_SIG{ + "6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC 30 09 00 00 A1 ?? ?? ?? ?? 33 C4 89 84 24 2C" + " 09 00 00 53 55 56 57 A1 ?? ?? ?? ?? 33 C4 50 8D 84 24 44 09 00 00 64 A3 00 00 00 00 8B 84 24" + " 6C 09 00 00 8B BC 24 54 09 00 00 8B AC 24 58 09 00 00"}; + } // namespace // ############################################################################// @@ -342,6 +367,7 @@ void BL1Hook::hook(void) { hook_process_event(); hook_call_function(); + // hook_save_package(); find_gobjects(); find_gnames(); @@ -353,10 +379,10 @@ void BL1Hook::hook(void) { find_static_find_object(); find_load_package(); - // I did a minor search for these for now disabled as they don't seem important rn - // hexedit_set_command(); - // hexedit_array_limit(); - // hexedit_array_limit_message(); + // These still need to be validated. + hexedit_set_command(); + hexedit_array_limit(); + hexedit_array_limit_message(); } namespace { @@ -438,6 +464,17 @@ void BL1Hook::find_static_find_object(void) { LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); } +namespace { + +UObject* Hook_LoadPackage(const UObject* obj, const wchar_t* pkg, uint32_t flags) { + std::wstring str{pkg}; + UObject* result = load_package_ptr(obj, pkg, flags); + LOG(MISC, "[LOAD_PACKAGE] ~ {:p}, '{}', {:#06x}, {}", (void*)obj, str, flags, + result->get_path_name()); + return result; +} +} // namespace + void BL1Hook::find_load_package(void) { load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); @@ -460,7 +497,7 @@ void __fastcall Hook_ProcessEvent(UObject* obj, func->get_path_name(), obj->get_path_name()); } - auto data = hook_manager::impl::preprocess_hook("ProcessEvent", func, obj); + auto data = hook_manager::impl::preprocess_hook(L"ProcessEvent", func, obj); if (data != nullptr) { // Copy args so that hooks can't modify them, for parity with call function const WrappedStruct args_base{func, params}; @@ -516,7 +553,7 @@ void __fastcall Hook_CallFunction(UObject* obj, void* result, UFunction* func) { try { - auto data = hook_manager::impl::preprocess_hook("CallFunction", func, obj); + auto data = hook_manager::impl::preprocess_hook(L"CallFunction", func, obj); if (data != nullptr) { WrappedStruct args{func}; auto original_code = stack->extract_current_args(args); @@ -568,6 +605,40 @@ void BL1Hook::hook_call_function(void) { detour(CALL_FUNCTION_SIG, &Hook_CallFunction, &call_function_ptr, "CallFunction"); } +// ############################################################################// +// | SAVE PACKAGE | +// ############################################################################// + +// NOTE: Leaving this here for now this is only relevant to the editor however I am unsure on the +// input parameters. Even those named are not guaranteed. + +namespace { +UObject* Hook_SavePackage(UObject* InOuter, + UObject* InPackage, + wchar_t* Filename, + UObject* InTemplate, + uint32_t Flags_00, + uint32_t Flags_01, + uint32_t Unknown_02, + void* ErrorMessage, + wchar_t* FileExtension, + int32_t bSaveAsBinary, + int32_t bOnlyIfDirty) { + LOG(MISC, "[SAVE_PACKAGE] ~ {:p}, {:p}, {:p}, {:p}", (void*)InOuter, (void*)InPackage, + (void*)Filename, (void*)InTemplate); + + UObject* result = + save_package_ptr(InOuter, InPackage, Filename, InTemplate, Flags_00, Flags_01, Unknown_02, + ErrorMessage, FileExtension, bSaveAsBinary, bOnlyIfDirty); + + return result; +} +} // namespace + +void BL1Hook::hook_save_package(void) { + detour(SAVE_PACKAGE_SIG, &Hook_SavePackage, &save_package_ptr, "Save Package"); +} + // ############################################################################// // | BL2 IMPL SAYS NOT IMPLEMENTED | // ############################################################################// @@ -585,7 +656,7 @@ void BL1Hook::ftext_as_culture_invariant(unreal::FText*, TemporaryFString&&) con } // ############################################################################// -// | | +// | UE CORE STUFF | // ############################################################################// std::wstring BL1Hook::uobject_path_name(const unreal::UObject* obj) const { @@ -608,7 +679,6 @@ void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number } unreal::UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { - LOG(MISC, "LoadPackage"); return load_package_ptr(nullptr, name.data(), flags); } @@ -621,9 +691,7 @@ UObject* BL1Hook::construct_object(UClass* cls, const FName& name, decltype(UObject::ObjectFlags) flags, UObject* template_obj) const { - LOG(MISC, "ConstructObject"); - return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0 /* false */ - ); + return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0); } const unreal::GNames& BL1Hook::gnames(void) const { @@ -648,24 +716,17 @@ void BL1Hook::u_free(void* data) const { gmalloc->vftable->u_free(gmalloc, data); } +// ############################################################################// +// | HEX EDITS | +// ############################################################################// + namespace { -// Match initial jump offset -const constinit Pattern<28> SET_COMMAND_SIG{ - "74 1A" // 74 1A | je borderlands.5E5505 - "8B5424 34" // 8B5424 34 | mov edx,dword ptr ss:[esp+34] - "53" // 53 | push ebx - "52" // 52 | push edx - "8BFE" // 8BFE | mov edi,esi - "E8 ????????" // E8 68F3FFFF | call borderlands.5E4860 - "83C4 08" // 83C4 08 | add esp,8 - "B8 01000000" // B8 01000000 | mov eax,1 - "E9 ????????" // E9 66110000 | jmp borderlands.5E666B -}; + +const constinit Pattern<17> SET_COMMAND_SIG{"75 16 8D 4C 24 18 68 ?? ?? ?? ?? 51 E8 ?? ?? ?? ??"}; + } // namespace void BL1Hook::hexedit_set_command(void) { - // TODO: Not even sure what this is and I really didn't put much effort into finding out. - // Regardless the usage here is one that gets called on console command submission. uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); if (set_command_msg == nullptr) { @@ -674,8 +735,9 @@ void BL1Hook::hexedit_set_command(void) { } // NOLINTBEGIN(readability-magic-numbers) - unlock_range(set_command_msg, 1); - set_command_msg[0] = 0xEB; + unlock_range(set_command_msg, 2); + set_command_msg[0] = 0x90; + set_command_msg[1] = 0x90; // NOLINTEND(readability-magic-numbers) } @@ -715,14 +777,24 @@ void BL1Hook::hexedit_array_limit_message(void) const { } namespace { -const constinit Pattern<19> ARRAY_LIMIT_SIG{ - "0F 8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 - "8b4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // 83C7 9D | add edi,FFFFFF9D - "57" // 57 | push edi - "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C -}; -} + +// Since the result of CALL is stored in EAX we can just NOP the CALL itself. Caller still cleans +// the stack anyway. +// 6A 64 PUSH 0x64 (100) +// 50 PUSH EAX +// 46 INC ESI +// E8 A1 CALL ClampToMax_FUN_00517770 +// F9 F2 FF +// 83 C4 08 ADD ESP,0x8 +// 3B F0 CMP ESI,EAX +// 0F 8C JL LAB_005e7d33 +// 59 FF +// FF FF + +const constinit Pattern<20> ARRAY_LIMIT_SIG{ + "6A 64 50 46 E8 ?? ?? ?? ?? 83 C4 08 3B F0 0F 8C 59 FF FF FF"}; + +} // namespace void BL1Hook::hexedit_array_limit(void) { auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); @@ -732,8 +804,21 @@ void BL1Hook::hexedit_array_limit(void) { LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); // NOLINTBEGIN(readability-magic-numbers) - unlock_range(array_limit, 1); - array_limit[0] = 0xEB; + auto* call_instruction = array_limit + 4; + + if (call_instruction[0] != 0xE8) { + LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", (void*)array_limit, + *call_instruction); + return; + } + + // Patching out the CALL ?? ?? ?? ?? to 5 NOP instructions + unlock_range(call_instruction, 5); + call_instruction[0] = 0x90; + call_instruction[1] = 0x90; + call_instruction[2] = 0x90; + call_instruction[3] = 0x90; + call_instruction[4] = 0x90; // NOLINTEND(readability-magic-numbers) } } @@ -745,8 +830,6 @@ void BL1Hook::hexedit_array_limit(void) { namespace { bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { - LOG(MISC, "[Hook_RemoveSayFromConsoleCommand] ~ Executing"); - static const auto console_command_func = in.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); @@ -763,7 +846,6 @@ bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { BoundFunction s_ConsoleOutputText{}; bool Hook_InjectConsole(hook_manager::Details& hook) { - LOG(MISC, "[Hook_InjectConsole] ~ Executing"); hook_manager::remove_hook(L"WillowGame.WillowGameViewportClient:PostRender", hook_manager::Type::PRE, L"Hook_InjectConsole"); @@ -775,8 +857,6 @@ bool Hook_InjectConsole(hook_manager::Details& hook) { } bool Hook_ConsoleCommandImpl(hook_manager::Details& hook) { - LOG(MISC, "[Hook_ConsoleCommandImpl] ~ Executing"); - const auto command_property = hook.args->type->find_prop_and_validate(L"Command"_fn); @@ -855,18 +935,28 @@ void BL1Hook::uconsole_output_text(const std::wstring& str) const { } void BL1Hook::inject_console(void) { - LOG(MISC, "Hooking Engine.Console:ShippingConsoleCommand"); - hook_manager::add_hook(L"Engine.Console:ShippingConsoleCommand", - unrealsdk::hook_manager::Type::PRE, L"Hook_RemoveSayFromConsoleCommand", - &Hook_RemoveSayFromConsoleCommand); - - LOG(MISC, "Hooking Engine.Console:ConsoleCommand"); - hook_manager::add_hook(L"Engine.Console:ConsoleCommand", hook_manager::Type::PRE, - L"Hook_ConsoleCommandImpl", &Hook_ConsoleCommandImpl); - - LOG(MISC, "Hooking WillowGame.WillowGameViewportClient:PostRender"); - hook_manager::add_hook(L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, L"Hook_InjectConsole", &Hook_InjectConsole); + // clang-format off + hook_manager::add_hook( + L"Engine.Console:ShippingConsoleCommand", + unrealsdk::hook_manager::Type::PRE, + L"Hook_RemoveSayFromConsoleCommand", + &Hook_RemoveSayFromConsoleCommand + ); + + hook_manager::add_hook( + L"Engine.Console:ConsoleCommand", + hook_manager::Type::PRE, + L"Hook_ConsoleCommandImpl", + &Hook_ConsoleCommandImpl + ); + + hook_manager::add_hook( + L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, + L"Hook_InjectConsole", + &Hook_InjectConsole + ); + // clang-format on } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index b8011c3..49ef665 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -47,6 +47,11 @@ namespace unrealsdk::game { */ static void hook_call_function(void); + /** + * @brief Hooks `SavePackage` and points it to a custom function. + */ + static void hook_save_package(void); + /** * @brief Finds GObjects, and populates the wrapper member. */ From 29a6ccba65fdc7824fe947cce568a9333c38efed Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:40:42 +0100 Subject: [PATCH 17/81] Cleaning up and reformatting the hex-edits. --- src/unrealsdk/game/bl1/bl1.cpp | 41 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 2998942..5ecbbe9 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -722,7 +722,13 @@ void BL1Hook::u_free(void* data) const { namespace { -const constinit Pattern<17> SET_COMMAND_SIG{"75 16 8D 4C 24 18 68 ?? ?? ?? ?? 51 E8 ?? ?? ?? ??"}; +const constinit Pattern<17> SET_COMMAND_SIG{ + "75 16" // 75 16 | jne borderlands.87E1A7 + "8D4C24 18" // 8D4C24 18 | lea ecx,dword ptr ss:[esp+18] + "68 ????????" // 68 0089B101 | push borderlands.1B18900 + "51" // 51 | push ecx + "E8 ????????" // E8 703CD4FF | call +}; } // namespace @@ -734,6 +740,8 @@ void BL1Hook::hexedit_set_command(void) { return; } + LOG(INFO, "Set Command: {:p}", reinterpret_cast(set_command_msg)); + // NOLINTBEGIN(readability-magic-numbers) unlock_range(set_command_msg, 2); set_command_msg[0] = 0x90; @@ -743,8 +751,6 @@ void BL1Hook::hexedit_set_command(void) { namespace { -// Just like in BL2 the jump offset is preserved for the initial jump since otherwise we would -// jump into arbitrary space. const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ "0F8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 "8B4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] @@ -757,7 +763,6 @@ const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ } // namespace void BL1Hook::hexedit_array_limit_message(void) const { - // TODO: Verify that this works as expected. uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); if (array_limit_msg == nullptr) { @@ -765,6 +770,8 @@ void BL1Hook::hexedit_array_limit_message(void) const { return; } + LOG(MISC, "Array Limit Message: {:p}", reinterpret_cast(array_limit_msg)); + // NOLINTBEGIN(readability-magic-numbers) unlock_range(array_limit_msg, 6); array_limit_msg[0] = 0xE9; @@ -778,21 +785,21 @@ void BL1Hook::hexedit_array_limit_message(void) const { namespace { -// Since the result of CALL is stored in EAX we can just NOP the CALL itself. Caller still cleans -// the stack anyway. -// 6A 64 PUSH 0x64 (100) -// 50 PUSH EAX -// 46 INC ESI -// E8 A1 CALL ClampToMax_FUN_00517770 -// F9 F2 FF -// 83 C4 08 ADD ESP,0x8 -// 3B F0 CMP ESI,EAX -// 0F 8C JL LAB_005e7d33 -// 59 FF -// FF FF +// - NOTE - +// In BL2 this seems to be inlined however for BL1 its not so we will NOP the CALL and its 4 byte +// address. The caller cleans the stack so need to worry about that. Might also be a good idea to +// move the signature forward to the CALL so we don't need to index weirdly. +// const constinit Pattern<20> ARRAY_LIMIT_SIG{ - "6A 64 50 46 E8 ?? ?? ?? ?? 83 C4 08 3B F0 0F 8C 59 FF FF FF"}; + "6A 64" // 6A 64 | push 64 + "50" // 50 | push eax + "46" // 46 | inc esi + "E8 ????????" // E8 A1F9F2FF | call + "83C4 08" // 83C4 08 | add esp,8 + "3BF0" // 3BF0 | cmp esi,eax + "0F8C 59FFFFFF" // 0F8C 59FFFFFF | jl borderlands.5E7D33 +}; } // namespace From cbb13381ddc95b272016dd151cf4ef4d9beb6c43 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:28:41 +0100 Subject: [PATCH 18/81] Apply consistent style to hex signatures excluding (GMalloc & SavePackage) --- src/unrealsdk/game/bl1/bl1.cpp | 239 +++++++++++++++------------------ 1 file changed, 106 insertions(+), 133 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 5ecbbe9..a1204f3 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -56,24 +56,6 @@ struct FMalloc { FMalloc* gmalloc; -// 8B0D ???????? 8B01 8B50 04 -// 00445911 | 8B0D B43BF701 | mov ecx,dword ptr ds:[1F73BB4] -// 00445917 | 8B01 | mov eax,dword ptr ds:[ecx] -// 00445919 | 8B50 04 | mov edx,dword ptr ds:[eax+4] -// 0044591C | 6A 08 | push 8 -// 0044591E | 68 50010000 | push 150 -// 00445923 | FFD2 | call edx -// 1F73BB4 -> 069A77B0 -> 01CC3AD8 -> [007B6EB0 013A8F50 013A8F70 013A8FC0] -// in vtable[1] we get -// 013A8F50 | 8B4424 04 | mov eax,dword ptr ss:[esp+4] -// 013A8F54 | 6A 10 | push 10 -// 013A8F56 | 50 | push eax -// 013A8F57 | FF15 B824A201 | call dword ptr ds:[<_aligned_malloc>] -// 013A8F5D | 83C4 08 | add esp,8 -// 013A8F60 | C2 0800 | ret 8 - -// 8B 0D ?? ?? ?? ?? 8B 01 8B 50 04 - // TODO: This pattern is not unique to GMalloc const constinit Pattern<11> GMALLOC_PATTERN{ "8B 0D {????????}" @@ -102,22 +84,6 @@ const constinit Pattern<15> GNAMES_SIG{ // | LOAD PACKAGE | // ############################################################################// -// -// Candidate Function: 005e1770 -// 005E1770 | 55 | push ebp -// 005E1771 | 8BEC | mov ebp,esp -// 005E1773 | 6A FF | push FFFFFFFF -// 005E1775 | 68 B0CD8E01 | push borderlands.18ECDB0 -// 005E177A | 64:A1 00000000 | mov eax,dword ptr fs:[0] -// 005E1780 | 50 | push eax -// 005E1781 | 83EC 28 | sub esp,28 -// 005E1784 | 53 | push ebx -// 005E1785 | 56 | push esi -// 005E1786 | 57 | push edi -// 005E1787 | A1 8069F101 | mov eax,dword ptr ds:[1F16980] -// 005E178C | 33C5 | xor eax,ebp -// 005E178E | 50 | push eax - using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); load_package_func load_package_ptr; @@ -141,14 +107,14 @@ const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ // ############################################################################// const constinit Pattern<34> FNAME_INIT_SIG{ - "6A FF" // push FFFFFFFF - "68 ?? ?? ?? ??" // push borderlands.18EB45B - "64 A1 00 00 00 00" // mov eax,dword ptr fs:[0] - "50" // push eax - "81EC 98 0C 00 00" // sub esp,C98 - "A1 ?? ?? ?? ??" // mov eax,dword ptr ds:[1F16980] - "33 C4" // xor eax,esp - "898424 940C0000" // mov dword ptr ss:[esp+C94],eax + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18EB45B + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "81EC 980C0000" // sub esp,C98 + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C4" // xor eax,esp + "898424 940C0000" // mov dword ptr ss:[esp+C94],eax }; // NOLINTNEXTLINE(modernize-use-using) @@ -167,18 +133,18 @@ fname_init_func fname_init_ptr = nullptr; // - NOTE - // This is inlined so we have to manually re-implement FFrame::Step using GNatives. const constinit Pattern<36> FFRAME_STEP_SIG{ - "74 ??" // 74 22 | je borderlands.59CEA4 - "8B45 D4" // 8B45 D4 | mov eax,dword ptr ss:[ebp-2C] - "830D ???????? 02" // 830D E83BF701 02 | or dword ptr ds:[1F73BE8],2 - "0FB610" // 0FB610 | movzx edx,byte ptr ds:[eax] - "8B1495 {????????}" // 8B1495 807AF901 | mov edx,dword ptr ds:[edx*4+1F97A80] - "57" // 57 | push edi - "8D4D BC" // 8D4D BC | lea ecx,dword ptr ss:[ebp-44] - "40" // 40 | inc eax - "51" // 51 | push ecx - "8B4D EC" // 8B4D EC | mov ecx,dword ptr ss:[ebp-14] - "8945 D4" // 8945 D4 | mov dword ptr ss:[ebp-2C],eax - "FFD2" // FFD2 | call edx + "74 ??" // je borderlands.59CEA4 + "8B45 D4" // mov eax,dword ptr ss:[ebp-2C] + "830D ???????? 02" // or dword ptr ds:[1F73BE8],2 + "0FB610" // movzx edx,byte ptr ds:[eax] + "8B1495 {????????}" // mov edx,dword ptr ds:[edx*4+1F97A80] + "57" // push edi + "8D4D BC" // lea ecx,dword ptr ss:[ebp-44] + "40" // inc eax + "51" // push ecx + "8B4D EC" // mov ecx,dword ptr ss:[ebp-14] + "8945 D4" // mov dword ptr ss:[ebp-2C],eax + "FFD2" // call edx }; // RET 0x8; Callee cleans up the stack (8 bytes) @@ -224,23 +190,23 @@ typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, construct_obj_func construct_obj_ptr; const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ - "6A FF" // 6A FF | push FFFFFFFF - "68 ????????" // 68 93D68E01 | push borderlands.18ED693 - "64A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83EC 0C" // 83EC 0C | sub esp,C - "53" // 53 | push ebx - "55" // 55 | push ebp - "56" // 56 | push esi - "57" // 57 | push edi - "A1 ????????" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] - "33C4" // 33C4 | xor eax,esp - "50" // 50 | push eax - "8D4424 20" // 8D4424 20 | lea eax,dword ptr ss:[esp+20] - "64 A3 00000000" // 64:A3 00000000 | mov dword ptr fs:[0],eax - "8B6C24 54" // 8B6C24 54 | mov ebp,dword ptr ss:[esp+54] - "896C24 14" // 896C24 14 | mov dword ptr ss:[esp+14],ebp - "85ED" // 85ED | test ebp,ebp + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18ED693 + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 0C" // sub esp,C + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C4" // xor eax,esp + "50" // push eax + "8D4424 20" // lea eax,dword ptr ss:[esp+20] + "64 A3 00000000" // mov dword ptr fs:[0],eax + "8B6C24 54" // mov ebp,dword ptr ss:[esp+54] + "896C24 14" // mov dword ptr ss:[esp+14],ebp + "85ED" // test ebp,ebp }; // ############################################################################// @@ -255,24 +221,24 @@ typedef UObject*(__cdecl* static_find_object_func)(const UClass* cls, static_find_object_func static_find_object_ptr; const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ - "6A FF" // 6A FF | push FFFFFFFF - "68 ?? ?? ?? ??" // 68 90C18E01 | push borderlands.18EC190 - "64 A1 00 00 00 00" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83 EC 24" // 83EC 24 | sub esp,24 - "53" // 53 | push ebx - "55" // 55 | push ebp - "56" // 56 | push esi - "57" // 57 | push edi - "A1 ?? ?? ?? ??" // A1 8069F101 | mov eax,dword ptr ds:[1F16980] - "33 C4" // 33C4 | xor eax,esp - "50" // 50 | push eax - "8D 44 24 38" // 8D4424 38 | lea eax,dword ptr ss:[esp+38] - "64 A3 00 00 00 00" // 64:A3 00000000 | mov dword ptr fs:[0],eax - "8B 74 24 4C" // 8B7424 4C | mov esi,dword ptr ss:[esp+4C] - "8B 7C 24 50" // 8B7C24 50 | mov edi,dword ptr ss:[esp+50] - "8B C6" // 8BC6 | mov eax,esi - "40" // 40 | inc eax + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18EC190 + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 24" // sub esp,24 + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33 C4" // xor eax,esp + "50" // push eax + "8D4424 38" // lea eax,dword ptr ss:[esp+38] + "64A3 00000000" // mov dword ptr fs:[0],eax + "8B7424 4C" // mov esi,dword ptr ss:[esp+4C] + "8B7C24 50" // mov edi,dword ptr ss:[esp+50] + "8BC6" // mov eax,esi + "40" // inc eax }; // ############################################################################// @@ -288,22 +254,23 @@ typedef void(__fastcall* process_event_func)(UObject* obj, process_event_func process_event_ptr; const constinit Pattern<43> PROCESS_EVENT_SIG{ - "55" - "8B EC" - "6A FF" - "68 ?? ?? ?? ??" - "64 A1 00 00 00 00" - "50" - "83 EC 40" - "A1 ?? ?? ?? ??" - "33 C5" - "89 45 F0" - "53" - "56" - "57" - "50" - "8D 45 F4" - "64 A3 00 00 00 00"}; + "55" // push ebp + "8BEC" // mov ebp,esp + "6A FF" // push FFFFFFFF + "68 ????????" // push + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 40" // sub esp,40 + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C5" // xor eax,ebp + "8945 F0" // mov dword ptr ss:[ebp-10],eax + "53" // push ebx + "56" // push esi + "57" // push edi + "50" // push eax + "8D45 F4" // lea eax,dword ptr ss:[ebp-C] + "64A3 00000000" // mov dword ptr fs:[0],eax +}; // ############################################################################// // | CALL FUNCTION | @@ -318,21 +285,27 @@ typedef void(__fastcall* call_function_func)(UObject* obj, call_function_func call_function_ptr; const constinit Pattern<31> CALL_FUNCTION_SIG{ - "55" // 55 | push ebp - "8DAC24 FCFBFFFF" // 8DAC24 FCFBFFFF | lea ebp,dword ptr ss:[esp-404] - "81EC 04040000" // 81EC 04040000 | sub esp,404 - "6A FF" // 6A FF | push FFFFFFFF - "68 ????????" // 68 38968E01 | push borderlands.18E9638 - "64 A1 00000000" // 64:A1 00000000 | mov eax,dword ptr fs:[0] - "50" // 50 | push eax - "83EC 40" // 83EC 40 | sub esp,40 + "55" // push ebp + "8DAC24 FCFBFFFF" // lea ebp,dword ptr ss:[esp-404] + "81EC 04040000" // sub esp,404 + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18E9638 + "64 A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 40" // sub esp,40 }; // ############################################################################// // | SAVE PACKAGE FUNCTION | // ############################################################################// -// TODO: This is purely for the Unreal Editor; Also not sure on the parameters. +// - NOTE - +// This is only for the editor it might be useful much later on but right now I will just leave it +// here so that it is known. +// +// The parameters into the function are not guaranteed to be correct I have verified that the +// UObjects are valid though. Unknown_XY are bitwise anded with constants as well. +// // NOLINTNEXTLINE(modernize-use-using) typedef UObject* (*save_package)(UObject* InOuter, @@ -723,11 +696,11 @@ void BL1Hook::u_free(void* data) const { namespace { const constinit Pattern<17> SET_COMMAND_SIG{ - "75 16" // 75 16 | jne borderlands.87E1A7 - "8D4C24 18" // 8D4C24 18 | lea ecx,dword ptr ss:[esp+18] - "68 ????????" // 68 0089B101 | push borderlands.1B18900 - "51" // 51 | push ecx - "E8 ????????" // E8 703CD4FF | call + "75 16" // jne borderlands.87E1A7 + "8D4C24 18" // lea ecx,dword ptr ss:[esp+18] + "68 ????????" // push borderlands.1B18900 + "51" // push ecx + "E8 ????????" // call }; } // namespace @@ -752,13 +725,13 @@ void BL1Hook::hexedit_set_command(void) { namespace { const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ - "0F8C 7E000000" // 0F8C 7E000000 | jl borderlands.5E7E64 - "8B4C24 38" // 8B4C24 38 | mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // 83C7 9D | add edi,FFFFFF9D - "57" // 57 | push edi - "68 ????????" // 68 2CF4A701 | push borderlands.1A7F42C - "E8 ????????" // E8 A83CF3FF | call borderlands.51BAA0 - "E9 ????????" // E9 67000000 | jmp borderlands.5E7E64 + "0F8C 7E000000" // jl borderlands.5E7E64 + "8B4C24 38" // mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // add edi,FFFFFF9D + "57" // push edi + "68 ????????" // push borderlands.1A7F42C + "E8 ????????" // call borderlands.51BAA0 + "E9 ????????" // jmp borderlands.5E7E64 }; } // namespace @@ -792,13 +765,13 @@ namespace { // const constinit Pattern<20> ARRAY_LIMIT_SIG{ - "6A 64" // 6A 64 | push 64 - "50" // 50 | push eax - "46" // 46 | inc esi - "E8 ????????" // E8 A1F9F2FF | call - "83C4 08" // 83C4 08 | add esp,8 - "3BF0" // 3BF0 | cmp esi,eax - "0F8C 59FFFFFF" // 0F8C 59FFFFFF | jl borderlands.5E7D33 + "6A 64" // push 64 + "50" // push eax + "46" // inc esi + "E8 ????????" // call + "83C4 08" // add esp,8 + "3BF0" // cmp esi,eax + "0F8C 59FFFFFF" // jl borderlands.5E7D33 }; } // namespace From a16a2f01f311acf23f8bbec077baa9b08cd337ef Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:48:15 +0100 Subject: [PATCH 19/81] GMalloc signature should now be unique to GMalloc --- src/unrealsdk/game/bl1/bl1.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index a1204f3..14ed695 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -56,11 +56,18 @@ struct FMalloc { FMalloc* gmalloc; -// TODO: This pattern is not unique to GMalloc -const constinit Pattern<11> GMALLOC_PATTERN{ - "8B 0D {????????}" - "8B 01" - "8B 50 04"}; +// - NOTE - +// This is *unique* to GMalloc for the 141 UDK however its not completely unique. Its just all +// other usages are also GMalloc. +// +const constinit Pattern<20> GMALLOC_PATTERN{ + "8B0D {????????}" // mov ecx,dword ptr ds:[1F73BB4] + "8B01" // mov eax,dword ptr ds:[ecx] + "8B50 04" // mov edx,dword ptr ds:[eax+4] + "6A 08" // push 8 + "68 E4000000" // push E4 + "FFD2" // call edx +}; GObjects gobjects_wrapper{}; From a675b273f69fdf560cbd840625577690e5f23686 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:40:25 +0100 Subject: [PATCH 20/81] Exposing 'UFunction::Func' for bl1 since its useful for getting address of native functions. --- src/unrealsdk/unreal/classes/ufunction.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index 6cd8155..269499a 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -58,6 +58,10 @@ class UFunction : public UStruct { private: uint8_t UnknownData00[0x6]; + +#if defined(UNREALSDK_GAME_BL1) + public: // Used in a console command to get address of function +#endif void* Func; #endif From 628b96f0082d0a92bdc0f8e384da15fc60ebb7c0 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:41:29 +0100 Subject: [PATCH 21/81] Refactoring and pulling everything out of bl1.cpp and into their appropriate files. --- src/unrealsdk/game/bl1/bl1.cpp | 956 +++--------------- src/unrealsdk/game/bl1/bl1.h | 260 +++-- src/unrealsdk/game/bl1/console.cpp | 331 ++++++ src/unrealsdk/game/bl1/globals.cpp | 75 ++ src/unrealsdk/game/bl1/hexedits.cpp | 127 +++ src/unrealsdk/game/bl1/hooks.cpp | 242 +++++ src/unrealsdk/game/bl1/memory.cpp | 83 ++ src/unrealsdk/game/bl1/object.cpp | 243 +++++ .../game/bl1/presistentobjectptr.cpp | 23 + 9 files changed, 1371 insertions(+), 969 deletions(-) create mode 100644 src/unrealsdk/game/bl1/console.cpp create mode 100644 src/unrealsdk/game/bl1/globals.cpp create mode 100644 src/unrealsdk/game/bl1/hexedits.cpp create mode 100644 src/unrealsdk/game/bl1/hooks.cpp create mode 100644 src/unrealsdk/game/bl1/memory.cpp create mode 100644 src/unrealsdk/game/bl1/object.cpp create mode 100644 src/unrealsdk/game/bl1/presistentobjectptr.cpp diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 14ed695..052cdf5 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -1,25 +1,10 @@ - #include "unrealsdk/pch.h" - #include "unrealsdk/game/bl1/bl1.h" -#include -#include -#include -#include -#include -#include -#include - -#include "unrealsdk/logging.h" +#include "unrealsdk/hook_manager.h" #include "unrealsdk/memory.h" -#include "unrealsdk/unreal/alignment.h" #include "unrealsdk/unreal/structs/fframe.h" -#include "unrealsdk/unreal/wrappers/gnames.h" - -#include "unrealsdk/unreal/classes/properties/copyable_property.h" -#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" -#include "unrealsdk/unreal/prop_traits.h" +#include "unrealsdk/version_error.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ && defined(UNREALSDK_GAME_BL1) @@ -28,326 +13,27 @@ using namespace unrealsdk::memory; using namespace unrealsdk::unreal; // - NOTE - -// Currently everything is in a single file mostly for testing purposes. The patterns that have -// been found are not minimal and may contain jump offsets. Additionally, the offsets are for the -// BL1 1.4.1 UDK version of the game. I did a quick scan for the ProccessEvent pattern in the Steam -// version and it did find the correct function however I didn't check anymore than that; other -// than to see if FFrame::Step was also inlined, it is. +// The offsets are for the BL1 1.4.1 UDK version of the game. I did a quick scan for the +// ProccessEvent pattern in the Steam version and it did find the correct function however I +// didn't check anymore than that; other than to see if FFrame::Step was also inlined, it is. // - -namespace { - -// ############################################################################// -// | GMALLOC | -// ############################################################################// - -struct FMalloc; - -struct FMallocVFtable { - void* exec; - void*(__thiscall* u_malloc)(FMalloc* self, uint32_t len, uint32_t align); - void*(__thiscall* u_realloc)(FMalloc* self, void* original, uint32_t len, uint32_t align); - void*(__thiscall* u_free)(FMalloc* self, void* data); -}; - -struct FMalloc { - FMallocVFtable* vftable; -}; - -FMalloc* gmalloc; - -// - NOTE - -// This is *unique* to GMalloc for the 141 UDK however its not completely unique. Its just all -// other usages are also GMalloc. +// I have done no work and put no effort into seeing if anything here can be used for the Enhanced +// version of the game. // -const constinit Pattern<20> GMALLOC_PATTERN{ - "8B0D {????????}" // mov ecx,dword ptr ds:[1F73BB4] - "8B01" // mov eax,dword ptr ds:[ecx] - "8B50 04" // mov edx,dword ptr ds:[eax+4] - "6A 08" // push 8 - "68 E4000000" // push E4 - "FFD2" // call edx -}; - -GObjects gobjects_wrapper{}; - -const constinit Pattern<15> GOBJECTS_SIG{ - "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] - "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] - "8B50 0C" // mov edx,dword ptr ds:[eax+C] - "2158 08" // and dword ptr ds:[eax+8],ebx -}; - -GNames gnames_wrapper{}; - -const constinit Pattern<15> GNAMES_SIG{ - "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] - "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] - "68 00100000" // push 1000 - "6A 00" // push 0 -}; - -// ############################################################################// -// | LOAD PACKAGE | -// ############################################################################// - -using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); -load_package_func load_package_ptr; - -const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ - "55" // push ebp - "8B EC" // mov ebp,esp - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18ECDB0 - "64 A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 28" // sub esp,28 - "53 56 57" // push ebx - "A1 ????????" // push esi - "33C5" // push edi - "50" // mov eax,dword ptr ds:[1F16980] - "8D45 F4" // xor eax,ebp -}; - -// ############################################################################// -// | FNAME INIT | -// ############################################################################// - -const constinit Pattern<34> FNAME_INIT_SIG{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18EB45B - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "81EC 980C0000" // sub esp,C98 - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C4" // xor eax,esp - "898424 940C0000" // mov dword ptr ss:[esp+C94],eax -}; - -// NOLINTNEXTLINE(modernize-use-using) -typedef void(__thiscall* fname_init_func)(FName* name, - const wchar_t* str, - int32_t number, - int32_t find_type, - int32_t split_name); - -fname_init_func fname_init_ptr = nullptr; - -// ############################################################################// -// | FFRAME STEP | -// ############################################################################// -// - NOTE - -// This is inlined so we have to manually re-implement FFrame::Step using GNatives. -const constinit Pattern<36> FFRAME_STEP_SIG{ - "74 ??" // je borderlands.59CEA4 - "8B45 D4" // mov eax,dword ptr ss:[ebp-2C] - "830D ???????? 02" // or dword ptr ds:[1F73BE8],2 - "0FB610" // movzx edx,byte ptr ds:[eax] - "8B1495 {????????}" // mov edx,dword ptr ds:[edx*4+1F97A80] - "57" // push edi - "8D4D BC" // lea ecx,dword ptr ss:[ebp-44] - "40" // inc eax - "51" // push ecx - "8B4D EC" // mov ecx,dword ptr ss:[ebp-14] - "8945 D4" // mov dword ptr ss:[ebp-2C],eax - "FFD2" // call edx -}; - -// RET 0x8; Callee cleans up the stack (8 bytes) -// NOLINTNEXTLINE(modernize-use-using) -typedef void(__stdcall* fframe_step_func)(FFrame*, void*); -fframe_step_func** fframe_step_gnatives; - -// ############################################################################// -// | GET PATH NAME | -// ############################################################################// - -// This one was basically identical to BL2. -// pattern: 8B4424 04 56 8BF1 3BF0 7476 85F6 7472 8B4E 28 53 -typedef void(__thiscall* get_path_name_func)(const UObject* self, - const UObject* stop_outer, - ManagedFString* str); -get_path_name_func get_path_name_ptr; -const constinit Pattern<19> GET_PATH_NAME_PATTERN{ - "8B4424 04" // mov eax,dword ptr ss:[esp+4] - "56" // push esi - "8BF1" // mov esi,ecx - "3BF0" // cmp esi,eax - "74 76" // je borderlands.5CF501 - "85F6" // test esi,esi - "74 72" // je borderlands.5CF501 - "8B4E 28" // mov ecx,dword ptr ds:[esi+28] - "53" // push ebx -}; - -// ############################################################################// -// | CONSTRUCT OBJECT | -// ############################################################################// - -// NOLINTNEXTLINE(modernize-use-using) -typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, - UObject* outer, - FName name, - uint64_t flags, - UObject* template_obj, - void* error_output_device, - void* instance_graph, - uint32_t assume_template_is_archetype); -construct_obj_func construct_obj_ptr; - -const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18ED693 - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 0C" // sub esp,C - "53" // push ebx - "55" // push ebp - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C4" // xor eax,esp - "50" // push eax - "8D4424 20" // lea eax,dword ptr ss:[esp+20] - "64 A3 00000000" // mov dword ptr fs:[0],eax - "8B6C24 54" // mov ebp,dword ptr ss:[esp+54] - "896C24 14" // mov dword ptr ss:[esp+14],ebp - "85ED" // test ebp,ebp -}; - -// ############################################################################// -// | STATIC FIND OBJECT | -// ############################################################################// - -// NOLINTNEXTLINE(modernize-use-using) -typedef UObject*(__cdecl* static_find_object_func)(const UClass* cls, - const UObject* package, - const wchar_t* str, - uint32_t exact_class); - -static_find_object_func static_find_object_ptr; -const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18EC190 - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 24" // sub esp,24 - "53" // push ebx - "55" // push ebp - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33 C4" // xor eax,esp - "50" // push eax - "8D4424 38" // lea eax,dword ptr ss:[esp+38] - "64A3 00000000" // mov dword ptr fs:[0],eax - "8B7424 4C" // mov esi,dword ptr ss:[esp+4C] - "8B7C24 50" // mov edi,dword ptr ss:[esp+50] - "8BC6" // mov eax,esi - "40" // inc eax -}; - -// ############################################################################// -// | PROCESS EVENT | -// ############################################################################// - -// NOLINTNEXTLINE(modernize-use-using) -typedef void(__fastcall* process_event_func)(UObject* obj, - void* /*edx*/, - UFunction* func, - void* params, - void* /*null*/); -process_event_func process_event_ptr; - -const constinit Pattern<43> PROCESS_EVENT_SIG{ - "55" // push ebp - "8BEC" // mov ebp,esp - "6A FF" // push FFFFFFFF - "68 ????????" // push - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 40" // sub esp,40 - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C5" // xor eax,ebp - "8945 F0" // mov dword ptr ss:[ebp-10],eax - "53" // push ebx - "56" // push esi - "57" // push edi - "50" // push eax - "8D45 F4" // lea eax,dword ptr ss:[ebp-C] - "64A3 00000000" // mov dword ptr fs:[0],eax -}; - -// ############################################################################// -// | CALL FUNCTION | -// ############################################################################// - -// NOLINTNEXTLINE(modernize-use-using) -typedef void(__fastcall* call_function_func)(UObject* obj, - void* /*edx*/, - FFrame* stack, - void* params, - UFunction* func); -call_function_func call_function_ptr; - -const constinit Pattern<31> CALL_FUNCTION_SIG{ - "55" // push ebp - "8DAC24 FCFBFFFF" // lea ebp,dword ptr ss:[esp-404] - "81EC 04040000" // sub esp,404 - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18E9638 - "64 A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 40" // sub esp,40 -}; - -// ############################################################################// -// | SAVE PACKAGE FUNCTION | -// ############################################################################// - -// - NOTE - -// This is only for the editor it might be useful much later on but right now I will just leave it -// here so that it is known. -// -// The parameters into the function are not guaranteed to be correct I have verified that the -// UObjects are valid though. Unknown_XY are bitwise anded with constants as well. -// - -// NOLINTNEXTLINE(modernize-use-using) -typedef UObject* (*save_package)(UObject* InOuter, - UObject* InPackage, - wchar_t* Filename, - UObject* InTemplate, - uint32_t Unknown_00, - uint32_t Unknown_01, - uint32_t Unknown_02, - void* ErrorMessage, - wchar_t* FileExtension, - int32_t bSaveAsBinary, - int32_t bOnlyIfDirty); - -save_package save_package_ptr = nullptr; - -const constinit Pattern<80> SAVE_PACKAGE_SIG{ - "6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC 30 09 00 00 A1 ?? ?? ?? ?? 33 C4 89 84 24 2C" - " 09 00 00 53 55 56 57 A1 ?? ?? ?? ?? 33 C4 50 8D 84 24 44 09 00 00 64 A3 00 00 00 00 8B 84 24" - " 6C 09 00 00 8B BC 24 54 09 00 00 8B AC 24 58 09 00 00"}; +namespace unrealsdk::game { +// These could be defined in the class but since they are only used here this will do for now. +namespace { +void hook_save_package(void); +void inject_qol_hooks(void); } // namespace -// ############################################################################// -// | BL1Hook | -// ############################################################################// - -namespace unrealsdk::game { - void BL1Hook::hook(void) { hook_antidebug(); hook_process_event(); hook_call_function(); - // hook_save_package(); find_gobjects(); find_gnames(); @@ -359,251 +45,144 @@ void BL1Hook::hook(void) { find_static_find_object(); find_load_package(); - // These still need to be validated. hexedit_set_command(); hexedit_array_limit(); hexedit_array_limit_message(); } -namespace { - -bool Hook_InstantlyLoadProfile(hook_manager::Details& in) { - // bIsProfileLoaded is set to true after 30 seconds; This sets it to true once the warp-tunnel - // has finished. - in.obj->get(L"ClientSetProfileLoaded"_fn).call(); - return false; -} - -} // namespace - void BL1Hook::post_init(void) { LOG(MISC, "Attaching Hooks!"); inject_console(); - - hook_manager::add_hook(L"WillowGame.WillowPlayerController:SpawningProcessComplete", - hook_manager::Type::POST, L"Hook_InstantlyLoadProfile", - &Hook_InstantlyLoadProfile); + inject_qol_hooks(); } // ############################################################################// -// | FIND METHODS | CALLED IN BL1Hook::hook() +// | FFRAME STEP | // ############################################################################// -void BL1Hook::find_gobjects(void) { - auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); - LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); - gobjects_wrapper = GObjects(gobjects_ptr); -} +namespace { -void BL1Hook::find_gnames(void) { - auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); - LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); - gnames_wrapper = GNames(gnames_ptr); -} +// - NOTE - +// This is inlined so we have to manually re-implement FFrame::Step using GNatives. +const constinit Pattern<36> FFRAME_STEP_SIG{ + "74 ??" // je borderlands.59CEA4 + "8B45 D4" // mov eax,dword ptr ss:[ebp-2C] + "830D ???????? 02" // or dword ptr ds:[1F73BE8],2 + "0FB610" // movzx edx,byte ptr ds:[eax] + "8B1495 {????????}" // mov edx,dword ptr ds:[edx*4+1F97A80] + "57" // push edi + "8D4D BC" // lea ecx,dword ptr ss:[ebp-44] + "40" // inc eax + "51" // push ecx + "8B4D EC" // mov ecx,dword ptr ss:[ebp-14] + "8945 D4" // mov dword ptr ss:[ebp-2C],eax + "FFD2" // call edx +}; -void BL1Hook::find_fname_init(void) { - fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); - LOG(MISC, "FName::Init: {:p}", (void*)fname_init_ptr); -} +// RET 0x8; Callee cleans up the stack (8 bytes) +// NOLINTNEXTLINE(modernize-use-using) +typedef void(__stdcall* fframe_step_func)(FFrame*, void*); +fframe_step_func** fframe_step_gnatives; + +} // namespace void BL1Hook::find_fframe_step(void) { fframe_step_gnatives = FFRAME_STEP_SIG.sigscan_nullable(); if (fframe_step_gnatives == nullptr) { - LOG(MISC, "FFrame::Step(...), GNatives was null."); + LOG(ERROR, "FFrame::Step(...), GNatives was null."); return; } LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); } -void BL1Hook::find_gmalloc(void) { - gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); - LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); +void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject*, void* param) const { + ((*fframe_step_gnatives)[*frame->Code++])(frame, param); } -void BL1Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); - LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); -} +// ############################################################################// +// | FNAME INIT | +// ############################################################################// namespace { -void __fastcall Hook_PathName(UObject* obj, void*, UObject* outer, ManagedFString* fstr) { - LOG(MISC, "[Hook_PathName] ~ {:p}, {:p}, {:p}", (void*)obj, (void*)outer, (void*)fstr); - get_path_name_ptr(obj, outer, fstr); -} -} // namespace -void BL1Hook::find_get_path_name(void) { - get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan("get_path_name_func"); - LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); -} +const constinit Pattern<34> FNAME_INIT_SIG{ + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18EB45B + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "81EC 980C0000" // sub esp,C98 + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C4" // xor eax,esp + "898424 940C0000" // mov dword ptr ss:[esp+C94],eax +}; -void BL1Hook::find_static_find_object(void) { - static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); - LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); -} +// NOLINTNEXTLINE(modernize-use-using) +typedef void(__thiscall* fname_init_func)(FName* name, + const wchar_t* str, + int32_t number, + int32_t find_type, + int32_t split_name); -namespace { +fname_init_func fname_init_ptr = nullptr; -UObject* Hook_LoadPackage(const UObject* obj, const wchar_t* pkg, uint32_t flags) { - std::wstring str{pkg}; - UObject* result = load_package_ptr(obj, pkg, flags); - LOG(MISC, "[LOAD_PACKAGE] ~ {:p}, '{}', {:#06x}, {}", (void*)obj, str, flags, - result->get_path_name()); - return result; -} } // namespace -void BL1Hook::find_load_package(void) { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); - LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); -} - -// ############################################################################// -// | PROCESS EVENT | -// ############################################################################// - -void __fastcall Hook_ProcessEvent(UObject* obj, - void* edx, - UFunction* func, - void* params, - void* null) { - try { - // This arg seems to be in the process of being deprecated, no usage in ghidra, always seems - // to be null, and it's gone in later ue versions. Gathering some extra info just in case. - if (null != nullptr) { - LOG(DEV_WARNING, L"Null param had a value in process event during func {} on obj {}", - func->get_path_name(), obj->get_path_name()); - } - - auto data = hook_manager::impl::preprocess_hook(L"ProcessEvent", func, obj); - if (data != nullptr) { - // Copy args so that hooks can't modify them, for parity with call function - const WrappedStruct args_base{func, params}; - WrappedStruct args = args_base.copy_params_only(); - hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; - - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); - - if (!block_execution) { - process_event_ptr(obj, edx, func, params, null); - } - - if (hook.ret.has_value()) { - hook.ret.copy_to(reinterpret_cast(params)); - } - - if (!hook_manager::impl::has_post_hooks(*data)) { - return; - } - - if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { - hook.ret.copy_from(reinterpret_cast(params)); - } - - if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); - } - - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, - hook); - - return; - } - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred during the ProcessEvent hook: {}", ex.what()); - } - - process_event_ptr(obj, edx, func, params, null); +void BL1Hook::find_fname_init(void) { + fname_init_ptr = FNAME_INIT_SIG.sigscan_nullable(); + LOG(MISC, "FName::Init: {:p}", (void*)fname_init_ptr); } -void BL1Hook::hook_process_event(void) { - detour(PROCESS_EVENT_SIG, &Hook_ProcessEvent, &process_event_ptr, "ProcessEvent"); +void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const { + fname_init_ptr(name, str, number, 1, 1); } // ############################################################################// -// | CALL FUNCTION | +// | SAVE PACKAGE | // ############################################################################// -void __fastcall Hook_CallFunction(UObject* obj, - void* edx, - FFrame* stack, - void* result, - UFunction* func) { - try { - auto data = hook_manager::impl::preprocess_hook(L"CallFunction", func, obj); - if (data != nullptr) { - WrappedStruct args{func}; - auto original_code = stack->extract_current_args(args); - - hook_manager::Details hook{obj, &args, {func->find_return_param()}, {func, obj}}; - - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); - - if (block_execution) { - stack->Code++; - } else { - stack->Code = original_code; - call_function_ptr(obj, edx, stack, result, func); - } - - if (hook.ret.has_value()) { - // Result is a pointer directly to where the property should go, remove the offset - hook.ret.copy_to(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); - } - - if (!hook_manager::impl::has_post_hooks(*data)) { - return; - } - - if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { - hook.ret.copy_from(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); - } - - if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); - } - - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, - hook); - - return; - } - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred during the CallFunction hook: {}", ex.what()); - } - - call_function_ptr(obj, edx, stack, result, func); -} +namespace { -void BL1Hook::hook_call_function(void) { - detour(CALL_FUNCTION_SIG, &Hook_CallFunction, &call_function_ptr, "CallFunction"); -} +// - NOTE - +// This is an function editor it might be useful much later on but right now I will leave it here +// so that it is known. +// +// The parameters into the function are not guaranteed to be correct I have verified that the +// UObjects are valid though. Unknown_XY are bitwise anded with constants as well. +// -// ############################################################################// -// | SAVE PACKAGE | -// ############################################################################// +// NOLINTNEXTLINE(modernize-use-using) +typedef UObject* (*save_package_func)(UObject* InOuter, + UObject* InPackage, + wchar_t* Filename, + UObject* InTemplate, + uint32_t Unknown_00, + uint32_t Unknown_01, + uint32_t Unknown_02, + void* ErrorMessage, + wchar_t* FileExtension, + int32_t bSaveAsBinary, + int32_t bOnlyIfDirty); + +save_package_func save_package_ptr = nullptr; -// NOTE: Leaving this here for now this is only relevant to the editor however I am unsure on the -// input parameters. Even those named are not guaranteed. +const constinit Pattern<80> SAVE_PACKAGE_SIG{ + "6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC 30 09 00 00 A1 ?? ?? ?? ?? 33 C4 89 84 24 2C" + " 09 00 00 53 55 56 57 A1 ?? ?? ?? ?? 33 C4 50 8D 84 24 44 09 00 00 64 A3 00 00 00 00 8B 84 24" + " 6C 09 00 00 8B BC 24 54 09 00 00 8B AC 24 58 09 00 00"}; -namespace { -UObject* Hook_SavePackage(UObject* InOuter, - UObject* InPackage, - wchar_t* Filename, - UObject* InTemplate, - uint32_t Flags_00, - uint32_t Flags_01, - uint32_t Unknown_02, - void* ErrorMessage, - wchar_t* FileExtension, - int32_t bSaveAsBinary, - int32_t bOnlyIfDirty) { +UObject* hook_save_package_detour(UObject* InOuter, + UObject* InPackage, + wchar_t* Filename, + UObject* InTemplate, + uint32_t Flags_00, + uint32_t Flags_01, + uint32_t Unknown_02, + void* ErrorMessage, + wchar_t* FileExtension, + int32_t bSaveAsBinary, + int32_t bOnlyIfDirty) { LOG(MISC, "[SAVE_PACKAGE] ~ {:p}, {:p}, {:p}, {:p}", (void*)InOuter, (void*)InPackage, (void*)Filename, (void*)InTemplate); @@ -613,337 +192,40 @@ UObject* Hook_SavePackage(UObject* InOuter, return result; } -} // namespace - -void BL1Hook::hook_save_package(void) { - detour(SAVE_PACKAGE_SIG, &Hook_SavePackage, &save_package_ptr, "Save Package"); -} - -// ############################################################################// -// | BL2 IMPL SAYS NOT IMPLEMENTED | -// ############################################################################// - -void BL1Hook::flazyobjectptr_assign(unreal::FLazyObjectPtr*, const unreal::UObject*) const { - throw_version_error("Lazy object pointers are not implemented in UE3"); -} - -void BL1Hook::fsoftobjectptr_assign(unreal::FSoftObjectPtr*, const unreal::UObject*) const { - throw_version_error("Lazy object pointers are not implemented in UE3"); -} - -void BL1Hook::ftext_as_culture_invariant(unreal::FText*, TemporaryFString&&) const { - throw_version_error("Not implemented in UE3"); -} - -// ############################################################################// -// | UE CORE STUFF | -// ############################################################################// - -std::wstring BL1Hook::uobject_path_name(const unreal::UObject* obj) const { - ManagedFString str{}; - get_path_name_ptr(obj, nullptr, &str); - return str; -} - -void BL1Hook::process_event(unreal::UObject* object, unreal::UFunction* func, void* params) const { - Hook_ProcessEvent(object, nullptr, func, params, nullptr); -} - -void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject*, void* param) const { - uint8_t index = *frame->Code++; - ((*fframe_step_gnatives)[index])(frame, param); -} - -void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const { - fname_init_ptr(name, str, number, 1, 1); -} - -unreal::UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { - return load_package_ptr(nullptr, name.data(), flags); -} - -unreal::UObject* BL1Hook::find_object(unreal::UClass* cls, const std::wstring& name) const { - return static_find_object_ptr(cls, nullptr, name.c_str(), 0 /* false */); -} - -UObject* BL1Hook::construct_object(UClass* cls, - UObject* outer, - const FName& name, - decltype(UObject::ObjectFlags) flags, - UObject* template_obj) const { - return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0); -} -const unreal::GNames& BL1Hook::gnames(void) const { - return gnames_wrapper; +void hook_save_package(void) { + detour(SAVE_PACKAGE_SIG, &hook_save_package_detour, &save_package_ptr, "bl1_hook_save_package"); } -const unreal::GObjects& BL1Hook::gobjects(void) const { - return gobjects_wrapper; -} - -void* BL1Hook::u_malloc(size_t len) const { - void* ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); - memset(ret, 0, len); - return ret; -} - -void* BL1Hook::u_realloc(void* original, size_t len) const { - return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); -} - -void BL1Hook::u_free(void* data) const { - gmalloc->vftable->u_free(gmalloc, data); -} +} // namespace // ############################################################################// -// | HEX EDITS | +// | CUSTOM HOOKS | // ############################################################################// namespace { -const constinit Pattern<17> SET_COMMAND_SIG{ - "75 16" // jne borderlands.87E1A7 - "8D4C24 18" // lea ecx,dword ptr ss:[esp+18] - "68 ????????" // push borderlands.1B18900 - "51" // push ecx - "E8 ????????" // call -}; - -} // namespace - -void BL1Hook::hexedit_set_command(void) { - uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); - - if (set_command_msg == nullptr) { - LOG(ERROR, "Failed to find set command message signature."); - return; - } - - LOG(INFO, "Set Command: {:p}", reinterpret_cast(set_command_msg)); - - // NOLINTBEGIN(readability-magic-numbers) - unlock_range(set_command_msg, 2); - set_command_msg[0] = 0x90; - set_command_msg[1] = 0x90; - // NOLINTEND(readability-magic-numbers) +bool hook_instantly_load_profile(hook_manager::Details& in) { + // bIsProfileLoaded is set to true after 30 seconds; This sets it to true once the warp-tunnel + // has finished. This allows you to instantly save on quit rather than having to wait 30s. + in.obj->get(L"ClientSetProfileLoaded"_fn).call(); + return false; } -namespace { - -const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ - "0F8C 7E000000" // jl borderlands.5E7E64 - "8B4C24 38" // mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // add edi,FFFFFF9D - "57" // push edi - "68 ????????" // push borderlands.1A7F42C - "E8 ????????" // call borderlands.51BAA0 - "E9 ????????" // jmp borderlands.5E7E64 -}; -} // namespace - -void BL1Hook::hexedit_array_limit_message(void) const { - uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); - - if (array_limit_msg == nullptr) { - LOG(ERROR, "Failed to find array limit message signature."); - return; - } - - LOG(MISC, "Array Limit Message: {:p}", reinterpret_cast(array_limit_msg)); - - // NOLINTBEGIN(readability-magic-numbers) - unlock_range(array_limit_msg, 6); - array_limit_msg[0] = 0xE9; - array_limit_msg[1] = 0x7F; - array_limit_msg[2] = 0x00; - array_limit_msg[3] = 0x00; - array_limit_msg[4] = 0x00; - array_limit_msg[5] = 0x90; - // NOLINTEND(readability-magic-numbers) +void inject_qol_hooks(void) { + hook_manager::add_hook(L"WillowGame.WillowPlayerController:SpawningProcessComplete", + hook_manager::Type::POST, L"bl1_hook_instantly_load_profile", + &hook_instantly_load_profile); } -namespace { - -// - NOTE - -// In BL2 this seems to be inlined however for BL1 its not so we will NOP the CALL and its 4 byte -// address. The caller cleans the stack so need to worry about that. Might also be a good idea to -// move the signature forward to the CALL so we don't need to index weirdly. -// - -const constinit Pattern<20> ARRAY_LIMIT_SIG{ - "6A 64" // push 64 - "50" // push eax - "46" // inc esi - "E8 ????????" // call - "83C4 08" // add esp,8 - "3BF0" // cmp esi,eax - "0F8C 59FFFFFF" // jl borderlands.5E7D33 -}; - } // namespace -void BL1Hook::hexedit_array_limit(void) { - auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); - if (array_limit == nullptr) { - LOG(ERROR, "Couldn't find array limit signature"); - } else { - LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); - - // NOLINTBEGIN(readability-magic-numbers) - auto* call_instruction = array_limit + 4; - - if (call_instruction[0] != 0xE8) { - LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", (void*)array_limit, - *call_instruction); - return; - } - - // Patching out the CALL ?? ?? ?? ?? to 5 NOP instructions - unlock_range(call_instruction, 5); - call_instruction[0] = 0x90; - call_instruction[1] = 0x90; - call_instruction[2] = 0x90; - call_instruction[3] = 0x90; - call_instruction[4] = 0x90; - // NOLINTEND(readability-magic-numbers) - } -} - // ############################################################################// -// | CONSOLE STUFF | +// | NOT IMPLEMENTED | // ############################################################################// -namespace { - -bool Hook_RemoveSayFromConsoleCommand(unrealsdk::hook_manager::Details& in) { - static const auto console_command_func = - in.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); - - static const auto command_property = - in.args->type->find_prop_and_validate(L"Command"_fn); - - auto param = in.args->get(command_property); - auto func = in.obj->get(console_command_func); - func.call(param); - - return true; -} - -BoundFunction s_ConsoleOutputText{}; - -bool Hook_InjectConsole(hook_manager::Details& hook) { - hook_manager::remove_hook(L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, L"Hook_InjectConsole"); - - auto console = hook.obj->get(L"ViewportConsole"_fn); - LOG(MISC, "[Hook_InjectConsole] ~ {:p}, '{}'", (void*)console, console->get_path_name()); - s_ConsoleOutputText = console->get(L"OutputTextLine"_fn); - - return false; -} - -bool Hook_ConsoleCommandImpl(hook_manager::Details& hook) { - const auto command_property = - hook.args->type->find_prop_and_validate(L"Command"_fn); - - const auto history_prop = hook.obj->Class->find_prop_and_validate(L"History"_fn); - const auto history_top_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); - const auto history_bot_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); - const auto history_cur_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); - - const UFunction* purge_command_func = - hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); - const UFunction* save_config_func = hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); - - auto line = hook.args->get(command_property); - - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { - return false; - } - - // Add to the history buffer - { - // History is a ring buffer of recent commands - // HistoryBot points to the oldest entry, or -1 if history is empty. - // HistoryTop points to the next entry to fill. If history is empty it's 0. The value it - // points at is *not* shown in the history if we've wrapped. - // HistoryCur is always set to the same as top after running a command - presumably it - // changes while scrolling, but we can't really check that - - // First remove it from history - hook.obj->get(purge_command_func).call(line); - - // Insert this line at top - auto history_top = hook.obj->get(history_top_prop); - hook.obj->set(history_prop, history_top, line); - - // Increment top - history_top = (history_top + 1) % history_prop->ArrayDim; - hook.obj->set(history_top_prop, history_top); - // And set current - hook.obj->set(history_cur_prop, history_top); - - // Increment bottom if needed - auto history_bot = hook.obj->get(history_bot_prop); - if ((history_bot == -1) || history_bot == history_top) { - hook.obj->set(history_bot_prop, - (history_bot + 1) % history_prop->ArrayDim); - } - - hook.obj->get(save_config_func).call(); - } - - LOG(INFO, L">>> {} <<<", line); - - try { - callback->operator()(line.c_str(), line.size(), cmd_len); - } catch (const std::exception& ex) { - LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); - } - - return true; -} -} // namespace - -bool BL1Hook::is_console_ready(void) const { - return s_ConsoleOutputText.func != nullptr; -} - -void BL1Hook::uconsole_output_text(const std::wstring& str) const { - if (!is_console_ready()) { - return; - } - s_ConsoleOutputText.call(str); -} - -void BL1Hook::inject_console(void) { - // clang-format off - hook_manager::add_hook( - L"Engine.Console:ShippingConsoleCommand", - unrealsdk::hook_manager::Type::PRE, - L"Hook_RemoveSayFromConsoleCommand", - &Hook_RemoveSayFromConsoleCommand - ); - - hook_manager::add_hook( - L"Engine.Console:ConsoleCommand", - hook_manager::Type::PRE, - L"Hook_ConsoleCommandImpl", - &Hook_ConsoleCommandImpl - ); - - hook_manager::add_hook( - L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, - L"Hook_InjectConsole", - &Hook_InjectConsole - ); - // clang-format on +void BL1Hook::ftext_as_culture_invariant(unreal::FText*, TemporaryFString&&) const { + throw_version_error("FTexts are not implemented in UE3"); } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 49ef665..2efb838 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -6,141 +6,137 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) namespace unrealsdk::game { - class BL1Hook : public AbstractHook { - protected: - /** - * @brief Hex edits out the `obj dump` array limit message. - */ - virtual void hexedit_array_limit_message(void) const; - - /** - * @brief Finds `FName::Init`, and sets up such that `fname_init` may be called. - */ - void find_fname_init(void); - - /** - * @brief Hooks the antidebug functions and disables them. - */ - static void hook_antidebug(void); - - /** - * @brief Hex edits out the protection on the set command. - */ - static void hexedit_set_command(void); - - /** - * @brief Hex edits out the `obj dump` array limit. - */ - static void hexedit_array_limit(void); - - /** - * @brief Hooks `UObject::ProcessEvent` and points it at the hook manager. - */ - static void hook_process_event(void); - - /** - * @brief Hooks `UObject::CallFunction` and points it at the hook manager. - */ - static void hook_call_function(void); - - /** - * @brief Hooks `SavePackage` and points it to a custom function. - */ - static void hook_save_package(void); - - /** - * @brief Finds GObjects, and populates the wrapper member. - */ - static void find_gobjects(void); - - /** - * @brief Finds GNames, and sets up such that `gnames` may be called. - */ - static void find_gnames(void); - - /** - * @brief Finds `FFrame::Step`, and sets up such that `fframe_step` may be called. - */ - static void find_fframe_step(void); - - /** - * @brief Finds `GMalloc`, and sets up such that `malloc`, `realloc`, and `free` may be called. - */ - static void find_gmalloc(void); - - /** - * @brief Finds `StaticConstructObject`, and sets up such that `construct_object` may be called. - */ - static void find_construct_object(void); - - /** - * @brief Finds `UObject::GetPathName`, and sets up such that `uobject_path_name` may be called. - */ - static void find_get_path_name(void); - - /** - * @brief Finds `StaticFindObject`, and sets up such that `find_object` may be called. - */ - static void find_static_find_object(void); - - /** - * @brief Finds `LoadPackage`, and sets up such that `load_package` may be called. - */ - static void find_load_package(void); - - /** - * @brief Creates a console and sets the bind (if required), and hooks logging onto it. - */ - static void inject_console(void); - - public: - void hook(void) override; - void post_init(void) override; - - [[nodiscard]] bool is_console_ready(void) const override; - - [[nodiscard]] const unreal::GObjects& gobjects(void) const override; - [[nodiscard]] const unreal::GNames& gnames(void) const override; - [[nodiscard]] void* u_malloc(size_t len) const override; - [[nodiscard]] void* u_realloc(void* original, size_t len) const override; - void u_free(void* data) const override; - [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, - unreal::UObject* outer, - const unreal::FName& name, - decltype(unreal::UObject::ObjectFlags) flags, - unreal::UObject* template_obj) const override; - [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, - const std::wstring& name) const override; - [[nodiscard]] unreal::UObject* load_package(const std::wstring& name, - uint32_t flags) const override; - - void fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const override; - void fframe_step(unreal::FFrame* frame, unreal::UObject* obj, void* param) const override; - void process_event(unreal::UObject* object, - unreal::UFunction* func, - void* params) const override; - void uconsole_output_text(const std::wstring& str) const override; - [[nodiscard]] std::wstring uobject_path_name(const unreal::UObject* obj) const override; - void ftext_as_culture_invariant(unreal::FText* text, - unreal::TemporaryFString&& str) const override; - void fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, - const unreal::UObject* obj) const override; - void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, - const unreal::UObject* obj) const override; - }; - - template<> - struct GameTraits { - static constexpr auto NAME = "Borderlands"; - - static bool matches_executable(std::string_view executable) { - return executable == "Borderlands.exe" || executable == "borderlands.exe"; - } - }; +class BL1Hook : public AbstractHook { + protected: + /** + * @brief Hex edits out the `obj dump` array limit message. + */ + virtual void hexedit_array_limit_message(void) const; + + /** + * @brief Finds `FName::Init`, and sets up such that `fname_init` may be called. + */ + void find_fname_init(void); + + /** + * @brief Hooks the antidebug functions and disables them. + */ + static void hook_antidebug(void); + + /** + * @brief Hex edits out the protection on the set command. + */ + static void hexedit_set_command(void); + + /** + * @brief Hex edits out the `obj dump` array limit. + */ + static void hexedit_array_limit(void); + + /** + * @brief Hooks `UObject::ProcessEvent` and points it at the hook manager. + */ + static void hook_process_event(void); + + /** + * @brief Hooks `UObject::CallFunction` and points it at the hook manager. + */ + static void hook_call_function(void); + + /** + * @brief Finds GObjects, and populates the wrapper member. + */ + static void find_gobjects(void); + + /** + * @brief Finds GNames, and sets up such that `gnames` may be called. + */ + static void find_gnames(void); + + /** + * @brief Finds `FFrame::Step`, and sets up such that `fframe_step` may be called. + */ + static void find_fframe_step(void); + + /** + * @brief Finds `GMalloc`, and sets up such that `malloc`, `realloc`, and `free` may be called. + */ + static void find_gmalloc(void); + + /** + * @brief Finds `StaticConstructObject`, and sets up such that `construct_object` may be called. + */ + static void find_construct_object(void); + + /** + * @brief Finds `UObject::GetPathName`, and sets up such that `uobject_path_name` may be called. + */ + static void find_get_path_name(void); + + /** + * @brief Finds `StaticFindObject`, and sets up such that `find_object` may be called. + */ + static void find_static_find_object(void); + + /** + * @brief Finds `LoadPackage`, and sets up such that `load_package` may be called. + */ + static void find_load_package(void); + + /** + * @brief Creates a console and sets the bind (if required), and hooks logging onto it. + */ + static void inject_console(void); + + public: + void hook(void) override; + void post_init(void) override; + + [[nodiscard]] bool is_console_ready(void) const override; + + [[nodiscard]] const unreal::GObjects& gobjects(void) const override; + [[nodiscard]] const unreal::GNames& gnames(void) const override; + [[nodiscard]] void* u_malloc(size_t len) const override; + [[nodiscard]] void* u_realloc(void* original, size_t len) const override; + void u_free(void* data) const override; + [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, + unreal::UObject* outer, + const unreal::FName& name, + decltype(unreal::UObject::ObjectFlags) flags, + unreal::UObject* template_obj) const override; + [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, + const std::wstring& name) const override; + [[nodiscard]] unreal::UObject* load_package(const std::wstring& name, + uint32_t flags) const override; + + void fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const override; + void fframe_step(unreal::FFrame* frame, unreal::UObject* obj, void* param) const override; + void process_event(unreal::UObject* object, + unreal::UFunction* func, + void* params) const override; + void uconsole_output_text(const std::wstring& str) const override; + [[nodiscard]] std::wstring uobject_path_name(const unreal::UObject* obj) const override; + void ftext_as_culture_invariant(unreal::FText* text, + unreal::TemporaryFString&& str) const override; + void fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, + const unreal::UObject* obj) const override; + void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, + const unreal::UObject* obj) const override; +}; + +template <> +struct GameTraits { + static constexpr auto NAME = "Borderlands"; + + static bool matches_executable(std::string_view executable) { + return executable == "Borderlands.exe" || executable == "borderlands.exe"; + } +}; } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp new file mode 100644 index 0000000..97e4726 --- /dev/null +++ b/src/unrealsdk/game/bl1/console.cpp @@ -0,0 +1,331 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" + +#include "unrealsdk/commands.h" +#include "unrealsdk/env.h" +#include "unrealsdk/hook_manager.h" +#include "unrealsdk/unreal/classes/properties/copyable_property.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustrproperty.h" +#include "unrealsdk/unreal/find_class.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::unreal; + +namespace unrealsdk::game { + +// TODO: Verify + +namespace { + +// ############################################################################// +// | CUSTOM COMMANDS | +// ############################################################################// + +// TODO: namespace these +void sset_command_impl(const wchar_t* args, size_t, size_t); +void sgetnativefunc_command_impl(const wchar_t* args, size_t, size_t); + +// ############################################################################// +// | DEFAULT CONSOLE HOOKS | +// ############################################################################// + +bool say_bypass_hook(hook_manager::Details& hook) { + static const auto console_command_func = + hook.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); + static const auto command_property = + hook.args->type->find_prop_and_validate(L"Command"_fn); + + hook.obj->get(console_command_func) + .call(hook.args->get(command_property)); + return true; +} + +bool console_command_hook(hook_manager::Details& hook) { + auto cls = hook.obj->Class; + + static const auto command_property = + hook.args->type->find_prop_and_validate(L"Command"_fn); + + static const auto history_prop = cls->find_prop_and_validate(L"History"_fn); + static const auto history_top_prop = + cls->find_prop_and_validate(L"HistoryTop"_fn); + static const auto history_bot_prop = + cls->find_prop_and_validate(L"HistoryBot"_fn); + static const auto history_cur_prop = + cls->find_prop_and_validate(L"HistoryCur"_fn); + + static const UFunction* purge_command_func = + cls->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + static const UFunction* save_config_func = cls->find_func_and_validate(L"SaveConfig"_fn); + + auto line = hook.args->get(command_property); + + auto [callback, cmd_len] = commands::impl::find_matching_command(line); + if (callback == nullptr) { + return false; + } + + // Add to the history buffer + { + // History is a ring buffer of recent commands + // HistoryBot points to the oldest entry, or -1 if history is empty. + // HistoryTop points to the next entry to fill. If history is empty it's 0. The value it + // points at is *not* shown in the history if we've wrapped. + // HistoryCur is always set to the same as top after running a command - presumably it + // changes while scrolling, but we can't really check that + + // First remove it from history + hook.obj->get(purge_command_func).call(line); + + // Insert this line at top + auto history_top = hook.obj->get(history_top_prop); + hook.obj->set(history_prop, history_top, line); + + // Increment top + history_top = (history_top + 1) % history_prop->ArrayDim; + hook.obj->set(history_top_prop, history_top); + // And set current + hook.obj->set(history_cur_prop, history_top); + + // Increment bottom if needed + auto history_bot = hook.obj->get(history_bot_prop); + if ((history_bot == -1) || history_bot == history_top) { + hook.obj->set(history_bot_prop, + (history_bot + 1) % history_prop->ArrayDim); + } + + hook.obj->get(save_config_func).call(); + } + + LOG(INFO, L">>> {} <<<", line); + + try { + callback->operator()(line.c_str(), line.size(), cmd_len); + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); + } + + return true; +} + +// Would prefer to call a native function where possible, however best I can tell, OutputText is +// actually implemented directly in unrealscript (along most of the console mechanics). +BoundFunction console_output_text{}; + +bool inject_console_hook(hook_manager::Details& hook) { + // clang-format off + hook_manager::remove_hook( + L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, + L"bl1_inject_console_hook" + ); + // clang-format on + + auto console = hook.obj->get(L"ViewportConsole"_fn); + + // Grab this reference ASAP + // Actually using OutputTextLine because it handles an empty string - OutputText does nothing + console_output_text = console->get(L"OutputTextLine"_fn); + + auto existing_console_key = console->get(L"ConsoleKey"_fn); + if (existing_console_key != L"None"_fn || existing_console_key == L"Undefine"_fn) { + LOG(MISC, "Console key is already set to '{}'", existing_console_key); + } else { + auto wanted_console_key = env::get(env::CONSOLE_KEY, env::defaults::CONSOLE_KEY); + console->set(L"ConsoleKey"_fn, FName{wanted_console_key}); + + LOG(MISC, "Set console key to '{}'", wanted_console_key); + } + + return false; +} + +} // namespace + +// ############################################################################// +// | BL1HOOK METHODS | +// ############################################################################// + +void BL1Hook::inject_console(void) { + // clang-format off + hook_manager::add_hook( + L"Engine.Console:ShippingConsoleCommand", + unrealsdk::hook_manager::Type::PRE, + L"bl1_say_bypass_hook", + &say_bypass_hook + ); + hook_manager::add_hook( + L"Engine.Console:ConsoleCommand", + hook_manager::Type::PRE, + L"bl1_console_command_hook", + &console_command_hook + ); + hook_manager::add_hook( + L"WillowGame.WillowGameViewportClient:PostRender", + hook_manager::Type::PRE, + L"bl1_inject_console_hook", + &inject_console_hook + ); + // clang-format on + + commands::add_command(L"sset", &sset_command_impl); + commands::add_command(L"sgetnativefunc", &sgetnativefunc_command_impl); +} + +void BL1Hook::uconsole_output_text(const std::wstring& str) const { + if (console_output_text.func == nullptr) { + return; + } + + console_output_text.call(str); +} + +bool BL1Hook::is_console_ready(void) const { + return console_output_text.func != nullptr; +} + +// ############################################################################// +// | CUSTOM HOOKS | +// ############################################################################// + +namespace { + +void uconsole_write(const std::wstring& text) { + console_output_text.call(text); +} + +void* uproperty_import_text(UProperty* prop, + const wchar_t* value, + void* write_to, + int32_t flags, + UObject* src, + void* err) { + // These are mostly speculative + constexpr auto INDEX_PRE_PROP_CHANGED = 0x11; + constexpr auto INDEX_POST_PROP_CHANGED = 0x13; + constexpr auto INDEX_IMPORT_TEXT = 0x54; + constexpr auto FLAG_DONT_NOTIFY_OBJECT = 0x200; + + typedef void(__fastcall * uprop_changed)(UObject*, void*, UProperty*); + typedef void*(__fastcall * uprop_import_text)(UProperty*, void*, const wchar_t*, void*, int32_t, + UObject*, void*); + + // Pre OnChange? + if ((src->ObjectFlags & FLAG_DONT_NOTIFY_OBJECT) == 0) { + reinterpret_cast(src->vftable[INDEX_PRE_PROP_CHANGED])(src, nullptr, prop); + } + + // UProperty::ImportText(...) + uprop_import_text func = reinterpret_cast(prop->vftable[INDEX_IMPORT_TEXT]); + void* result = func(prop, nullptr, value, write_to, flags, src, err); + + // Post OnChange? + if ((src->ObjectFlags & FLAG_DONT_NOTIFY_OBJECT) == 0) { + reinterpret_cast(src->vftable[INDEX_POST_PROP_CHANGED])(src, nullptr, prop); + } + + return result; +} + +} // namespace + +// ############################################################################// +// | CUSTOM CONSOLE COMMANDS | +// ############################################################################// + +namespace { + +// NOTE: src->get(prop) gives linker errors; we only need the value pointer here. +void* uproperty_get_value_ptr(UProperty* prop, UObject* src, int32_t index = 0) { + return reinterpret_cast(reinterpret_cast(src) + prop->Offset_Internal + + prop->ElementSize * index); +} + +void sset_command_impl(const wchar_t* line, size_t, size_t) { + std::wstringstream ss{line}; + + // sset CLASS NAME PROPERTY VALUE + // sset WillowGame.PlayerClassDefinition gd_Brick.Character.CharacterClass_Brick FOV 90.0 + std::wstring ignore{}; + std::wstring cls{}; + std::wstring name{}; + std::wstring prop_name{}; + + if (!(ss >> ignore >> cls >> name >> prop_name)) { + uconsole_write(L"Invalid format for 'sset' command"); + return; + } + + // From current position to the end of the string + ss >> std::ws; // Remove leading whitespace + std::streampos pos = ss.tellg(); + std::wstring value{ss.str().substr(static_cast(pos))}; + + UObject* found = find_object(cls, name); + + if (found == nullptr) { + uconsole_write(L"Failed to find object '" + name + L"'"); + return; + } + + UProperty* prop = found->Class->find_prop(FName{prop_name}); + + if (prop == nullptr) { + uconsole_write(L"Failed to find property '" + prop_name + L"'"); + return; + } + + void* value_ptr = uproperty_get_value_ptr(prop, found); + uproperty_import_text(prop, value.c_str(), value_ptr, 1, found, nullptr); +} + +void sgetnativefunc_command_impl(const wchar_t* line, size_t, size_t) { + // - NOTE - + // I don't expect this one to be used a lot its mostly for finding addresses of functions in + // disassembly. The text output might not be useful since you can just add a breakpoint here + // and copy the address directly. + // + + std::wstringstream ss{line}; + std::wstring ignore; + std::wstring cls_name; + std::wstring func_name; + + if (!(ss >> ignore >> cls_name >> func_name)) { + uconsole_write(L"Invalid format for sgetnativefunc"); + return; + } + + UClass* found_cls = find_class(cls_name); + + // Couldn't find the class + if (!found_cls) { + uconsole_write(L"Could not find class '" + cls_name + L"'"); + return; + } + + UFunction* found_func = found_cls->find_func_and_validate(FName{func_name}); + + // Couldn't find the function + if (!found_func) { + uconsole_write(fmt::format(L"Failed to find function '{}' in '{}'", func_name, + found_cls->get_path_name())); + return; + } + + std::wstring msg = fmt::format(L"Function address is '{:#010x}'", + reinterpret_cast(found_func->Func)); + + uconsole_write(msg); + LOG(MISC, "[CONSOLE] ~ {}", msg); +} + +} // namespace + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/globals.cpp b/src/unrealsdk/game/bl1/globals.cpp new file mode 100644 index 0000000..84a5411 --- /dev/null +++ b/src/unrealsdk/game/bl1/globals.cpp @@ -0,0 +1,75 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" + +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/wrappers/gnames.h" +#include "unrealsdk/unreal/wrappers/gobjects.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::memory; +using namespace unrealsdk::unreal; + +namespace unrealsdk::game { + +namespace { + +// ############################################################################// +// | GOBJECTS | +// ############################################################################// + +GObjects gobjects_wrapper{}; + +const constinit Pattern<15> GOBJECTS_SIG{ + "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] + "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] + "8B50 0C" // mov edx,dword ptr ds:[eax+C] + "2158 08" // and dword ptr ds:[eax+8],ebx +}; + +} // namespace + +void BL1Hook::find_gobjects(void) { + auto gobjects_ptr = read_offset(GOBJECTS_SIG.sigscan_nullable()); + LOG(MISC, "GObjects: {:p}", reinterpret_cast(gobjects_ptr)); + + gobjects_wrapper = GObjects(gobjects_ptr); +} + +const GObjects& BL1Hook::gobjects(void) const { + return gobjects_wrapper; +}; + +// ############################################################################// +// | GNAMES | +// ############################################################################// + +namespace { + +GNames gnames_wrapper{}; + +const constinit Pattern<15> GNAMES_SIG{ + "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] + "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] + "68 00100000" // push 1000 + "6A 00" // push 0 +}; + +} // namespace + +void BL1Hook::find_gnames(void) { + auto gnames_ptr = read_offset(GNAMES_SIG.sigscan_nullable()); + LOG(MISC, "GNames: {:p}", reinterpret_cast(gnames_ptr)); + + gnames_wrapper = GNames(gnames_ptr); +} + +const GNames& BL1Hook::gnames(void) const { + return gnames_wrapper; +} + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp new file mode 100644 index 0000000..a161e11 --- /dev/null +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -0,0 +1,127 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" +#include "unrealsdk/memory.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::memory; + +namespace unrealsdk::game { + +// ############################################################################// +// | SIGNATURES | +// ############################################################################// + +namespace { + +const constinit Pattern<17> SET_COMMAND_SIG{ + "75 16" // jne borderlands.87E1A7 + "8D4C24 18" // lea ecx,dword ptr ss:[esp+18] + "68 ????????" // push borderlands.1B18900 + "51" // push ecx + "E8 ????????" // call +}; + +const constinit Pattern<20> ARRAY_LIMIT_SIG{ + "6A 64" // push 64 + "50" // push eax + "46" // inc esi + "E8 ????????" // call + "83C4 08" // add esp,8 + "3BF0" // cmp esi,eax + "0F8C 59FFFFFF" // jl borderlands.5E7D33 +}; + +const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ + "0F8C 7E000000" // jl borderlands.5E7E64 + "8B4C24 38" // mov ecx,dword ptr ss:[esp+38] + "83C7 9D" // add edi,FFFFFF9D + "57" // push edi + "68 ????????" // push borderlands.1A7F42C + "E8 ????????" // call borderlands.51BAA0 + "E9 ????????" // jmp borderlands.5E7E64 +}; + +} // namespace + +// ############################################################################// +// | HEX EDITS | +// ############################################################################// + +void BL1Hook::hexedit_set_command(void) { + uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); + + if (set_command_msg == nullptr) { + LOG(ERROR, "Failed to find set command message signature."); + return; + } + + LOG(INFO, "Set Command: {:p}", reinterpret_cast(set_command_msg)); + + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(set_command_msg, 2); + set_command_msg[0] = 0x90; + set_command_msg[1] = 0x90; + // NOLINTEND(readability-magic-numbers) +} + +void BL1Hook::hexedit_array_limit(void) { + // - NOTE - + // In BL2 this seems to be inlined however for BL1 its not so we will NOP the CALL and its + // 4 byte address. The caller cleans the stack so need to worry about that. Might also be a + // good idea to move the signature forward to the CALL so we don't need to index weirdly. + // + + auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); + if (array_limit == nullptr) { + LOG(ERROR, "Couldn't find array limit signature"); + } else { + LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); + + // NOLINTBEGIN(readability-magic-numbers) + auto* call_instruction = array_limit + 4; + + // Should *never* be true + if (call_instruction[0] != 0xE8) { + LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", (void*)array_limit, + *call_instruction); + return; + } + + // Patching out the CALL ???????? to 5 NOP instructions + unlock_range(call_instruction, 5); + call_instruction[0] = 0x90; + call_instruction[1] = 0x90; + call_instruction[2] = 0x90; + call_instruction[3] = 0x90; + call_instruction[4] = 0x90; + // NOLINTEND(readability-magic-numbers) + } +} + +void BL1Hook::hexedit_array_limit_message(void) const { + uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); + + if (array_limit_msg == nullptr) { + LOG(ERROR, "Failed to find array limit message signature."); + return; + } + + LOG(MISC, "Array Limit Message: {:p}", reinterpret_cast(array_limit_msg)); + + // NOLINTBEGIN(readability-magic-numbers) + unlock_range(array_limit_msg, 6); + array_limit_msg[0] = 0xE9; + array_limit_msg[1] = 0x7F; + array_limit_msg[2] = 0x00; + array_limit_msg[3] = 0x00; + array_limit_msg[4] = 0x00; + array_limit_msg[5] = 0x90; + // NOLINTEND(readability-magic-numbers) +} + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp new file mode 100644 index 0000000..4be1e7a --- /dev/null +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -0,0 +1,242 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" + +#include "unrealsdk/env.h" +#include "unrealsdk/hook_manager.h" +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/classes/ufunction.h" +#include "unrealsdk/unreal/structs/fframe.h" +#include "unrealsdk/unreal/wrappers/wrapped_struct.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::memory; +using namespace unrealsdk::unreal; + +namespace unrealsdk::game { + +namespace { + +// This function is actually thiscall, but MSVC won't let us declare static thiscall functions +// As a workaround, declare it fastcall, and add a dummy edx arg. +// NOLINTNEXTLINE(modernize-use-using) +typedef void(__fastcall* process_event_func)(UObject* obj, + void* /*edx*/, + UFunction* func, + void* params, + void* /*null*/); +process_event_func process_event_ptr; + +const constinit Pattern<43> PROCESS_EVENT_SIG{ + "55" // push ebp + "8BEC" // mov ebp,esp + "6A FF" // push FFFFFFFF + "68 ????????" // push + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 40" // sub esp,40 + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C5" // xor eax,ebp + "8945 F0" // mov dword ptr ss:[ebp-10],eax + "53" // push ebx + "56" // push esi + "57" // push edi + "50" // push eax + "8D45 F4" // lea eax,dword ptr ss:[ebp-C] + "64A3 00000000" // mov dword ptr fs:[0],eax +}; + +void __fastcall process_event_hook(UObject* obj, + void* edx, + UFunction* func, + void* params, + void* null) { + try { + // This arg seems to be in the process of being deprecated, no usage in ghidra, always seems + // to be null, and it's gone in later ue versions. Gathering some extra info just in case. + if (null != nullptr) { + LOG(DEV_WARNING, L"Null param had a value in process event during func {} on obj {}", + func->get_path_name(), obj->get_path_name()); + } + + auto data = hook_manager::impl::preprocess_hook(L"ProcessEvent", func, obj); + if (data != nullptr) { + // Copy args so that hooks can't modify them, for parity with call function + const WrappedStruct args_base{func, params}; + WrappedStruct args = args_base.copy_params_only(); + hook_manager::Details hook{.obj = obj, + .args = &args, + .ret = {func->find_return_param()}, + .func = {.func = func, .object = obj}}; + + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + + if (!block_execution) { + process_event_ptr(obj, edx, func, params, null); + } + + if (hook.ret.has_value()) { + hook.ret.copy_to(reinterpret_cast(params)); + } + + if (!hook_manager::impl::has_post_hooks(*data)) { + return; + } + + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from(reinterpret_cast(params)); + } + + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } + + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook); + + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the ProcessEvent hook: {}", ex.what()); + } + + process_event_ptr(obj, edx, func, params, null); +} + +std::recursive_mutex process_event_mutex{}; + +void __fastcall locking_process_event_hook(UObject* obj, + void* edx, + UFunction* func, + void* params, + void* null) { + const std::lock_guard lock{process_event_mutex}; + process_event_hook(obj, edx, func, params, null); +} + +static_assert(std::is_same_v, + "process_event signature is incorrect"); +static_assert(std::is_same_v, + "process_event signature is incorrect"); + +/** + * @brief Checks if we should use a locking process event implementation. + * + * @return True if we should use locks. + */ +bool locking(void) { + // Basically just a function so we can be sure this static is initialized late - LTO hopefully + // takes care of it + static auto locking = env::defined(env::LOCKING_PROCESS_EVENT); + return locking; +} + +} // namespace + +void BL1Hook::hook_process_event(void) { + detour(PROCESS_EVENT_SIG, locking() ? locking_process_event_hook : process_event_hook, + &process_event_ptr, "ProcessEvent"); +} + +void BL1Hook::process_event(UObject* object, UFunction* func, void* params) const { + if (locking()) { + const std::lock_guard lock{process_event_mutex}; + process_event_hook(object, nullptr, func, params, nullptr); + } else { + process_event_hook(object, nullptr, func, params, nullptr); + } +} + +namespace { + +// NOLINTNEXTLINE(modernize-use-using) +typedef void(__fastcall* call_function_func)(UObject* obj, + void* /*edx*/, + FFrame* stack, + void* params, + UFunction* func); +call_function_func call_function_ptr; + +const constinit Pattern<31> CALL_FUNCTION_SIG{ + "55" // push ebp + "8DAC24 FCFBFFFF" // lea ebp,dword ptr ss:[esp-404] + "81EC 04040000" // sub esp,404 + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18E9638 + "64 A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 40" // sub esp,40 +}; + +void __fastcall call_function_hook(UObject* obj, + void* edx, + FFrame* stack, + void* result, + UFunction* func) { + try { + auto data = hook_manager::impl::preprocess_hook(L"CallFunction", func, obj); + if (data != nullptr) { + WrappedStruct args{func}; + auto original_code = stack->extract_current_args(args); + + hook_manager::Details hook{.obj = obj, + .args = &args, + .ret = {func->find_return_param()}, + .func = {.func = func, .object = obj}}; + + const bool block_execution = + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + + if (block_execution) { + stack->Code++; + } else { + stack->Code = original_code; + call_function_ptr(obj, edx, stack, result, func); + } + + if (hook.ret.has_value()) { + // Result is a pointer directly to where the property should go, remove the offset + hook.ret.copy_to(reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } + + if (!hook_manager::impl::has_post_hooks(*data)) { + return; + } + + if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { + hook.ret.copy_from(reinterpret_cast(result) + - hook.ret.prop->Offset_Internal); + } + + if (!block_execution) { + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + } + + hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook); + + return; + } + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred during the CallFunction hook: {}", ex.what()); + } + + call_function_ptr(obj, edx, stack, result, func); +} + +static_assert(std::is_same_v, + "call_function signature is incorrect"); + +} // namespace + +void BL1Hook::hook_call_function(void) { + detour(CALL_FUNCTION_SIG, call_function_hook, &call_function_ptr, "CallFunction"); +} + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/memory.cpp b/src/unrealsdk/game/bl1/memory.cpp new file mode 100644 index 0000000..896787c --- /dev/null +++ b/src/unrealsdk/game/bl1/memory.cpp @@ -0,0 +1,83 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/alignment.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::memory; +using namespace unrealsdk::unreal; + +namespace unrealsdk::game { + +namespace { + +#if defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" // thiscall on non-class +#endif + +struct FMalloc; +struct FMallocVFtable { + void* exec; + void*(__thiscall* u_malloc)(FMalloc* self, uint32_t len, uint32_t align); + void*(__thiscall* u_realloc)(FMalloc* self, void* original, uint32_t len, uint32_t align); + void*(__thiscall* u_free)(FMalloc* self, void* data); +}; +struct FMalloc { + FMallocVFtable* vftable; +}; + +#if defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif + +FMalloc* gmalloc; + +// - NOTE - +// This is *unique* to GMalloc for the 141 UDK however its not completely unique. Its just all +// other usages are also GMalloc. Might be a better idea to use ds:[eax+8] since realloc is a bit +// rarer and used pretty much exclusively by dynamic contiguous containers. +// + +const constinit Pattern<20> GMALLOC_PATTERN{ + "8B0D {????????}" // mov ecx,dword ptr ds:[1F73BB4] + "8B01" // mov eax,dword ptr ds:[ecx] + "8B50 04" // mov edx,dword ptr ds:[eax+4] + "6A 08" // push 8 + "68 E4000000" // push E4 + "FFD2" // call edx +}; + +} // namespace + +// ############################################################################// +// | BL1Hook Methods | +// ############################################################################// + +void BL1Hook::find_gmalloc(void) { + // Using plain `sigscan` since there's an extra level of indirection here, want to make sure to + // print an error before we potentially dereference it + gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); + LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); +} + +void* BL1Hook::u_malloc(size_t len) const { + auto ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); + memset(ret, 0, len); + return ret; +} + +void* BL1Hook::u_realloc(void* original, size_t len) const { + return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); +} + +void BL1Hook::u_free(void* data) const { + gmalloc->vftable->u_free(gmalloc, data); +} + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp new file mode 100644 index 0000000..a3ec47d --- /dev/null +++ b/src/unrealsdk/game/bl1/object.cpp @@ -0,0 +1,243 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" +#include "unrealsdk/logging.h" +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/structs/fstring.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +using namespace unrealsdk::unreal; +using namespace unrealsdk::memory; + +namespace unrealsdk::game { + +// ############################################################################// +// | CONSTRUCT OBJECT | +// ############################################################################// + +#pragma region ConstructObject + +namespace { + +// NOLINTNEXTLINE(modernize-use-using) +typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, + UObject* outer, + FName name, + uint64_t flags, + UObject* template_obj, + void* error_output_device, + void* instance_graph, + uint32_t assume_template_is_archetype); +construct_obj_func construct_obj_ptr; + +const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18ED693 + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 0C" // sub esp,C + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C4" // xor eax,esp + "50" // push eax + "8D4424 20" // lea eax,dword ptr ss:[esp+20] + "64 A3 00000000" // mov dword ptr fs:[0],eax + "8B6C24 54" // mov ebp,dword ptr ss:[esp+54] + "896C24 14" // mov dword ptr ss:[esp+14],ebp + "85ED" // test ebp,ebp +}; + +} // namespace + +void BL1Hook::find_construct_object(void) { + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); +} + +UObject* BL1Hook::construct_object(UClass* cls, + UObject* outer, + const FName& name, + decltype(UObject::ObjectFlags) flags, + UObject* template_obj) const { + return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, + 0 /* false */); +} + +#pragma endregion + +// ############################################################################// +// | PATH NAME | +// ############################################################################// + +#pragma region PathName + +// UObject::PathName is exposed to UnrealScript, we *can* get away with just calling it. +// That has a decent bit of overhead however, especially if process event is locking, and this +// function is used to tell if to call a hook, it's called all the time. +// Stick with a native function call for speed. + +namespace { + +#if defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" // thiscall on non-class +#endif + +// NOLINTNEXTLINE(modernize-use-using) +typedef void(__thiscall* get_path_name_func)(const UObject* self, + const UObject* stop_outer, + ManagedFString* str); + +#if defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif + +get_path_name_func get_path_name_ptr; + +const constinit Pattern<19> GET_PATH_NAME_PATTERN{ + "8B4424 04" // mov eax,dword ptr ss:[esp+4] + "56" // push esi + "8BF1" // mov esi,ecx + "3BF0" // cmp esi,eax + "74 76" // je borderlands.5CF501 + "85F6" // test esi,esi + "74 72" // je borderlands.5CF501 + "8B4E 28" // mov ecx,dword ptr ds:[esi+28] + "53" // push ebx +}; + +} // namespace + +void BL1Hook::find_get_path_name(void) { + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan_nullable(); + LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); +} + +std::wstring BL1Hook::uobject_path_name(const UObject* obj) const { + ManagedFString str{}; + get_path_name_ptr(obj, nullptr, &str); + return str; +} + +#pragma endregion + +// ############################################################################// +// | FIND OBJECT | +// ############################################################################// + +#pragma region FindObject + +// Again UObject::FindObject is exposed to UnrealScript. We don't care as much about performance +// here, but may as well still use a native call, was easy enough to find. + +namespace { + +// NOLINTNEXTLINE(modernize-use-using) +typedef UObject*(__cdecl* static_find_object_func)(const UClass* cls, + const UObject* package, + const wchar_t* str, + uint32_t exact_class); + +static_find_object_func static_find_object_ptr; +const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18EC190 + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 24" // sub esp,24 + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33 C4" // xor eax,esp + "50" // push eax + "8D4424 38" // lea eax,dword ptr ss:[esp+38] + "64A3 00000000" // mov dword ptr fs:[0],eax + "8B7424 4C" // mov esi,dword ptr ss:[esp+4C] + "8B7C24 50" // mov edi,dword ptr ss:[esp+50] + "8BC6" // mov eax,esi + "40" // inc eax +}; + +} // namespace + +void BL1Hook::find_static_find_object(void) { + static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan_nullable(); + LOG(MISC, "StaticFindObject: {:p}", reinterpret_cast(static_find_object_ptr)); +} + +UObject* BL1Hook::find_object(UClass* cls, const std::wstring& name) const { + return static_find_object_ptr(cls, nullptr, name.c_str(), 0 /* false */); +} + +#pragma endregion + +// ############################################################################// +// | LOAD PACKAGE | +// ############################################################################// + +#pragma region LoadPackage + +namespace { + +using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); +load_package_func load_package_ptr; + +const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ + "55" // push ebp + "8B EC" // mov ebp,esp + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.18ECDB0 + "64 A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 28" // sub esp,28 + "53 56 57" // push ebx + "A1 ????????" // push esi + "33C5" // push edi + "50" // mov eax,dword ptr ds:[1F16980] + "8D45 F4" // xor eax,ebp +}; + +} // namespace + +#define UNREALSDK_BL1_LOG_LOAD_PACKAGE + +#if defined(UNREALSDK_BL1_LOG_LOAD_PACKAGE) + +namespace { +UObject* bl1_load_package_detour(const UObject* outer, const wchar_t* name, uint32_t flags) { + LOG(INFO, L"[LOAD_PACKAGE] ~ {:p}, '{}', {:#08x}", (void*)outer, name, flags); + return load_package_ptr(outer, name, flags); +} +} // namespace + +void BL1Hook::find_load_package(void) { + detour(LOAD_PACKAGE_PATTERN, &bl1_load_package_detour, &load_package_ptr, + "bl1_load_package_detour"); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); +} + +#else + +void BL1Hook::find_load_package(void) { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); +} + +#endif + +[[nodiscard]] UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { + return load_package_ptr(nullptr, name.data(), flags); +} + +#pragma endregion + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl1/presistentobjectptr.cpp b/src/unrealsdk/game/bl1/presistentobjectptr.cpp new file mode 100644 index 0000000..5bc8e5c --- /dev/null +++ b/src/unrealsdk/game/bl1/presistentobjectptr.cpp @@ -0,0 +1,23 @@ +#include "unrealsdk/pch.h" + +#include "unrealsdk/game/bl1/bl1.h" +#include "unrealsdk/version_error.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +namespace unrealsdk::game { + +void BL1Hook::fsoftobjectptr_assign(unreal::FSoftObjectPtr* /* ptr */, + const unreal::UObject* /* obj */) const { + throw_version_error("Soft object pointers are not implemented in UE3"); +} + +void BL1Hook::flazyobjectptr_assign(unreal::FLazyObjectPtr* /* ptr */, + const unreal::UObject* /* obj */) const { + throw_version_error("Lazy object pointers are not implemented in UE3"); +} + +} // namespace unrealsdk::game + +#endif From 03ca9599a8aff2b9c4f58bbf25fb5dddba2008a7 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:38:48 +0100 Subject: [PATCH 22/81] Remove comments relating to bl2's implementation and provide override for logging LoadPackage. --- src/unrealsdk/game/bl1/bl1.h | 5 +++++ src/unrealsdk/game/bl1/object.cpp | 31 +++++++------------------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 2efb838..08b24cf 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -5,6 +5,7 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" +#include "unrealsdk/env.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ && defined(UNREALSDK_GAME_BL1) @@ -12,6 +13,10 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { + + public: + static const constexpr env::env_var_key KEY_LOG_LOAD_PACKAGE = "UNREALSDK_BL1_LOG_LOAD_PKG"; + protected: /** * @brief Hex edits out the `obj dump` array limit message. diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp index a3ec47d..3091668 100644 --- a/src/unrealsdk/game/bl1/object.cpp +++ b/src/unrealsdk/game/bl1/object.cpp @@ -76,11 +76,6 @@ UObject* BL1Hook::construct_object(UClass* cls, #pragma region PathName -// UObject::PathName is exposed to UnrealScript, we *can* get away with just calling it. -// That has a decent bit of overhead however, especially if process event is locking, and this -// function is used to tell if to call a hook, it's called all the time. -// Stick with a native function call for speed. - namespace { #if defined(__MINGW32__) @@ -132,9 +127,6 @@ std::wstring BL1Hook::uobject_path_name(const UObject* obj) const { #pragma region FindObject -// Again UObject::FindObject is exposed to UnrealScript. We don't care as much about performance -// here, but may as well still use a native call, was easy enough to find. - namespace { // NOLINTNEXTLINE(modernize-use-using) @@ -206,32 +198,23 @@ const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ } // namespace -#define UNREALSDK_BL1_LOG_LOAD_PACKAGE - -#if defined(UNREALSDK_BL1_LOG_LOAD_PACKAGE) - namespace { UObject* bl1_load_package_detour(const UObject* outer, const wchar_t* name, uint32_t flags) { - LOG(INFO, L"[LOAD_PACKAGE] ~ {:p}, '{}', {:#08x}", (void*)outer, name, flags); + LOG(INFO, L"[LOAD_PACKAGE] ~ {:p}, '{}', {:#010x}", (void*)outer, name, flags); return load_package_ptr(outer, name, flags); } } // namespace void BL1Hook::find_load_package(void) { - detour(LOAD_PACKAGE_PATTERN, &bl1_load_package_detour, &load_package_ptr, - "bl1_load_package_detour"); + if (env::defined(KEY_LOG_LOAD_PACKAGE)) { + detour(LOAD_PACKAGE_PATTERN, &bl1_load_package_detour, &load_package_ptr, + "bl1_load_package_detour"); + } else { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); + } LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); } -#else - -void BL1Hook::find_load_package(void) { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); - LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); -} - -#endif - [[nodiscard]] UObject* BL1Hook::load_package(const std::wstring& name, uint32_t flags) const { return load_package_ptr(nullptr, name.data(), flags); } From cf07fca9f9ef9373cffb94c415df8fb23ac5dd8d Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:38:59 +0100 Subject: [PATCH 23/81] Potential fix/hack for stopping the game from reloading manually modified objects in 'sset' command. --- src/unrealsdk/game/bl1/console.cpp | 59 ++++++++++++++++++------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 97e4726..0607d45 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -17,18 +17,19 @@ using namespace unrealsdk::unreal; namespace unrealsdk::game { -// TODO: Verify - -namespace { - // ############################################################################// // | CUSTOM COMMANDS | // ############################################################################// -// TODO: namespace these +namespace bl1::cmd { + void sset_command_impl(const wchar_t* args, size_t, size_t); void sgetnativefunc_command_impl(const wchar_t* args, size_t, size_t); +} // namespace bl1::cmd + +namespace { + // ############################################################################// // | DEFAULT CONSOLE HOOKS | // ############################################################################// @@ -172,8 +173,8 @@ void BL1Hook::inject_console(void) { ); // clang-format on - commands::add_command(L"sset", &sset_command_impl); - commands::add_command(L"sgetnativefunc", &sgetnativefunc_command_impl); + commands::add_command(L"sset", &bl1::cmd::sset_command_impl); + commands::add_command(L"sgetnativefunc", &bl1::cmd::sgetnativefunc_command_impl); } void BL1Hook::uconsole_output_text(const std::wstring& str) const { @@ -194,6 +195,12 @@ bool BL1Hook::is_console_ready(void) const { namespace { +// NOTE: src->get(prop) gives linker errors; we only need the value pointer here. +void* uproperty_get_value_ptr(UProperty* prop, UObject* src, int32_t index = 0) { + return reinterpret_cast(reinterpret_cast(src) + prop->Offset_Internal + + prop->ElementSize * index); +} + void uconsole_write(const std::wstring& text) { console_output_text.call(text); } @@ -204,18 +211,18 @@ void* uproperty_import_text(UProperty* prop, int32_t flags, UObject* src, void* err) { - // These are mostly speculative + // These are mostly speculative; I would hope the bitflags are the same... constexpr auto INDEX_PRE_PROP_CHANGED = 0x11; constexpr auto INDEX_POST_PROP_CHANGED = 0x13; constexpr auto INDEX_IMPORT_TEXT = 0x54; - constexpr auto FLAG_DONT_NOTIFY_OBJECT = 0x200; + constexpr auto RF_NeedInitialisation = 0x200; typedef void(__fastcall * uprop_changed)(UObject*, void*, UProperty*); typedef void*(__fastcall * uprop_import_text)(UProperty*, void*, const wchar_t*, void*, int32_t, UObject*, void*); // Pre OnChange? - if ((src->ObjectFlags & FLAG_DONT_NOTIFY_OBJECT) == 0) { + if ((src->ObjectFlags & RF_NeedInitialisation) == 0) { reinterpret_cast(src->vftable[INDEX_PRE_PROP_CHANGED])(src, nullptr, prop); } @@ -223,8 +230,24 @@ void* uproperty_import_text(UProperty* prop, uprop_import_text func = reinterpret_cast(prop->vftable[INDEX_IMPORT_TEXT]); void* result = func(prop, nullptr, value, write_to, flags, src, err); + // - NOTE - + // ```C + // if ((*(uint *)&piVar2->ObjectFlags & 0x4000) == 0) { + // ... + // if ((((*(uint *)&piVar2->ObjectFlags & lower_bits | this_upper_bits & upper_bits) == 0) && + // ((lower_bits & upper_bits) != 0xffffffff)) || ((this_upper_bits & 0x20000000) != 0)) { + // ``` + // This snippet would eventually cause a reload/reset on the object to defaults stored in the + // .upk we don't want that because we have just modified it and would like to keep our changes. + // So adding 0x4000 to the src->ObjectFlags stops 'src' from being reset. This probably + // introduces new issues or quirks though. Note that this snippet only flags the object for + // reloading. + // + // Ref: 0x005f988a, + src->ObjectFlags |= 0x4000; // RF_NewerVersionExists + // Post OnChange? - if ((src->ObjectFlags & FLAG_DONT_NOTIFY_OBJECT) == 0) { + if ((src->ObjectFlags & RF_NeedInitialisation) == 0) { reinterpret_cast(src->vftable[INDEX_POST_PROP_CHANGED])(src, nullptr, prop); } @@ -237,15 +260,7 @@ void* uproperty_import_text(UProperty* prop, // | CUSTOM CONSOLE COMMANDS | // ############################################################################// -namespace { - -// NOTE: src->get(prop) gives linker errors; we only need the value pointer here. -void* uproperty_get_value_ptr(UProperty* prop, UObject* src, int32_t index = 0) { - return reinterpret_cast(reinterpret_cast(src) + prop->Offset_Internal - + prop->ElementSize * index); -} - -void sset_command_impl(const wchar_t* line, size_t, size_t) { +void bl1::cmd::sset_command_impl(const wchar_t* line, size_t, size_t) { std::wstringstream ss{line}; // sset CLASS NAME PROPERTY VALUE @@ -283,7 +298,7 @@ void sset_command_impl(const wchar_t* line, size_t, size_t) { uproperty_import_text(prop, value.c_str(), value_ptr, 1, found, nullptr); } -void sgetnativefunc_command_impl(const wchar_t* line, size_t, size_t) { +void bl1::cmd::sgetnativefunc_command_impl(const wchar_t* line, size_t, size_t) { // - NOTE - // I don't expect this one to be used a lot its mostly for finding addresses of functions in // disassembly. The text output might not be useful since you can just add a breakpoint here @@ -324,8 +339,6 @@ void sgetnativefunc_command_impl(const wchar_t* line, size_t, size_t) { LOG(MISC, "[CONSOLE] ~ {}", msg); } -} // namespace - } // namespace unrealsdk::game #endif From d0df9b4d1f0455a3630562a21131eccccdf00bf1 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 16 Oct 2024 00:56:24 +0100 Subject: [PATCH 24/81] Shorten environment key names and add a key for hooking save package. --- src/unrealsdk/game/bl1/bl1.cpp | 76 ++++++++++++++++--------------- src/unrealsdk/game/bl1/bl1.h | 3 +- src/unrealsdk/game/bl1/object.cpp | 2 +- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 052cdf5..f53d354 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -35,6 +35,10 @@ void BL1Hook::hook(void) { hook_process_event(); hook_call_function(); + if (env::defined(KEY_LOG_SAVE_PKG)) { + hook_save_package(); + } + find_gobjects(); find_gnames(); find_fname_init(); @@ -148,48 +152,46 @@ namespace { // This is an function editor it might be useful much later on but right now I will leave it here // so that it is known. // -// The parameters into the function are not guaranteed to be correct I have verified that the -// UObjects are valid though. Unknown_XY are bitwise anded with constants as well. -// // NOLINTNEXTLINE(modernize-use-using) -typedef UObject* (*save_package_func)(UObject* InOuter, - UObject* InPackage, - wchar_t* Filename, - UObject* InTemplate, - uint32_t Unknown_00, - uint32_t Unknown_01, - uint32_t Unknown_02, - void* ErrorMessage, - wchar_t* FileExtension, - int32_t bSaveAsBinary, - int32_t bOnlyIfDirty); +typedef int32_t (*save_package_func)(UObject* InOuter, + UObject* Base, + int64_t TopLevelFlags, + wchar_t* Filename, + void* Error, // non-null + void* Conform, + bool bForceByteSwapping, + bool bWarnOfLongFilename, + uint32_t SaveFlags, + UObject* TargetPlatform, // ? + void* FinalTimeStamp, + int Unknown_00 /* pointer? */); save_package_func save_package_ptr = nullptr; -const constinit Pattern<80> SAVE_PACKAGE_SIG{ - "6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC 30 09 00 00 A1 ?? ?? ?? ?? 33 C4 89 84 24 2C" - " 09 00 00 53 55 56 57 A1 ?? ?? ?? ?? 33 C4 50 8D 84 24 44 09 00 00 64 A3 00 00 00 00 8B 84 24" - " 6C 09 00 00 8B BC 24 54 09 00 00 8B AC 24 58 09 00 00"}; - -UObject* hook_save_package_detour(UObject* InOuter, - UObject* InPackage, - wchar_t* Filename, - UObject* InTemplate, - uint32_t Flags_00, - uint32_t Flags_01, - uint32_t Unknown_02, - void* ErrorMessage, - wchar_t* FileExtension, - int32_t bSaveAsBinary, - int32_t bOnlyIfDirty) { - LOG(MISC, "[SAVE_PACKAGE] ~ {:p}, {:p}, {:p}, {:p}", (void*)InOuter, (void*)InPackage, - (void*)Filename, (void*)InTemplate); - - UObject* result = - save_package_ptr(InOuter, InPackage, Filename, InTemplate, Flags_00, Flags_01, Unknown_02, - ErrorMessage, FileExtension, bSaveAsBinary, bOnlyIfDirty); - +const constinit Pattern<51> SAVE_PACKAGE_SIG{ + "55 8D AC 24 D4 F3 FF FF 81 EC 2C 0C 00 00 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC A0" + "03 00 00 A1 ?? ?? ?? ?? 33 C5 89 85 28 0C 00 00 53 56 57 50"}; + +int32_t hook_save_package_detour(UObject* InOuter, + UObject* Base, + int64_t TopLevelFlags, + wchar_t* Filename, + void* Error, + void* Conform, + bool bForceByteSwapping, + bool bWarnOfLongFilename, + uint32_t SaveFlags, + UObject* TargetPlatform, + void* FinalTimeStamp, + int Unknown_00) { + LOG(MISC, L"Saving Package: {:p}, {:p}, {:#016x}, {:#08x}, '{}'", + reinterpret_cast(InOuter), reinterpret_cast(Base), TopLevelFlags, SaveFlags, + Filename); + + int32_t result = save_package_ptr(InOuter, Base, TopLevelFlags, Filename, Error, Conform, + bForceByteSwapping, bWarnOfLongFilename, SaveFlags, + TargetPlatform, FinalTimeStamp, Unknown_00); return result; } diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 08b24cf..5d9387a 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -15,7 +15,8 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { public: - static const constexpr env::env_var_key KEY_LOG_LOAD_PACKAGE = "UNREALSDK_BL1_LOG_LOAD_PKG"; + static const constexpr env::env_var_key KEY_LOG_LOAD_PKG = "UNREALSDK_BL1_LOG_LOAD_PKG"; + static const constexpr env::env_var_key KEY_LOG_SAVE_PKG = "UNREALSDK_BL1_LOG_SAVE_PKG"; protected: /** diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp index 3091668..4a1cc01 100644 --- a/src/unrealsdk/game/bl1/object.cpp +++ b/src/unrealsdk/game/bl1/object.cpp @@ -206,7 +206,7 @@ UObject* bl1_load_package_detour(const UObject* outer, const wchar_t* name, uint } // namespace void BL1Hook::find_load_package(void) { - if (env::defined(KEY_LOG_LOAD_PACKAGE)) { + if (env::defined(KEY_LOG_LOAD_PKG)) { detour(LOAD_PACKAGE_PATTERN, &bl1_load_package_detour, &load_package_ptr, "bl1_load_package_detour"); } else { From bcfb6db710f50d61e140c5a286fbfa748d7b4b87 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Wed, 16 Oct 2024 02:15:18 +0100 Subject: [PATCH 25/81] Added a hook to log an error message generator. --- src/unrealsdk/game/bl1/bl1.cpp | 40 +++++++++++++++++++++++++++++++++- src/unrealsdk/game/bl1/bl1.h | 4 ++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index f53d354..4b17343 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -26,6 +26,7 @@ namespace unrealsdk::game { // These could be defined in the class but since they are only used here this will do for now. namespace { void hook_save_package(void); +void hook_resolve_error(void); void inject_qol_hooks(void); } // namespace @@ -39,6 +40,10 @@ void BL1Hook::hook(void) { hook_save_package(); } + if (env::defined(KEY_LOG_EXTENDED_DBG)) { + hook_resolve_error(); + } + find_gobjects(); find_gnames(); find_fname_init(); @@ -158,7 +163,7 @@ typedef int32_t (*save_package_func)(UObject* InOuter, UObject* Base, int64_t TopLevelFlags, wchar_t* Filename, - void* Error, // non-null + void* Error, // non-null void* Conform, bool bForceByteSwapping, bool bWarnOfLongFilename, @@ -201,6 +206,39 @@ void hook_save_package(void) { } // namespace +// ############################################################################// +// | EXTENDED DEBUGGING | +// ############################################################################// + +namespace { + +// - NOTE - +// I've seen this function get called with static strings quite a fair bit in ghidra could be useful +// for identifying soft/critical errors. +// + +const constinit Pattern<28> EXTENDED_DEBUGGING_SIG{ + "51 8B 44 24 14 8B 4C 24 10 8B 54 24 0C 56 8B 74 24 0C 6A 00 50 51 52 68 ?? ?? ?? ??"}; + +// NOLINTNEXTLINE(modernize-use-using) +typedef wchar_t**(__cdecl* resolve_error)(wchar_t**, wchar_t*, wchar_t*, int32_t); + +resolve_error resolve_error_ptr = nullptr; + +wchar_t** resolve_error_detour(wchar_t** obj, wchar_t* error, wchar_t* ctx, int32_t flags) { + wchar_t** msg = resolve_error_ptr(obj, error, ctx, flags); + // [RESOLVE_ERR] Core::ObjectNotFound | 0x0 'Object not found ...' + LOG(WARNING, L"[RESOLVE_ERR] {}::{} | {:#08x} '{}'", ctx, error, flags, *msg); + return msg; +} + +void hook_resolve_error(void) { + detour(EXTENDED_DEBUGGING_SIG, &resolve_error_detour, &resolve_error_ptr, + "bl1_hook_raise_error"); +} + +} // namespace + // ############################################################################// // | CUSTOM HOOKS | // ############################################################################// diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 5d9387a..b87d2f3 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -3,9 +3,9 @@ #include "unrealsdk/pch.h" +#include "unrealsdk/env.h" #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#include "unrealsdk/env.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ && defined(UNREALSDK_GAME_BL1) @@ -13,10 +13,10 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { - public: static const constexpr env::env_var_key KEY_LOG_LOAD_PKG = "UNREALSDK_BL1_LOG_LOAD_PKG"; static const constexpr env::env_var_key KEY_LOG_SAVE_PKG = "UNREALSDK_BL1_LOG_SAVE_PKG"; + static const constexpr env::env_var_key KEY_LOG_EXTENDED_DBG = "UNREALSDK_BL1_LOG_EXTENDED_DBG"; protected: /** From acb8d63f2aefd80021504e73c76db4a317dba73a Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:06:56 +0100 Subject: [PATCH 26/81] Added a hook into an initialisation function which can be used to delay sdk initialisation. --- src/unrealsdk/game/bl1/bl1.cpp | 59 ++++++++++++++++++++++++++++++++++ src/unrealsdk/game/bl1/bl1.h | 5 +++ 2 files changed, 64 insertions(+) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 4b17343..be3cd4a 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -25,14 +25,38 @@ namespace unrealsdk::game { // These could be defined in the class but since they are only used here this will do for now. namespace { +static std::atomic_bool bl1_has_initialised{false}; + void hook_save_package(void); void hook_resolve_error(void); void inject_qol_hooks(void); +void hook_init_func(void); } // namespace void BL1Hook::hook(void) { hook_antidebug(); + if (!env::defined(KEY_DO_NOT_WAIT_FOR_INIT)) { + hook_init_func(); + // Shouldn't take longer than 60s tbh but just incase someones pc is exceptionally slow. + const float MAX_WAIT_TIME = env::get_numeric(KEY_MAX_WAIT_TIME, 120.0F); + using Clock = std::chrono::steady_clock; + auto start = Clock::now(); + + // Wait until the game has initialised or until we timeout + while (!bl1_has_initialised.load(std::memory_order_relaxed)) { + // yielding is an option but this is probably better + std::this_thread::sleep_for(std::chrono::milliseconds{100}); + + float elapsed = std::chrono::duration(Clock::now() - start).count(); + if (elapsed > MAX_WAIT_TIME) { + LOG(INFO, + "bl1sdk is aborting initialisation as it has taken too long to initialise."); + return; + } + } + } + hook_process_event(); hook_call_function(); @@ -258,6 +282,41 @@ void inject_qol_hooks(void) { &hook_instantly_load_profile); } +const constinit Pattern<45> INIT_FUNC_SIG{ + "6A FF" // push FFFFFFFF + "68 ????????" // push + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 3C" // sub esp,3C + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F16980] + "33C4" // xor eax,esp + "50" // push eax + "8D4424 50" // lea eax,dword ptr ss:[esp+50] + "64A3 00000000" // mov dword ptr fs:[0],eax + "8BD9" // mov ebx,ecx + "EB 0E" // jmp borderlands.13ADAC9 + "4D" // dec ebp + "61" // popad +}; + +// Not sure if this is a __thiscall or an actual __fastcall; ecx is used. +typedef void(__fastcall* init_function)(void* ecx, void* edx); + +init_function init_func_ptr = nullptr; + +void __fastcall detour_init_func(void* ecx, void* edx) { + init_func_ptr(ecx, edx); + bl1_has_initialised.store(true, std::memory_order_relaxed); +} + +void hook_init_func(void) { + detour(INIT_FUNC_SIG, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); +} + } // namespace // ############################################################################// diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index b87d2f3..8c1c65b 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -18,6 +18,11 @@ class BL1Hook : public AbstractHook { static const constexpr env::env_var_key KEY_LOG_SAVE_PKG = "UNREALSDK_BL1_LOG_SAVE_PKG"; static const constexpr env::env_var_key KEY_LOG_EXTENDED_DBG = "UNREALSDK_BL1_LOG_EXTENDED_DBG"; + // I can't see this one ever being used tbh + static const constexpr env::env_var_key KEY_DO_NOT_WAIT_FOR_INIT = + "UNREALSDK_BL1_DO_NOT_WAIT_FOR_INIT"; + static const constexpr env::env_var_key KEY_MAX_WAIT_TIME = "UNREALSDK_BL1_MAX_INIT_WAIT_TIME"; + protected: /** * @brief Hex edits out the `obj dump` array limit message. From bf7c40917883d26b27aa827aa358331f9f7445d6 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:49:47 +0100 Subject: [PATCH 27/81] Remove unneeded QoL hook function. --- src/unrealsdk/game/bl1/bl1.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index be3cd4a..9b090c4 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -29,7 +29,6 @@ static std::atomic_bool bl1_has_initialised{false}; void hook_save_package(void); void hook_resolve_error(void); -void inject_qol_hooks(void); void hook_init_func(void); } // namespace @@ -86,7 +85,6 @@ void BL1Hook::hook(void) { void BL1Hook::post_init(void) { LOG(MISC, "Attaching Hooks!"); inject_console(); - inject_qol_hooks(); } // ############################################################################// @@ -269,19 +267,6 @@ void hook_resolve_error(void) { namespace { -bool hook_instantly_load_profile(hook_manager::Details& in) { - // bIsProfileLoaded is set to true after 30 seconds; This sets it to true once the warp-tunnel - // has finished. This allows you to instantly save on quit rather than having to wait 30s. - in.obj->get(L"ClientSetProfileLoaded"_fn).call(); - return false; -} - -void inject_qol_hooks(void) { - hook_manager::add_hook(L"WillowGame.WillowPlayerController:SpawningProcessComplete", - hook_manager::Type::POST, L"bl1_hook_instantly_load_profile", - &hook_instantly_load_profile); -} - const constinit Pattern<45> INIT_FUNC_SIG{ "6A FF" // push FFFFFFFF "68 ????????" // push @@ -310,6 +295,7 @@ init_function init_func_ptr = nullptr; void __fastcall detour_init_func(void* ecx, void* edx) { init_func_ptr(ecx, edx); + // When this is true the unrealscript game engine has been created bl1_has_initialised.store(true, std::memory_order_relaxed); } From 0c921cf5e97bec97a985d0d358d143bc3cda7c72 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:55:17 +0100 Subject: [PATCH 28/81] Added an option to enable locking in the uconsole_output_text function. --- src/unrealsdk/game/bl1/bl1.h | 2 ++ src/unrealsdk/game/bl1/console.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 8c1c65b..d89a787 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -22,6 +22,8 @@ class BL1Hook : public AbstractHook { static const constexpr env::env_var_key KEY_DO_NOT_WAIT_FOR_INIT = "UNREALSDK_BL1_DO_NOT_WAIT_FOR_INIT"; static const constexpr env::env_var_key KEY_MAX_WAIT_TIME = "UNREALSDK_BL1_MAX_INIT_WAIT_TIME"; + static const constexpr env::env_var_key KEY_LOCKING_CONSOLE_WRITE = + "UNREALSDK_BL1_LOCKING_CONSOLE_WRITE"; protected: /** diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 0607d45..35cc0f8 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -182,7 +182,14 @@ void BL1Hook::uconsole_output_text(const std::wstring& str) const { return; } - console_output_text.call(str); + if (env::defined(KEY_LOCKING_CONSOLE_WRITE)) { + static std::mutex s_Mutex{}; + std::unique_lock guard{s_Mutex}; + console_output_text.call(str); + + } else { + console_output_text.call(str); + } } bool BL1Hook::is_console_ready(void) const { @@ -244,7 +251,7 @@ void* uproperty_import_text(UProperty* prop, // reloading. // // Ref: 0x005f988a, - src->ObjectFlags |= 0x4000; // RF_NewerVersionExists + src->ObjectFlags |= 0x4000; // RF_NewerVersionExists // Post OnChange? if ((src->ObjectFlags & RF_NeedInitialisation) == 0) { From 972708fc7fa1b425f6b15b715f6d282ae810e50b Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:56:51 +0100 Subject: [PATCH 29/81] Disable logging in resolve_error_detour temporarily. --- src/unrealsdk/game/bl1/bl1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 9b090c4..29e70ca 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -250,7 +250,7 @@ resolve_error resolve_error_ptr = nullptr; wchar_t** resolve_error_detour(wchar_t** obj, wchar_t* error, wchar_t* ctx, int32_t flags) { wchar_t** msg = resolve_error_ptr(obj, error, ctx, flags); // [RESOLVE_ERR] Core::ObjectNotFound | 0x0 'Object not found ...' - LOG(WARNING, L"[RESOLVE_ERR] {}::{} | {:#08x} '{}'", ctx, error, flags, *msg); + // LOG(WARNING, L"[RESOLVE_ERR] {}::{} | {:#08x} '{}'", ctx, error, flags, *msg); return msg; } From 68d9698901fa4fd4e1cc757ddfd2736f798f5ec1 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Fri, 25 Oct 2024 22:29:44 +0100 Subject: [PATCH 30/81] Add Steam signature for the init function as it differs slightly. --- src/unrealsdk/game/bl1/bl1.cpp | 45 +++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 29e70ca..06707bc 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -267,7 +267,39 @@ void hook_resolve_error(void) { namespace { -const constinit Pattern<45> INIT_FUNC_SIG{ +// - NOTE - +// The init function is only used to delay initialisation of the SDK to ensure that we can proceed +// with injection at the right time. However, the Steams version of this function is different +// the core functionality though is the same. So we will try the UDK version and then the Steam +// version. If both fail then we won't crash but it will be a 'Silent' error for most users which +// is not ideal. + +const constinit Pattern<64> INIT_FUNC_STEAM_SIG{ + "6A FF" // push FFFFFFFF + "68 ????????" // push borderlands.198D998 + "64A1 00000000" // mov eax,dword ptr fs:[0] + "50" // push eax + "83EC 3C" // sub esp,3C + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax,dword ptr ds:[1F131C0] + "33C4" // xor eax,esp + "50" // push eax + "8D4424 50" // lea eax,dword ptr ss:[esp+50] + "64A3 00000000" // mov dword ptr fs:[0],eax + "8BD9" // mov ebx,ecx + "68 ????????" // push borderlands.1CC3E78 + "E8 ????????" // call borderlands.5C20F0 + "50" // push eax + "E8 ????????" // call borderlands.5C2A80 + "83C4 08" // add esp,8 + "85C0" // test eax,eax + "74 ??" // je borderlands.138F06A +}; + +const constinit Pattern<45> INIT_FUNC_141_UDK_SIG{ "6A FF" // push FFFFFFFF "68 ????????" // push "64A1 00000000" // mov eax,dword ptr fs:[0] @@ -283,7 +315,7 @@ const constinit Pattern<45> INIT_FUNC_SIG{ "8D4424 50" // lea eax,dword ptr ss:[esp+50] "64A3 00000000" // mov dword ptr fs:[0],eax "8BD9" // mov ebx,ecx - "EB 0E" // jmp borderlands.13ADAC9 + "EB ??" // jmp borderlands.13ADAC9 "4D" // dec ebp "61" // popad }; @@ -300,7 +332,14 @@ void __fastcall detour_init_func(void* ecx, void* edx) { } void hook_init_func(void) { - detour(INIT_FUNC_SIG, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + uintptr_t udk_addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); + if (udk_addr == uintptr_t{0}) { + LOG(INFO, "Attempting to hook Steam init function..."); + detour(INIT_FUNC_STEAM_SIG, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + } else { + LOG(INFO, "Attempting to hook UDK init function..."); + detour(udk_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + } } } // namespace From dbebacccb84c749a568f435023db5dc415683325 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 26 Oct 2024 10:47:46 +0100 Subject: [PATCH 31/81] Init function hooking will now try multiple times before eventually aborting. --- src/unrealsdk/game/bl1/bl1.cpp | 48 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 06707bc..2ada386 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -332,13 +332,47 @@ void __fastcall detour_init_func(void* ecx, void* edx) { } void hook_init_func(void) { - uintptr_t udk_addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); - if (udk_addr == uintptr_t{0}) { - LOG(INFO, "Attempting to hook Steam init function..."); - detour(INIT_FUNC_STEAM_SIG, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); - } else { - LOG(INFO, "Attempting to hook UDK init function..."); - detour(udk_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + // - NOTE - + // This is like this because the Steam version has different loading logic which causes the + // offsets to not be found. Now fundementally this is a problem of microseconds, as in, by + // compiling in debug mode the hook will work all the time. So, the optimisations provided in + // release mode are to cause the hooks to fail. So to stop this we will try to hook more + // than once with a small delay between each attempt. We could disable optimisations here but + // thats just stupid lol. + // + // This way also has the added benefit of working even if you launch the game from outside of + // Steam however I still wouldn't recommend it. + // + constexpr size_t MAX_ATTEMPTS = 10; + constexpr size_t DEFAULT_DELAY = 100; + constexpr uintptr_t INVALID_ADDR{0}; + + for (size_t i = 0; i < MAX_ATTEMPTS; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_DELAY)); + uintptr_t udk_addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); + uintptr_t steam_addr = INIT_FUNC_STEAM_SIG.sigscan_nullable(); + + // This should never happen but if it does we will want to know + if (udk_addr != INVALID_ADDR && steam_addr != INVALID_ADDR) { + LOG(ERROR, + "Found the UDK init function signature and the Steam init function..." + " That shouldn't be possible..."); + continue; + } + + // Hook UDK + if (udk_addr != INVALID_ADDR) { + LOG(INFO, "Found UDK Init function at {:#016x} after {} attempts", udk_addr, i); + detour(udk_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + return; + } + + // Hook Steam + if (steam_addr != INVALID_ADDR) { + LOG(INFO, "Found Steam Init function at {:#016x} after {} attempts", steam_addr, i); + detour(steam_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + return; + } } } From da32165d30e08b045edd73aac7a764fb690a0dc4 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:32:10 +0000 Subject: [PATCH 32/81] Added extra logging information for when a detour fails. --- src/unrealsdk/memory.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/memory.cpp b/src/unrealsdk/memory.cpp index fb90881..fa6b3a2 100644 --- a/src/unrealsdk/memory.cpp +++ b/src/unrealsdk/memory.cpp @@ -93,13 +93,15 @@ bool detour(uintptr_t addr, void* detour_func, void** original_func, std::string status = MH_CreateHook(reinterpret_cast(addr), detour_func, original_func); if (status != MH_OK) { - LOG(ERROR, "Failed to create detour for {}", name); + const char* error = MH_StatusToString(status); + LOG(ERROR, "Failed to create detour for '{}'; With error: '{}'", name, error); return false; } status = MH_EnableHook(reinterpret_cast(addr)); if (status != MH_OK) { - LOG(ERROR, "Failed to enable detour for {}", name); + const char* error = MH_StatusToString(status); + LOG(ERROR, "Failed to enable detour for '{}'; With error: '{}'", name, error); return false; } From de54b99f3b64af5abf3f7ea3de3d46888d7e7642 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:33:17 +0000 Subject: [PATCH 33/81] Hooking the init function should be a bit more consistent now. --- src/unrealsdk/game/bl1/bl1.cpp | 81 ++++++++++++++-------------------- src/unrealsdk/game/bl1/bl1.h | 4 ++ 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 2ada386..70d4402 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -12,34 +12,28 @@ using namespace unrealsdk::memory; using namespace unrealsdk::unreal; -// - NOTE - -// The offsets are for the BL1 1.4.1 UDK version of the game. I did a quick scan for the -// ProccessEvent pattern in the Steam version and it did find the correct function however I -// didn't check anymore than that; other than to see if FFrame::Step was also inlined, it is. -// -// I have done no work and put no effort into seeing if anything here can be used for the Enhanced -// version of the game. -// - namespace unrealsdk::game { // These could be defined in the class but since they are only used here this will do for now. namespace { static std::atomic_bool bl1_has_initialised{false}; +using Clock = std::chrono::steady_clock; +static Clock::time_point init_start{}; + void hook_save_package(void); void hook_resolve_error(void); void hook_init_func(void); } // namespace void BL1Hook::hook(void) { + init_start = Clock::now(); hook_antidebug(); if (!env::defined(KEY_DO_NOT_WAIT_FOR_INIT)) { hook_init_func(); // Shouldn't take longer than 60s tbh but just incase someones pc is exceptionally slow. const float MAX_WAIT_TIME = env::get_numeric(KEY_MAX_WAIT_TIME, 120.0F); - using Clock = std::chrono::steady_clock; auto start = Clock::now(); // Wait until the game has initialised or until we timeout @@ -270,9 +264,7 @@ namespace { // - NOTE - // The init function is only used to delay initialisation of the SDK to ensure that we can proceed // with injection at the right time. However, the Steams version of this function is different -// the core functionality though is the same. So we will try the UDK version and then the Steam -// version. If both fail then we won't crash but it will be a 'Silent' error for most users which -// is not ideal. +// the core functionality though is the same. const constinit Pattern<64> INIT_FUNC_STEAM_SIG{ "6A FF" // push FFFFFFFF @@ -326,6 +318,8 @@ typedef void(__fastcall* init_function)(void* ecx, void* edx); init_function init_func_ptr = nullptr; void __fastcall detour_init_func(void* ecx, void* edx) { + auto elapsed = std::chrono::duration(Clock::now() - init_start).count(); + LOG(INFO, "Init function called {:.6f}s after initialisation", elapsed); init_func_ptr(ecx, edx); // When this is true the unrealscript game engine has been created bl1_has_initialised.store(true, std::memory_order_relaxed); @@ -333,44 +327,37 @@ void __fastcall detour_init_func(void* ecx, void* edx) { void hook_init_func(void) { // - NOTE - - // This is like this because the Steam version has different loading logic which causes the - // offsets to not be found. Now fundementally this is a problem of microseconds, as in, by - // compiling in debug mode the hook will work all the time. So, the optimisations provided in - // release mode are to cause the hooks to fail. So to stop this we will try to hook more - // than once with a small delay between each attempt. We could disable optimisations here but - // thats just stupid lol. + // We have around 1 to 2 seconds to hook this function before we miss our chance. // - // This way also has the added benefit of working even if you launch the game from outside of - // Steam however I still wouldn't recommend it. - // - constexpr size_t MAX_ATTEMPTS = 10; - constexpr size_t DEFAULT_DELAY = 100; - constexpr uintptr_t INVALID_ADDR{0}; - - for (size_t i = 0; i < MAX_ATTEMPTS; ++i) { - std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_DELAY)); - uintptr_t udk_addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); - uintptr_t steam_addr = INIT_FUNC_STEAM_SIG.sigscan_nullable(); - - // This should never happen but if it does we will want to know - if (udk_addr != INVALID_ADDR && steam_addr != INVALID_ADDR) { - LOG(ERROR, - "Found the UDK init function signature and the Steam init function..." - " That shouldn't be possible..."); - continue; - } - - // Hook UDK - if (udk_addr != INVALID_ADDR) { - LOG(INFO, "Found UDK Init function at {:#016x} after {} attempts", udk_addr, i); - detour(udk_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + constexpr uintptr_t INVALID_ADDRESS{0}; + + // Note: The time requested is still constrained to the OS; i.e., min on windows is 16ms + auto delay = std::chrono::milliseconds{ + std::max(env::get_numeric(BL1Hook::KEY_INIT_FUNC_POLL_RATE, 20), 10)}; + const auto max_attempts = 5000LL / delay.count(); + LOG(INFO, "Attempting to hook init function with polling rate: {}ms", delay.count()); + + // Will try for a bit + for (int i = 0; i < max_attempts; ++i) { + std::this_thread::sleep_for(delay); + + // Steam + uintptr_t addr = INIT_FUNC_STEAM_SIG.sigscan_nullable(); + if (addr != INVALID_ADDRESS) { + bool ok = detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_steam_init_func"); + if (!ok) { + continue; + } return; } - // Hook Steam - if (steam_addr != INVALID_ADDR) { - LOG(INFO, "Found Steam Init function at {:#016x} after {} attempts", steam_addr, i); - detour(steam_addr, &detour_init_func, &init_func_ptr, "bl1_hook_init_func"); + // UDK 141 + addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); + if (addr != INVALID_ADDRESS) { + bool ok = detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_udk_init_func"); + if (!ok) { + continue; + } return; } } diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index d89a787..5e5326e 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -25,6 +25,10 @@ class BL1Hook : public AbstractHook { static const constexpr env::env_var_key KEY_LOCKING_CONSOLE_WRITE = "UNREALSDK_BL1_LOCKING_CONSOLE_WRITE"; + // Polling rate in milliseconds + static const constexpr env::env_var_key KEY_INIT_FUNC_POLL_RATE = + "UNREALSDK_BL1_INIT_FUNC_POLL_RATE"; + protected: /** * @brief Hex edits out the `obj dump` array limit message. From 65b8e0ece619a2fcf76b87c02dd63834b3951b8f Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:34:01 +0000 Subject: [PATCH 34/81] Use a recursive mutex for locking console writes. --- src/unrealsdk/game/bl1/console.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 35cc0f8..8b0077a 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -183,8 +183,8 @@ void BL1Hook::uconsole_output_text(const std::wstring& str) const { } if (env::defined(KEY_LOCKING_CONSOLE_WRITE)) { - static std::mutex s_Mutex{}; - std::unique_lock guard{s_Mutex}; + static std::recursive_mutex s_Mutex{}; + std::lock_guard guard{s_Mutex}; console_output_text.call(str); } else { From f981faf0bb5835a9b491e717672233bc3b282aee Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sun, 24 Nov 2024 16:46:03 +0000 Subject: [PATCH 35/81] Update to new toml configuration system. --- src/unrealsdk/game/bl1/bl1.cpp | 83 +++++++++++++++------------ src/unrealsdk/game/bl1/bl1.h | 19 +----- src/unrealsdk/game/bl1/bl1_config.cpp | 65 +++++++++++++++++++++ src/unrealsdk/game/bl1/bl1_config.h | 28 +++++++++ src/unrealsdk/game/bl1/console.cpp | 7 +-- src/unrealsdk/game/bl1/hexedits.cpp | 4 +- src/unrealsdk/game/bl1/hooks.cpp | 3 +- src/unrealsdk/game/bl1/object.cpp | 2 +- 8 files changed, 147 insertions(+), 64 deletions(-) create mode 100644 src/unrealsdk/game/bl1/bl1_config.cpp create mode 100644 src/unrealsdk/game/bl1/bl1_config.h diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 70d4402..3da4f6d 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -16,64 +16,59 @@ namespace unrealsdk::game { // These could be defined in the class but since they are only used here this will do for now. namespace { -static std::atomic_bool bl1_has_initialised{false}; + +std::atomic_bool bl1_has_initialised{false}; using Clock = std::chrono::steady_clock; -static Clock::time_point init_start{}; void hook_save_package(void); void hook_resolve_error(void); -void hook_init_func(void); +bool hook_init_func(void); } // namespace void BL1Hook::hook(void) { - init_start = Clock::now(); hook_antidebug(); - if (!env::defined(KEY_DO_NOT_WAIT_FOR_INIT)) { - hook_init_func(); - // Shouldn't take longer than 60s tbh but just incase someones pc is exceptionally slow. - const float MAX_WAIT_TIME = env::get_numeric(KEY_MAX_WAIT_TIME, 120.0F); - auto start = Clock::now(); - - // Wait until the game has initialised or until we timeout - while (!bl1_has_initialised.load(std::memory_order_relaxed)) { - // yielding is an option but this is probably better - std::this_thread::sleep_for(std::chrono::milliseconds{100}); - - float elapsed = std::chrono::duration(Clock::now() - start).count(); - if (elapsed > MAX_WAIT_TIME) { - LOG(INFO, - "bl1sdk is aborting initialisation as it has taken too long to initialise."); - return; - } - } + if (!hook_init_func()) { + return; } hook_process_event(); hook_call_function(); - if (env::defined(KEY_LOG_SAVE_PKG)) { + if (bl1_cfg::is_log_save_package()) { hook_save_package(); } - if (env::defined(KEY_LOG_EXTENDED_DBG)) { + // A lot of these types of functions don't belong here and can be implemented as native python + // modules. That will happen eventually. + if (false) { hook_resolve_error(); } - find_gobjects(); - find_gnames(); + // Grabbing these asap seems fine find_fname_init(); find_fframe_step(); - find_gmalloc(); find_construct_object(); find_get_path_name(); find_static_find_object(); find_load_package(); + // idk if these are required or if they are correct hexedit_set_command(); hexedit_array_limit(); hexedit_array_limit_message(); + + // This ensures that the unrealscript is initialised when we exit/return + while (!bl1_has_initialised.load(std::memory_order_relaxed)) { + std::this_thread::sleep_for(std::chrono::milliseconds{50}); + } + + // Delayed until after initialisation to ensure we grab valid data; Should be able to avoid this + // and grab earlier if we grab a pointer to the global not the value held by the global. + find_gobjects(); + find_gnames(); + find_gmalloc(); } void BL1Hook::post_init(void) { @@ -232,6 +227,7 @@ namespace { // I've seen this function get called with static strings quite a fair bit in ghidra could be useful // for identifying soft/critical errors. // +// Disabled for now; -Ry const constinit Pattern<28> EXTENDED_DEBUGGING_SIG{ "51 8B 44 24 14 8B 4C 24 10 8B 54 24 0C 56 8B 74 24 0C 6A 00 50 51 52 68 ?? ?? ?? ??"}; @@ -318,27 +314,31 @@ typedef void(__fastcall* init_function)(void* ecx, void* edx); init_function init_func_ptr = nullptr; void __fastcall detour_init_func(void* ecx, void* edx) { - auto elapsed = std::chrono::duration(Clock::now() - init_start).count(); - LOG(INFO, "Init function called {:.6f}s after initialisation", elapsed); + using std::chrono::duration; + auto start = Clock::now(); + LOG(INFO, "Init function called"); init_func_ptr(ecx, edx); + LOG(INFO, "Init function took {}s to execute", duration(Clock::now() - start).count()); + // When this is true the unrealscript game engine has been created bl1_has_initialised.store(true, std::memory_order_relaxed); } -void hook_init_func(void) { +bool hook_init_func(void) { // - NOTE - - // We have around 1 to 2 seconds to hook this function before we miss our chance. + // I don't think is actually has to be in a loop. // constexpr uintptr_t INVALID_ADDRESS{0}; + constexpr float INIT_FUNC_TIMEOUT_SECONDS{15.0F}; // Note: The time requested is still constrained to the OS; i.e., min on windows is 16ms - auto delay = std::chrono::milliseconds{ - std::max(env::get_numeric(BL1Hook::KEY_INIT_FUNC_POLL_RATE, 20), 10)}; - const auto max_attempts = 5000LL / delay.count(); + // this should just be 'as fast as possible' so 20ms for all systems should suffice. + auto delay = std::chrono::milliseconds{bl1_cfg::init_func_poll_rate_ms()}; LOG(INFO, "Attempting to hook init function with polling rate: {}ms", delay.count()); - // Will try for a bit - for (int i = 0; i < max_attempts; ++i) { + auto start = Clock::now(); + + while (true) { std::this_thread::sleep_for(delay); // Steam @@ -348,7 +348,7 @@ void hook_init_func(void) { if (!ok) { continue; } - return; + return true; } // UDK 141 @@ -358,7 +358,14 @@ void hook_init_func(void) { if (!ok) { continue; } - return; + return true; + } + + // This should never really be hit adding it just incase though + using std::chrono::duration; + if (duration(Clock::now() - start).count() > INIT_FUNC_TIMEOUT_SECONDS) { + LOG(ERROR, "It has taken too long to hook the init function; Aborting..."); + return false; } } } diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 5e5326e..6db486e 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -3,7 +3,7 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/env.h" +#include "unrealsdk/game/bl1/bl1_config.h" #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" @@ -13,21 +13,6 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { - public: - static const constexpr env::env_var_key KEY_LOG_LOAD_PKG = "UNREALSDK_BL1_LOG_LOAD_PKG"; - static const constexpr env::env_var_key KEY_LOG_SAVE_PKG = "UNREALSDK_BL1_LOG_SAVE_PKG"; - static const constexpr env::env_var_key KEY_LOG_EXTENDED_DBG = "UNREALSDK_BL1_LOG_EXTENDED_DBG"; - - // I can't see this one ever being used tbh - static const constexpr env::env_var_key KEY_DO_NOT_WAIT_FOR_INIT = - "UNREALSDK_BL1_DO_NOT_WAIT_FOR_INIT"; - static const constexpr env::env_var_key KEY_MAX_WAIT_TIME = "UNREALSDK_BL1_MAX_INIT_WAIT_TIME"; - static const constexpr env::env_var_key KEY_LOCKING_CONSOLE_WRITE = - "UNREALSDK_BL1_LOCKING_CONSOLE_WRITE"; - - // Polling rate in milliseconds - static const constexpr env::env_var_key KEY_INIT_FUNC_POLL_RATE = - "UNREALSDK_BL1_INIT_FUNC_POLL_RATE"; protected: /** @@ -38,7 +23,7 @@ class BL1Hook : public AbstractHook { /** * @brief Finds `FName::Init`, and sets up such that `fname_init` may be called. */ - void find_fname_init(void); + static void find_fname_init(void); /** * @brief Hooks the antidebug functions and disables them. diff --git a/src/unrealsdk/game/bl1/bl1_config.cpp b/src/unrealsdk/game/bl1/bl1_config.cpp new file mode 100644 index 0000000..70e9e01 --- /dev/null +++ b/src/unrealsdk/game/bl1/bl1_config.cpp @@ -0,0 +1,65 @@ +// +// Date : 24/11/2024 +// Project : unrealsdk +// Author : -Ry +// + +#include "unrealsdk/game/bl1/bl1_config.h" +#include "unrealsdk/config.h" + +namespace unrealsdk::game::bl1_cfg { + +// ############################################################################// +// | TOML DEF | +// ############################################################################// + +namespace { +using TomlKeyType = std::string_view; + +// clang-format off + +// USDK +constexpr TomlKeyType CONSOLE_KEY = "unrealsdk.console_key"; +constexpr TomlKeyType LOCKING_PROCESS_EVENT = "unrealsdk.locking_process_event"; + +// BL1SDK +constexpr TomlKeyType LOG_LOAD_PACKAGE = "unrealsdk.bl1.log_load_package"; +constexpr TomlKeyType LOG_SAVE_PACKAGE = "unrealsdk.bl1.log_save_package"; +constexpr TomlKeyType LOCKING_CONSOLE_WRITE = "unrealsdk.bl1.locking_console_write"; + +constexpr TomlKeyType INIT_FUNC_POLL_RATE_MS = "unrealsdk.bl1.init_func_poll_rate_ms"; +constexpr int64_t DEFAULT_POLL_RATE_MS = 20; + +// clang-format on + +} // namespace + +// ############################################################################// +// | FUNCTION IMPLS | +// ############################################################################// + +std::string console_key(void) { + return std::string{config::get_str("unrealsdk.console_key").value_or("Tilde")}; +} + +bool is_locking_console_write(void) { + return config::get_bool(LOCKING_CONSOLE_WRITE).value_or(false); +} + +bool is_locking_process_event(void) { + return config::get_bool(LOCKING_PROCESS_EVENT).value_or(false); +} + +bool is_log_load_package(void) { + return config::get_bool(LOG_LOAD_PACKAGE).value_or(false); +} + +bool is_log_save_package(void) { + return config::get_bool(LOG_LOAD_PACKAGE).value_or(false); +} + +int64_t init_func_poll_rate_ms(void) { + return config::get_int(INIT_FUNC_POLL_RATE_MS).value_or(DEFAULT_POLL_RATE_MS); +} + +} // namespace unrealsdk::game::bl1_cfg \ No newline at end of file diff --git a/src/unrealsdk/game/bl1/bl1_config.h b/src/unrealsdk/game/bl1/bl1_config.h new file mode 100644 index 0000000..f83fc8f --- /dev/null +++ b/src/unrealsdk/game/bl1/bl1_config.h @@ -0,0 +1,28 @@ +// +// Date : 24/11/2024 +// Project : unrealsdk +// Author : -Ry +// +#ifndef UNREALSDK_BL1_CONFIG_H +#define UNREALSDK_BL1_CONFIG_H + +#include "unrealsdk/pch.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) + +namespace unrealsdk::game::bl1_cfg { + +std::string console_key(void); +bool is_locking_console_write(void); + +bool is_locking_process_event(void); +bool is_log_load_package(void); +bool is_log_save_package(void); +int64_t init_func_poll_rate_ms(void); + +} // namespace unrealsdk::game::bl1_cfg + +#endif + +#endif diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 8b0077a..e37f3b7 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -3,7 +3,6 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/commands.h" -#include "unrealsdk/env.h" #include "unrealsdk/hook_manager.h" #include "unrealsdk/unreal/classes/properties/copyable_property.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" @@ -136,7 +135,7 @@ bool inject_console_hook(hook_manager::Details& hook) { if (existing_console_key != L"None"_fn || existing_console_key == L"Undefine"_fn) { LOG(MISC, "Console key is already set to '{}'", existing_console_key); } else { - auto wanted_console_key = env::get(env::CONSOLE_KEY, env::defaults::CONSOLE_KEY); + auto wanted_console_key = bl1_cfg::console_key(); console->set(L"ConsoleKey"_fn, FName{wanted_console_key}); LOG(MISC, "Set console key to '{}'", wanted_console_key); @@ -182,7 +181,7 @@ void BL1Hook::uconsole_output_text(const std::wstring& str) const { return; } - if (env::defined(KEY_LOCKING_CONSOLE_WRITE)) { + if (bl1_cfg::is_locking_console_write()) { static std::recursive_mutex s_Mutex{}; std::lock_guard guard{s_Mutex}; console_output_text.call(str); @@ -234,7 +233,7 @@ void* uproperty_import_text(UProperty* prop, } // UProperty::ImportText(...) - uprop_import_text func = reinterpret_cast(prop->vftable[INDEX_IMPORT_TEXT]); + auto func = reinterpret_cast(prop->vftable[INDEX_IMPORT_TEXT]); void* result = func(prop, nullptr, value, write_to, flags, src, err); // - NOTE - diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp index a161e11..05b4a67 100644 --- a/src/unrealsdk/game/bl1/hexedits.cpp +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -51,7 +51,7 @@ const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ // ############################################################################// void BL1Hook::hexedit_set_command(void) { - uint8_t* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); + auto* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); if (set_command_msg == nullptr) { LOG(ERROR, "Failed to find set command message signature."); @@ -102,7 +102,7 @@ void BL1Hook::hexedit_array_limit(void) { } void BL1Hook::hexedit_array_limit_message(void) const { - uint8_t* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); + auto* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); if (array_limit_msg == nullptr) { LOG(ERROR, "Failed to find array limit message signature."); diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 4be1e7a..98f5007 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -2,7 +2,6 @@ #include "unrealsdk/game/bl1/bl1.h" -#include "unrealsdk/env.h" #include "unrealsdk/hook_manager.h" #include "unrealsdk/memory.h" #include "unrealsdk/unreal/classes/ufunction.h" @@ -130,7 +129,7 @@ static_assert(std::is_same_v Date: Sun, 19 Jan 2025 00:59:25 +0000 Subject: [PATCH 36/81] Add comments to uclass.h and ustruct.h indicating when a merge conflict was resolved and the hash of the commit that resolved it. --- src/unrealsdk/unreal/classes/uclass.h | 6 ++++++ src/unrealsdk/unreal/classes/ustruct.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index 4c12a6a..8547325 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -30,6 +30,12 @@ class UClass : public UStruct { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) + // - NOTE: Merge Conflicts (ustruct.h and uclass.h) + // Commit: 7803d86de171a5e19e66688074d8809329e42bb8 + // Manually resolved; only tested compilation for BL1, UE3, and UE4 but no more than that. + // ; 2025/01/19 (YYYY/MM/DD) + // - - - + private: #ifdef UE4 uint8_t UnknownData00[0x70]; diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index e9d20ae..cddf8de 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -32,6 +32,12 @@ class UStruct : public UField { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) + // - NOTE: Merge Conflicts (ustruct.h and uclass.h) + // Commit: 7803d86de171a5e19e66688074d8809329e42bb8 + // Manually resolved; only tested compilation for BL1, UE3, and UE4 but no more than that. + // ; 2025/01/19 (YYYY/MM/DD) + // - - - + #ifdef UE4 /* Struct this inherits from, may be null */ UStruct* SuperField; From 33b84314ca365aa648724e7d9dcadbb346cc1485 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:54:41 +0000 Subject: [PATCH 37/81] Add a note comment and replace cstyle cast with a much more annoying c++ style cast --- src/unrealsdk/game/bl1/hexedits.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp index 05b4a67..e79d524 100644 --- a/src/unrealsdk/game/bl1/hexedits.cpp +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -8,6 +8,12 @@ using namespace unrealsdk::memory; +// - NOTE - +// I genuinely have no idea what these do and if they were required or not lol. They don't seem to +// have any negative impact at runtime so fundamentally they should/might be correct. I was just +// using the BL2 implementation as a checklist and these were trivial to find & patch. +// + namespace unrealsdk::game { // ############################################################################// @@ -85,8 +91,8 @@ void BL1Hook::hexedit_array_limit(void) { // Should *never* be true if (call_instruction[0] != 0xE8) { - LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", (void*)array_limit, - *call_instruction); + LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", + static_cast(array_limit), *call_instruction); return; } From 394a9c87c4388012f881cc951ab92588e8a109ba Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:55:47 +0000 Subject: [PATCH 38/81] raise a runtime exception if the pointer from `find_gmalloc` is null --- src/unrealsdk/game/bl1/memory.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl1/memory.cpp b/src/unrealsdk/game/bl1/memory.cpp index 896787c..0dc99d9 100644 --- a/src/unrealsdk/game/bl1/memory.cpp +++ b/src/unrealsdk/game/bl1/memory.cpp @@ -58,9 +58,15 @@ const constinit Pattern<20> GMALLOC_PATTERN{ // ############################################################################// void BL1Hook::find_gmalloc(void) { - // Using plain `sigscan` since there's an extra level of indirection here, want to make sure to - // print an error before we potentially dereference it + // TODO: I would prefer to create FMalloc if it is null since all usages are wrapped in an + // if null then create block. However, with the delayed hooking via the init function this will + // always be non-null. + gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); + if (!gmalloc) { + // This happens if we try to hook it too early. + throw std::runtime_error("GMalloc is null"); + } LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); } From c59064a4686d208d79e1d9ac5bd619db48899722 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Payne <50511769+Ry0511@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:01:25 +0000 Subject: [PATCH 39/81] Fix: CallFunction was not synchronised which caused crashes when hooked functions were called. Fix: Console commands were not being added to the history ring buffer Remove: Custom console commands which did not belong in the SDK. --- src/unrealsdk/game/bl1/bl1_config.cpp | 8 +- src/unrealsdk/game/bl1/bl1_config.h | 2 - src/unrealsdk/game/bl1/console.cpp | 272 +++++++------------------- src/unrealsdk/game/bl1/hooks.cpp | 71 ++++--- 4 files changed, 109 insertions(+), 244 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1_config.cpp b/src/unrealsdk/game/bl1/bl1_config.cpp index 70e9e01..3b95bdb 100644 --- a/src/unrealsdk/game/bl1/bl1_config.cpp +++ b/src/unrealsdk/game/bl1/bl1_config.cpp @@ -20,11 +20,9 @@ using TomlKeyType = std::string_view; // USDK constexpr TomlKeyType CONSOLE_KEY = "unrealsdk.console_key"; -constexpr TomlKeyType LOCKING_PROCESS_EVENT = "unrealsdk.locking_process_event"; // BL1SDK constexpr TomlKeyType LOG_LOAD_PACKAGE = "unrealsdk.bl1.log_load_package"; -constexpr TomlKeyType LOG_SAVE_PACKAGE = "unrealsdk.bl1.log_save_package"; constexpr TomlKeyType LOCKING_CONSOLE_WRITE = "unrealsdk.bl1.locking_console_write"; constexpr TomlKeyType INIT_FUNC_POLL_RATE_MS = "unrealsdk.bl1.init_func_poll_rate_ms"; @@ -39,17 +37,13 @@ constexpr int64_t DEFAULT_POLL_RATE_MS = 20; // ############################################################################// std::string console_key(void) { - return std::string{config::get_str("unrealsdk.console_key").value_or("Tilde")}; + return std::string{config::get_str(CONSOLE_KEY).value_or("Tilde")}; } bool is_locking_console_write(void) { return config::get_bool(LOCKING_CONSOLE_WRITE).value_or(false); } -bool is_locking_process_event(void) { - return config::get_bool(LOCKING_PROCESS_EVENT).value_or(false); -} - bool is_log_load_package(void) { return config::get_bool(LOG_LOAD_PACKAGE).value_or(false); } diff --git a/src/unrealsdk/game/bl1/bl1_config.h b/src/unrealsdk/game/bl1/bl1_config.h index f83fc8f..425311d 100644 --- a/src/unrealsdk/game/bl1/bl1_config.h +++ b/src/unrealsdk/game/bl1/bl1_config.h @@ -14,9 +14,7 @@ namespace unrealsdk::game::bl1_cfg { std::string console_key(void); -bool is_locking_console_write(void); -bool is_locking_process_event(void); bool is_log_load_package(void); bool is_log_save_package(void); int64_t init_func_poll_rate_ms(void); diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index e37f3b7..bc54174 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -16,24 +16,20 @@ using namespace unrealsdk::unreal; namespace unrealsdk::game { -// ############################################################################// -// | CUSTOM COMMANDS | -// ############################################################################// - -namespace bl1::cmd { - -void sset_command_impl(const wchar_t* args, size_t, size_t); -void sgetnativefunc_command_impl(const wchar_t* args, size_t, size_t); +namespace { -} // namespace bl1::cmd +BoundFunction console_output_text{}; -namespace { +//////////////////////////////////////////////////////////////////////////////// +// | CONSOLE HOOKS | +//////////////////////////////////////////////////////////////////////////////// -// ############################################################################// -// | DEFAULT CONSOLE HOOKS | -// ############################################################################// +// - NOTE - +// I don't know if the say_crash_fix_hook is needed; don't know if the implementation/fix +// needs to change either. Just going assume it is not needed until someone complains about crashing +// in multiplayer lol. -bool say_bypass_hook(hook_manager::Details& hook) { +bool say_bypass_hook(const hook_manager::Details& hook) { static const auto console_command_func = hook.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); static const auto command_property = @@ -44,23 +40,23 @@ bool say_bypass_hook(hook_manager::Details& hook) { return true; } -bool console_command_hook(hook_manager::Details& hook) { - auto cls = hook.obj->Class; - +bool console_command_hook(const hook_manager::Details& hook) { static const auto command_property = hook.args->type->find_prop_and_validate(L"Command"_fn); - static const auto history_prop = cls->find_prop_and_validate(L"History"_fn); + static const auto history_prop = + hook.obj->Class->find_prop_and_validate(L"History"_fn); static const auto history_top_prop = - cls->find_prop_and_validate(L"HistoryTop"_fn); + hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); static const auto history_bot_prop = - cls->find_prop_and_validate(L"HistoryBot"_fn); + hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); static const auto history_cur_prop = - cls->find_prop_and_validate(L"HistoryCur"_fn); + hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); static const UFunction* purge_command_func = - cls->find_func_and_validate(L"PurgeCommandFromHistory"_fn); - static const UFunction* save_config_func = cls->find_func_and_validate(L"SaveConfig"_fn); + hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + static const UFunction* save_config_func = + hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); auto line = hook.args->get(command_property); @@ -101,7 +97,25 @@ bool console_command_hook(hook_manager::Details& hook) { hook.obj->get(save_config_func).call(); } - LOG(INFO, L">>> {} <<<", line); + /* + * This is a little awkward. + * Since we can't let execution though to the unreal function, we're responsible for printing + * the executed command line. + * + * We do this via output text directly, rather than the LOG macro, so that it's not affected by + * the console log level, and so that it happens immediately (the LOG macro is queued, and can + * get out of order with respect to native engine messages). + * + * However, for custom console commands it's also nice to see what the command was in the log + * file, since you'll see all their output too. + * + * We don't really expose a "write to log file only", since it's not usually something useful, + * so as a compromise just use the LOG macro on the lowest possible log level, and assume the + * lowest people practically set their console log level to is dev warning. + */ + auto msg = format(L">>> {} <<<", line); + console_output_text.call(msg); + LOG(MIN, L"{}", msg); try { callback->operator()(line.c_str(), line.size(), cmd_len); @@ -112,13 +126,30 @@ bool console_command_hook(hook_manager::Details& hook) { return true; } -// Would prefer to call a native function where possible, however best I can tell, OutputText is -// actually implemented directly in unrealscript (along most of the console mechanics). -BoundFunction console_output_text{}; +bool pc_console_command_hook(const hook_manager::Details& hook) { + static const auto command_property = + hook.args->type->find_prop_and_validate(L"Command"_fn); + + auto line = hook.args->get(command_property); + + auto [callback, cmd_len] = commands::impl::find_matching_command(line); + if (callback == nullptr) { + return false; + } + + // This hook does not go to console, so there's no extra processing to be done, we can just run + // the callback immediately + try { + callback->operator()(line.c_str(), line.size(), cmd_len); + } catch (const std::exception& ex) { + LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); + } + return true; +} -bool inject_console_hook(hook_manager::Details& hook) { +bool inject_console_hook(const hook_manager::Details& hook) { // clang-format off - hook_manager::remove_hook( + remove_hook( L"WillowGame.WillowGameViewportClient:PostRender", hook_manager::Type::PRE, L"bl1_inject_console_hook" @@ -146,205 +177,50 @@ bool inject_console_hook(hook_manager::Details& hook) { } // namespace -// ############################################################################// -// | BL1HOOK METHODS | -// ############################################################################// +//////////////////////////////////////////////////////////////////////////////// +// | BL1HOOK ENTRIES | +//////////////////////////////////////////////////////////////////////////////// void BL1Hook::inject_console(void) { // clang-format off - hook_manager::add_hook( + add_hook( L"Engine.Console:ShippingConsoleCommand", - unrealsdk::hook_manager::Type::PRE, + hook_manager::Type::PRE, L"bl1_say_bypass_hook", &say_bypass_hook ); - hook_manager::add_hook( + add_hook( L"Engine.Console:ConsoleCommand", hook_manager::Type::PRE, L"bl1_console_command_hook", &console_command_hook ); - hook_manager::add_hook( + add_hook( + L"Engine.PlayerController:ConsoleCommand", + hook_manager::Type::PRE, + L"bl1_pc_console_command_hook", + &pc_console_command_hook + ); + add_hook( L"WillowGame.WillowGameViewportClient:PostRender", hook_manager::Type::PRE, L"bl1_inject_console_hook", &inject_console_hook ); // clang-format on - - commands::add_command(L"sset", &bl1::cmd::sset_command_impl); - commands::add_command(L"sgetnativefunc", &bl1::cmd::sgetnativefunc_command_impl); } void BL1Hook::uconsole_output_text(const std::wstring& str) const { if (console_output_text.func == nullptr) { return; } - - if (bl1_cfg::is_locking_console_write()) { - static std::recursive_mutex s_Mutex{}; - std::lock_guard guard{s_Mutex}; - console_output_text.call(str); - - } else { - console_output_text.call(str); - } + console_output_text.call(str); } bool BL1Hook::is_console_ready(void) const { return console_output_text.func != nullptr; } -// ############################################################################// -// | CUSTOM HOOKS | -// ############################################################################// - -namespace { - -// NOTE: src->get(prop) gives linker errors; we only need the value pointer here. -void* uproperty_get_value_ptr(UProperty* prop, UObject* src, int32_t index = 0) { - return reinterpret_cast(reinterpret_cast(src) + prop->Offset_Internal - + prop->ElementSize * index); -} - -void uconsole_write(const std::wstring& text) { - console_output_text.call(text); -} - -void* uproperty_import_text(UProperty* prop, - const wchar_t* value, - void* write_to, - int32_t flags, - UObject* src, - void* err) { - // These are mostly speculative; I would hope the bitflags are the same... - constexpr auto INDEX_PRE_PROP_CHANGED = 0x11; - constexpr auto INDEX_POST_PROP_CHANGED = 0x13; - constexpr auto INDEX_IMPORT_TEXT = 0x54; - constexpr auto RF_NeedInitialisation = 0x200; - - typedef void(__fastcall * uprop_changed)(UObject*, void*, UProperty*); - typedef void*(__fastcall * uprop_import_text)(UProperty*, void*, const wchar_t*, void*, int32_t, - UObject*, void*); - - // Pre OnChange? - if ((src->ObjectFlags & RF_NeedInitialisation) == 0) { - reinterpret_cast(src->vftable[INDEX_PRE_PROP_CHANGED])(src, nullptr, prop); - } - - // UProperty::ImportText(...) - auto func = reinterpret_cast(prop->vftable[INDEX_IMPORT_TEXT]); - void* result = func(prop, nullptr, value, write_to, flags, src, err); - - // - NOTE - - // ```C - // if ((*(uint *)&piVar2->ObjectFlags & 0x4000) == 0) { - // ... - // if ((((*(uint *)&piVar2->ObjectFlags & lower_bits | this_upper_bits & upper_bits) == 0) && - // ((lower_bits & upper_bits) != 0xffffffff)) || ((this_upper_bits & 0x20000000) != 0)) { - // ``` - // This snippet would eventually cause a reload/reset on the object to defaults stored in the - // .upk we don't want that because we have just modified it and would like to keep our changes. - // So adding 0x4000 to the src->ObjectFlags stops 'src' from being reset. This probably - // introduces new issues or quirks though. Note that this snippet only flags the object for - // reloading. - // - // Ref: 0x005f988a, - src->ObjectFlags |= 0x4000; // RF_NewerVersionExists - - // Post OnChange? - if ((src->ObjectFlags & RF_NeedInitialisation) == 0) { - reinterpret_cast(src->vftable[INDEX_POST_PROP_CHANGED])(src, nullptr, prop); - } - - return result; -} - -} // namespace - -// ############################################################################// -// | CUSTOM CONSOLE COMMANDS | -// ############################################################################// - -void bl1::cmd::sset_command_impl(const wchar_t* line, size_t, size_t) { - std::wstringstream ss{line}; - - // sset CLASS NAME PROPERTY VALUE - // sset WillowGame.PlayerClassDefinition gd_Brick.Character.CharacterClass_Brick FOV 90.0 - std::wstring ignore{}; - std::wstring cls{}; - std::wstring name{}; - std::wstring prop_name{}; - - if (!(ss >> ignore >> cls >> name >> prop_name)) { - uconsole_write(L"Invalid format for 'sset' command"); - return; - } - - // From current position to the end of the string - ss >> std::ws; // Remove leading whitespace - std::streampos pos = ss.tellg(); - std::wstring value{ss.str().substr(static_cast(pos))}; - - UObject* found = find_object(cls, name); - - if (found == nullptr) { - uconsole_write(L"Failed to find object '" + name + L"'"); - return; - } - - UProperty* prop = found->Class->find_prop(FName{prop_name}); - - if (prop == nullptr) { - uconsole_write(L"Failed to find property '" + prop_name + L"'"); - return; - } - - void* value_ptr = uproperty_get_value_ptr(prop, found); - uproperty_import_text(prop, value.c_str(), value_ptr, 1, found, nullptr); -} - -void bl1::cmd::sgetnativefunc_command_impl(const wchar_t* line, size_t, size_t) { - // - NOTE - - // I don't expect this one to be used a lot its mostly for finding addresses of functions in - // disassembly. The text output might not be useful since you can just add a breakpoint here - // and copy the address directly. - // - - std::wstringstream ss{line}; - std::wstring ignore; - std::wstring cls_name; - std::wstring func_name; - - if (!(ss >> ignore >> cls_name >> func_name)) { - uconsole_write(L"Invalid format for sgetnativefunc"); - return; - } - - UClass* found_cls = find_class(cls_name); - - // Couldn't find the class - if (!found_cls) { - uconsole_write(L"Could not find class '" + cls_name + L"'"); - return; - } - - UFunction* found_func = found_cls->find_func_and_validate(FName{func_name}); - - // Couldn't find the function - if (!found_func) { - uconsole_write(fmt::format(L"Failed to find function '{}' in '{}'", func_name, - found_cls->get_path_name())); - return; - } - - std::wstring msg = fmt::format(L"Function address is '{:#010x}'", - reinterpret_cast(found_func->Func)); - - uconsole_write(msg); - LOG(MISC, "[CONSOLE] ~ {}", msg); -} - } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 98f5007..172389b 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -2,7 +2,9 @@ #include "unrealsdk/game/bl1/bl1.h" +#include #include "unrealsdk/hook_manager.h" +#include "unrealsdk/locks.h" #include "unrealsdk/memory.h" #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/structs/fframe.h" @@ -16,10 +18,12 @@ using namespace unrealsdk::unreal; namespace unrealsdk::game { +//////////////////////////////////////////////////////////////////////////////// +// | PROCESS EVENT HOOK | +//////////////////////////////////////////////////////////////////////////////// + namespace { -// This function is actually thiscall, but MSVC won't let us declare static thiscall functions -// As a workaround, declare it fastcall, and add a dummy edx arg. // NOLINTNEXTLINE(modernize-use-using) typedef void(__fastcall* process_event_func)(UObject* obj, void* /*edx*/, @@ -70,8 +74,7 @@ void __fastcall process_event_hook(UObject* obj, .ret = {func->find_return_param()}, .func = {.func = func, .object = obj}}; - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + const bool block_execution = run_hooks_of_type(*data, hook_manager::Type::PRE, hook); if (!block_execution) { process_event_ptr(obj, edx, func, params, null); @@ -81,7 +84,7 @@ void __fastcall process_event_hook(UObject* obj, hook.ret.copy_to(reinterpret_cast(params)); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!has_post_hooks(*data)) { return; } @@ -90,11 +93,10 @@ void __fastcall process_event_hook(UObject* obj, } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + run_hooks_of_type(*data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, - hook); + run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; } @@ -105,14 +107,12 @@ void __fastcall process_event_hook(UObject* obj, process_event_ptr(obj, edx, func, params, null); } -std::recursive_mutex process_event_mutex{}; - void __fastcall locking_process_event_hook(UObject* obj, void* edx, UFunction* func, void* params, void* null) { - const std::lock_guard lock{process_event_mutex}; + const locks::FunctionCall lock{}; process_event_hook(obj, edx, func, params, null); } @@ -121,34 +121,22 @@ static_assert(std::is_same_v, static_assert(std::is_same_v, "process_event signature is incorrect"); -/** - * @brief Checks if we should use a locking process event implementation. - * - * @return True if we should use locks. - */ -bool locking(void) { - // Basically just a function so we can be sure this static is initialized late - LTO hopefully - // takes care of it - static auto locking = bl1_cfg::is_locking_process_event(); - return locking; -} - } // namespace void BL1Hook::hook_process_event(void) { - detour(PROCESS_EVENT_SIG, locking() ? locking_process_event_hook : process_event_hook, + bool locking = locks::FunctionCall::enabled(); + detour(PROCESS_EVENT_SIG, locking ? locking_process_event_hook : process_event_hook, &process_event_ptr, "ProcessEvent"); } void BL1Hook::process_event(UObject* object, UFunction* func, void* params) const { - if (locking()) { - const std::lock_guard lock{process_event_mutex}; - process_event_hook(object, nullptr, func, params, nullptr); - } else { - process_event_hook(object, nullptr, func, params, nullptr); - } + locking_process_event_hook(object, nullptr, func, params, nullptr); } +//////////////////////////////////////////////////////////////////////////////// +// | CALL FUNCTION HOOK | +//////////////////////////////////////////////////////////////////////////////// + namespace { // NOLINTNEXTLINE(modernize-use-using) @@ -186,8 +174,7 @@ void __fastcall call_function_hook(UObject* obj, .ret = {func->find_return_param()}, .func = {.func = func, .object = obj}}; - const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + const bool block_execution = run_hooks_of_type(*data, hook_manager::Type::PRE, hook); if (block_execution) { stack->Code++; @@ -202,7 +189,7 @@ void __fastcall call_function_hook(UObject* obj, - hook.ret.prop->Offset_Internal); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!has_post_hooks(*data)) { return; } @@ -212,11 +199,10 @@ void __fastcall call_function_hook(UObject* obj, } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + run_hooks_of_type(*data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, - hook); + run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; } @@ -227,13 +213,24 @@ void __fastcall call_function_hook(UObject* obj, call_function_ptr(obj, edx, stack, result, func); } +void __fastcall locking_call_function_hook(UObject* obj, + void* edx, + FFrame* stack, + void* result, + UFunction* func) { + const locks::FunctionCall lock{}; + call_function_hook(obj, edx, stack, result, func); +} + static_assert(std::is_same_v, "call_function signature is incorrect"); } // namespace void BL1Hook::hook_call_function(void) { - detour(CALL_FUNCTION_SIG, call_function_hook, &call_function_ptr, "CallFunction"); + bool locking = locks::FunctionCall::enabled(); + detour(CALL_FUNCTION_SIG, locking ? locking_call_function_hook : call_function_hook, + &call_function_ptr, "CallFunction"); } } // namespace unrealsdk::game From 11ec11d30662812a46f63f9d0140e1d72ac76d62 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 16 Feb 2025 17:00:30 +1300 Subject: [PATCH 40/81] upgrade to c++23, remove libfmt --- .gitmodules | 3 -- CMakeLists.txt | 2 +- README.md | 4 +- common_cmake | 2 +- libs/fmt | 1 - src/unrealsdk/format.h | 39 ------------------- src/unrealsdk/game/bl2/console.cpp | 2 +- src/unrealsdk/game/bl3/console.cpp | 2 +- src/unrealsdk/logging.cpp | 8 ++-- src/unrealsdk/logging.h | 7 ++-- src/unrealsdk/pch.h | 14 ++----- src/unrealsdk/unreal/namedobjectcache.h | 7 ++-- src/unrealsdk/unreal/structs/fname.h | 5 +-- .../unreal/structs/fscriptdelegate.cpp | 6 +-- src/unrealsdk/unreal/structs/ftext.h | 5 +-- src/unrealsdk/utils.h | 4 +- src/unrealsdk/version.cpp | 13 +++---- 17 files changed, 33 insertions(+), 91 deletions(-) delete mode 160000 libs/fmt delete mode 100644 src/unrealsdk/format.h diff --git a/.gitmodules b/.gitmodules index ac1c13a..1b42c38 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "libs/minhook"] path = libs/minhook url = https://github.com/TsudaKageyu/minhook.git -[submodule "libs/fmt"] - path = libs/fmt - url = https://github.com/fmtlib/fmt.git [submodule "libs/tomlplusplus"] path = libs/tomlplusplus url = https://github.com/marzer/tomlplusplus.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e9dd1da..9af4492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(UNREALSDK_SHARED False CACHE BOOL "If set, compiles as a shared library inst add_library(_unrealsdk_interface INTERFACE) -target_compile_features(_unrealsdk_interface INTERFACE cxx_std_20) +target_compile_features(_unrealsdk_interface INTERFACE cxx_std_23) set_target_properties(_unrealsdk_interface PROPERTIES COMPILE_WARNING_AS_ERROR True INTERPROCEDURAL_OPTIMIZATION True diff --git a/README.md b/README.md index dc83913..53dd813 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,7 @@ auto op_string = hook.obj->get(L"BuildOverpowerPromptS So there are a few extra steps to integrate the sdk into your project before you can start using hooks like above. -The SDK requires at least C++20. This is primarily for templated lambdas - you may still be able to -use it if your compiler doesn't yet fully support it (e.g. it falls back to fmtlib if `std::format` -is not available). +The SDK requires at least C++23. The recommended way to link against the sdk is as a submodule. diff --git a/common_cmake b/common_cmake index c27d687..92175e1 160000 --- a/common_cmake +++ b/common_cmake @@ -1 +1 @@ -Subproject commit c27d68718f01ca2e40fc2627fa7461b552a0d6ce +Subproject commit 92175e1c74b78bbb6746a589793cf54a9e7c5465 diff --git a/libs/fmt b/libs/fmt deleted file mode 160000 index 0c9fce2..0000000 --- a/libs/fmt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0c9fce2ffefecfdce794e1859584e25877b7b592 diff --git a/src/unrealsdk/format.h b/src/unrealsdk/format.h deleted file mode 100644 index c29c8cb..0000000 --- a/src/unrealsdk/format.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef UNREALSDK_FORMAT_H -#define UNREALSDK_FORMAT_H - -// Header to switch between std::format and fmtlib, and alias them under `unrealsdk::fmt` - -// NOLINTBEGIN(misc-unused-using-decls) - -#if __cpp_lib_format - -#include - -namespace unrealsdk::fmt { - -using std::format; -using std::format_context; -using std::formatter; - -} // namespace unrealsdk::fmt - -#else - -#include -#include -#include -#include - -namespace unrealsdk::fmt { - -using ::fmt::format; -using ::fmt::format_context; -using ::fmt::formatter; - -} // namespace unrealsdk::fmt - -#endif - -// NOLINTEND(misc-unused-using-decls) - -#endif /* UNREALSDK_FORMAT_H */ diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index c661417..e89a2ab 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -227,7 +227,7 @@ bool console_command_hook(hook_manager::Details& hook) { as a compromise just use the LOG macro on the lowest possible log level, and assume the lowest people practically set their console log level to is dev warning. */ - auto msg = unrealsdk::fmt::format(L">>> {} <<<", line); + auto msg = std::format(L">>> {} <<<", line); console_output_text.call(msg); LOG(MIN, L"{}", msg); diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index d773bfa..ffc690e 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -137,7 +137,7 @@ void console_command_hook(UObject* console_obj, UnmanagedFString* raw_line) { useful, so as a compromise just use the LOG macro on the lowest possible log level, and assume the lowest people practically set their console log level to is dev warning. */ - auto msg = unrealsdk::fmt::format(L">>> {} <<<", line); + auto msg = std::format(L">>> {} <<<", line); static_uconsole_output_text(msg); LOG(MIN, L"{}", msg); diff --git a/src/unrealsdk/logging.cpp b/src/unrealsdk/logging.cpp index f26e654..7d0e9c6 100644 --- a/src/unrealsdk/logging.cpp +++ b/src/unrealsdk/logging.cpp @@ -224,7 +224,7 @@ constexpr auto LEVEL_WIDTH = 4; * @return The formatted message */ std::string format_message(const LogMessage& msg) { - return unrealsdk::fmt::format( + return std::format( "{1:>{0}%F %T}Z {3:>{2}}@{5:<{4}d} {7:>{6}}| {8}\n", DATE_WIDTH + sizeof(' ') + TIME_WIDTH, time_from_unix_ms(msg.unix_time_ms), LOCATION_WIDTH, truncate_leading_chunks(msg.location, "\\/:", LOCATION_WIDTH), LINE_WIDTH, msg.line, @@ -237,9 +237,9 @@ std::string format_message(const LogMessage& msg) { * @return The header. */ std::string get_header(void) { - return unrealsdk::fmt::format("{1:<{0}} {3:<{2}} {5:>{4}}@{7:<{6}} {9:>{8}}| \n", DATE_WIDTH, - "date", TIME_WIDTH + sizeof('Z'), "time", LOCATION_WIDTH, - "location", LINE_WIDTH, "line", LEVEL_WIDTH, "v"); + return std::format("{1:<{0}} {3:<{2}} {5:>{4}}@{7:<{6}} {9:>{8}}| \n", DATE_WIDTH, "date", + TIME_WIDTH + sizeof('Z'), "time", LOCATION_WIDTH, "location", LINE_WIDTH, + "line", LEVEL_WIDTH, "v"); } #endif diff --git a/src/unrealsdk/logging.h b/src/unrealsdk/logging.h index cc186f7..67c8dcc 100644 --- a/src/unrealsdk/logging.h +++ b/src/unrealsdk/logging.h @@ -2,7 +2,7 @@ #define UNREALSDK_LOGGING_H // Because this file in included in the pch, we can't include the pch here instead of these -#include "unrealsdk/format.h" +#include namespace unrealsdk::logging { @@ -92,9 +92,8 @@ void remove_callback(log_callback callback); * @param ... The format string + it's contents. */ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define LOG(level, ...) \ - unrealsdk::logging::log((unrealsdk::logging::Level::level), \ - unrealsdk::fmt::format(__VA_ARGS__), \ +#define LOG(level, ...) \ + unrealsdk::logging::log((unrealsdk::logging::Level::level), std::format(__VA_ARGS__), \ {(const char*)(__FUNCTION__), sizeof(__FUNCTION__) - 1}, (__LINE__)) #endif /* UNREALSDK_LOGGING_H */ diff --git a/src/unrealsdk/pch.h b/src/unrealsdk/pch.h index d22d834..89f4db1 100644 --- a/src/unrealsdk/pch.h +++ b/src/unrealsdk/pch.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -56,10 +58,6 @@ #include #include -// This file is just a forwarder for whichever formatting library is configured, it doesn't define -// anything itself, so is fine to include here -#include "unrealsdk/format.h" - // This file is mostly just here so that the `LOG` macro is automatically available everywhere // It only includes library headers, so is also ok to include #include "unrealsdk/logging.h" @@ -73,11 +71,7 @@ using std::uint32_t; using std::uint64_t; using std::uint8_t; -#if __cplusplus > 202002L -using std::float32_t; -using std::float64_t; -#else - +// Awaiting better compiler support to replace these with std::float32_t // NOLINTBEGIN(readability-magic-numbers) static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24, "float is not ieee 32-bit"); @@ -88,8 +82,6 @@ static_assert(std::numeric_limits::is_iec559 && std::numeric_limitsis_instance(this->uclass)) { - throw std::invalid_argument(unrealsdk::utils::narrow(unrealsdk::fmt::format( - L"Found object of unexpected class when searching for '{}': {}", name, - obj->get_path_name()))); + throw std::invalid_argument(unrealsdk::utils::narrow( + std::format(L"Found object of unexpected class when searching for '{}': {}", name, + obj->get_path_name()))); } return add_to_cache(reinterpret_cast(obj)); diff --git a/src/unrealsdk/unreal/structs/fname.h b/src/unrealsdk/unreal/structs/fname.h index 68f5810..8da5ede 100644 --- a/src/unrealsdk/unreal/structs/fname.h +++ b/src/unrealsdk/unreal/structs/fname.h @@ -69,9 +69,8 @@ FName operator"" _fn(const wchar_t* str, size_t len); // Custom FName formatter, which just casts to a string first template <> -struct unrealsdk::fmt::formatter - : unrealsdk::fmt::formatter { - auto format(unrealsdk::unreal::FName name, unrealsdk::fmt::format_context& ctx) const { +struct std::formatter : std::formatter { + auto format(unrealsdk::unreal::FName name, std::format_context& ctx) const { return formatter::format((std::string)name, ctx); } }; diff --git a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp index 994a4db..8f4491c 100644 --- a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp +++ b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp @@ -66,12 +66,12 @@ void FScriptDelegate::validate_signature(const std::optional& fun try { func_from_find = func->object->Class->find_func_and_validate(func->func->Name); } catch (const std::invalid_argument&) { - throw std::invalid_argument(unrealsdk::fmt::format( + throw std::invalid_argument(std::format( "Could not convert function to delegate: could not find function with name '{}'", func->func->Name)); } if (func_from_find != func->func) { - throw std::invalid_argument(utils::narrow(unrealsdk::fmt::format( + throw std::invalid_argument(utils::narrow(std::format( L"Could not convert function to delegate: got another function with the same name," "{} instead of {}", func_from_find->get_path_name(), func->func->get_path_name()))); @@ -100,7 +100,7 @@ void FScriptDelegate::validate_signature(const std::optional& fun [](UProperty* func, UProperty* sig) { return func->Class == sig->Class; }); if (func_diff != func_props.end() && sig_diff != sig_props.end()) { - throw std::invalid_argument(unrealsdk::fmt::format( + throw std::invalid_argument(std::format( "Function signature does not match delegate: function's {} {} != delegate's {} {}", (*func_diff)->Class->Name, (*func_diff)->Name, (*sig_diff)->Class->Name, (*sig_diff)->Name)); diff --git a/src/unrealsdk/unreal/structs/ftext.h b/src/unrealsdk/unreal/structs/ftext.h index cd1cb93..95d6e2c 100644 --- a/src/unrealsdk/unreal/structs/ftext.h +++ b/src/unrealsdk/unreal/structs/ftext.h @@ -74,9 +74,8 @@ struct FText { // Custom FText formatter, which just casts to a string first template <> -struct unrealsdk::fmt::formatter - : unrealsdk::fmt::formatter { - auto format(unrealsdk::unreal::FText text, unrealsdk::fmt::format_context& ctx) const { +struct std::formatter : std::formatter { + auto format(unrealsdk::unreal::FText text, std::format_context& ctx) const { return formatter::format((std::string)text, ctx); } }; diff --git a/src/unrealsdk/utils.h b/src/unrealsdk/utils.h index f60be74..b20a3de 100644 --- a/src/unrealsdk/utils.h +++ b/src/unrealsdk/utils.h @@ -160,8 +160,8 @@ using StringViewMap = std::unordered_map, std::equal // Custom wstring formatter, which calls narrow template <> -struct unrealsdk::fmt::formatter : unrealsdk::fmt::formatter { - auto format(const std::wstring& str, unrealsdk::fmt::format_context& ctx) const { +struct std::formatter : std::formatter { + auto format(const std::wstring& str, std::format_context& ctx) const { return formatter::format(unrealsdk::utils::narrow(str), ctx); } }; diff --git a/src/unrealsdk/version.cpp b/src/unrealsdk/version.cpp index e492c60..261d032 100644 --- a/src/unrealsdk/version.cpp +++ b/src/unrealsdk/version.cpp @@ -22,13 +22,12 @@ namespace { #include "unrealsdk/git.inl" const constexpr auto GIT_HASH_CHARS = 8; -const std::string VERSION_STR = - unrealsdk::fmt::format("unrealsdk v{}.{}.{} ({}{})", - VERSION_MAJOR, - VERSION_MINOR, - VERSION_PATCH, - std::string(GIT_HEAD_SHA1).substr(0, GIT_HASH_CHARS), - GIT_IS_DIRTY ? ", dirty" : ""); +const std::string VERSION_STR = std::format("unrealsdk v{}.{}.{} ({}{})", + VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH, + std::string(GIT_HEAD_SHA1).substr(0, GIT_HASH_CHARS), + GIT_IS_DIRTY ? ", dirty" : ""); #endif } // namespace From 18ecfc3500802441d387c716ef2d6c7ae9840a21 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 16 Feb 2025 17:07:50 +1300 Subject: [PATCH 41/81] change construct_objects flags to always be u64 this means `unrealsdk.h` no longer depends on `uobject.h`, which cleans up some things for later commits --- src/unrealsdk/game/abstract_hook.h | 4 ++-- src/unrealsdk/game/bl2/bl2.h | 2 +- src/unrealsdk/game/bl2/object.cpp | 2 +- src/unrealsdk/game/bl3/bl3.h | 2 +- src/unrealsdk/game/bl3/object.cpp | 5 ++++- src/unrealsdk/unrealsdk.h | 4 ++-- src/unrealsdk/unrealsdk_fw.inl | 2 +- src/unrealsdk/unrealsdk_main.cpp | 2 +- src/unrealsdk/unrealsdk_wrappers.cpp | 2 +- 9 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index 3b62763..d4c9be7 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -3,7 +3,6 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/structs/fname.h" #ifndef UNREALSDK_IMPORTING @@ -14,6 +13,7 @@ class GNames; class GObjects; class UClass; class UFunction; +class UObject; struct FFrame; struct FLazyObjectPtr; struct FSoftObjectPtr; @@ -61,7 +61,7 @@ struct AbstractHook { unreal::UClass* cls, unreal::UObject* outer, const unreal::FName& name, - decltype(unreal::UObject::ObjectFlags) flags, + uint64_t flags, unreal::UObject* template_obj) const = 0; [[nodiscard]] virtual unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const = 0; diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index 2e0ac4a..f0e2b4a 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -109,7 +109,7 @@ class BL2Hook : public AbstractHook { [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, unreal::UObject* outer, const unreal::FName& name, - decltype(unreal::UObject::ObjectFlags) flags, + uint64_t flags, unreal::UObject* template_obj) const override; [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const override; diff --git a/src/unrealsdk/game/bl2/object.cpp b/src/unrealsdk/game/bl2/object.cpp index 419aa63..1215219 100644 --- a/src/unrealsdk/game/bl2/object.cpp +++ b/src/unrealsdk/game/bl2/object.cpp @@ -63,7 +63,7 @@ void BL2Hook::find_construct_object(void) { UObject* BL2Hook::construct_object(UClass* cls, UObject* outer, const FName& name, - decltype(UObject::ObjectFlags) flags, + uint64_t flags, UObject* template_obj) const { return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0 /* false */); diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index ceb6762..040fa56 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -98,7 +98,7 @@ class BL3Hook : public AbstractHook { [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, unreal::UObject* outer, const unreal::FName& name, - decltype(unreal::UObject::ObjectFlags) flags, + uint64_t flags, unreal::UObject* template_obj) const override; [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const override; diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index 30348f9..49fc526 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -55,8 +55,11 @@ void BL3Hook::find_construct_object(void) { UObject* BL3Hook::construct_object(UClass* cls, UObject* outer, const FName& name, - decltype(UObject::ObjectFlags) flags, + uint64_t flags, UObject* template_obj) const { + if (flags > std::numeric_limits::max()) { + throw std::out_of_range("construct_object flags out of range, only 32-bits are supported") + } return construct_obj_ptr(cls, outer, name, flags, 0, template_obj, 0 /* false */, nullptr, 0 /* false */); } diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index 966184c..b24a3e3 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -3,7 +3,6 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/structs/fname.h" namespace unrealsdk::game { @@ -18,6 +17,7 @@ class GNames; class GObjects; class UClass; class UFunction; +class UObject; struct FFrame; struct FLazyObjectPtr; struct FSoftObjectPtr; @@ -121,7 +121,7 @@ void u_free(void* data); [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, unreal::UObject* outer, const unreal::FName& name = {0, 0}, - decltype(unreal::UObject::ObjectFlags) flags = 0, + uint64_t flags = 0, unreal::UObject* template_obj = nullptr); /** diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index 69a1de1..d942798 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -41,7 +41,7 @@ UNREALSDK_CAPI([[nodiscard]] UObject*, UClass* cls, UObject* outer, const FName* name = nullptr, - decltype(UObject::ObjectFlags) flags = 0, + uint64_t flags = 0, UObject* template_obj = nullptr); UNREALSDK_CAPI([[nodiscard]] UObject*, find_object, diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index 59b3dec..6963a58 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -123,7 +123,7 @@ UNREALSDK_CAPI([[nodiscard]] UObject*, UClass* cls, UObject* outer, const FName* name, - decltype(UObject::ObjectFlags) flags, + uint64_t flags, UObject* template_obj) { FName local_name{0, 0}; if (name != nullptr) { diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index f0f9cae..fc73b31 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -43,7 +43,7 @@ void u_free(void* data) { UObject* construct_object(UClass* cls, UObject* outer, const FName& name, - decltype(UObject::ObjectFlags) flags, + uint64_t flags, UObject* template_obj) { return UNREALSDK_MANGLE(construct_object)(cls, outer, &name, flags, template_obj); } From 1f84e5df41d813e56622d2555c73276f81e408aa Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 16 Feb 2025 17:14:13 +1300 Subject: [PATCH 42/81] prototype dynamic field offsets going to leave bl3 broken for now --- src/unrealsdk/game/abstract_hook.h | 7 ++ src/unrealsdk/game/bl2/bl2.h | 1 + src/unrealsdk/game/bl2/offsets.cpp | 27 +++++ src/unrealsdk/game/bl2/offsets/uobject.h | 76 ++++++++++++++ src/unrealsdk/unreal/classes/uobject.cpp | 4 + src/unrealsdk/unreal/classes/uobject.h | 42 ++++---- src/unrealsdk/unreal/offset_list.h | 39 ++++++++ src/unrealsdk/unreal/offsets.h | 121 +++++++++++++++++++++++ src/unrealsdk/unrealsdk.h | 14 +++ src/unrealsdk/unrealsdk_fw.inl | 2 + src/unrealsdk/unrealsdk_main.cpp | 4 + src/unrealsdk/unrealsdk_wrappers.cpp | 4 + 12 files changed, 324 insertions(+), 17 deletions(-) create mode 100644 src/unrealsdk/game/bl2/offsets.cpp create mode 100644 src/unrealsdk/game/bl2/offsets/uobject.h create mode 100644 src/unrealsdk/unreal/offset_list.h create mode 100644 src/unrealsdk/unreal/offsets.h diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index d4c9be7..04d4d4f 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -22,6 +22,12 @@ struct TemporaryFString; } // namespace unrealsdk::unreal +namespace unrealsdk::unreal::offsets { + +struct OffsetList; + +} // namespace unrealsdk::unreal::offsets + namespace unrealsdk::game { #pragma region Hook Classes @@ -81,6 +87,7 @@ struct AbstractHook { const unreal::UObject* obj) const = 0; virtual void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const = 0; + [[nodiscard]] virtual const unreal::offsets::OffsetList& get_offsets(void) const = 0; }; #pragma endregion diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index f0e2b4a..c086eec 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -129,6 +129,7 @@ class BL2Hook : public AbstractHook { const unreal::UObject* obj) const override; void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; + [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; }; template <> diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp new file mode 100644 index 0000000..bd8ec46 --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -0,0 +1,27 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/bl2.h" +#include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/unreal/offset_list.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::unreal::offsets; + +namespace unrealsdk::game { + +namespace { + +struct OffsetClasses { + using UObject = bl2::UObject; +}; + +const auto OFFSETS = OffsetList::from(); + +} // namespace + +[[nodiscard]] const unreal::offsets::OffsetList& BL2Hook::get_offsets(void) const { + return OFFSETS; +} + +} // namespace unrealsdk::game +#endif diff --git a/src/unrealsdk/game/bl2/offsets/uobject.h b/src/unrealsdk/game/bl2/offsets/uobject.h new file mode 100644 index 0000000..7701d33 --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets/uobject.h @@ -0,0 +1,76 @@ +#ifndef UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H +#define UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/structs/fname.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UObject; +class UClass; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl2 { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming) + +class UObject { + public: + UObject() = delete; + UObject(const UObject&) = delete; + UObject(UObject&&) = delete; + UObject& operator=(const UObject&) = delete; + UObject& operator=(UObject&&) = delete; + ~UObject() = delete; + + private: + uintptr_t* vftable; + void* HashNext; + + public: + uint64_t ObjectFlags; + + private: + void* HashOuterNext; + void* StateFrame; + unreal::UObject* _Linker; + void* _LinkerIndex; + + public: + int32_t InternalIndex; + + private: + int32_t NetIndex; + + public: + UObject* Outer; + unreal::FName Name; + unreal::UClass* Class; + + private: + unreal::UObject* ObjectArchetype; +}; + +// NOLINTEND(readability-identifier-naming) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::bl2 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H */ diff --git a/src/unrealsdk/unreal/classes/uobject.cpp b/src/unrealsdk/unreal/classes/uobject.cpp index e2f4221..655b796 100644 --- a/src/unrealsdk/unreal/classes/uobject.cpp +++ b/src/unrealsdk/unreal/classes/uobject.cpp @@ -4,6 +4,8 @@ #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/classes/ustruct_funcs.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/wrappers/bound_function.h" @@ -11,6 +13,8 @@ namespace unrealsdk::unreal { +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UObject, UNREALSDK_UOBJECT_FIELDS); + #ifdef UNREALSDK_INTERNAL_PATH_NAME /** diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index e5bce0f..70d38f8 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -4,6 +4,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/class_traits.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/structs/fname.h" @@ -31,36 +32,43 @@ class UObject { // NOLINTBEGIN(readability-identifier-naming) #if UE4 + using object_flags_type = uint32_t; +#else + using object_flags_type = uint64_t; +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UOBJECT_FIELDS(X) \ + X(object_flags_type, ObjectFlags) \ + X(int32_t, InternalIndex) \ + X(UClass*, Class) \ + X(FName, Name) \ + X(UObject*, Outer) + + UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECT_FIELDS); + +#if UE4 + uintptr_t* vftable; int32_t ObjectFlags; int32_t InternalIndex; UClass* Class; FName Name; UObject* Outer; #else - private: - void* HashNext; - - public: - uint64_t ObjectFlags; private: + // TODO: remove + void* HashNext; + uint64_t ObjectFlags_member; void* HashOuterNext; void* StateFrame; class UObject* _Linker; void* _LinkerIndex; - - public: - int InternalIndex; - - private: + int InternalIndex_member; int NetIndex; - - public: - UObject* Outer; - FName Name; - UClass* Class; - - private: + UObject* Outer_member; + FName Name_member; + UClass* Class_member; UObject* ObjectArchetype; public: diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h new file mode 100644 index 0000000..ce2447d --- /dev/null +++ b/src/unrealsdk/unreal/offset_list.h @@ -0,0 +1,39 @@ +#ifndef UNREALSDK_UNREAL_OFFSET_LIST_H +#define UNREALSDK_UNREAL_OFFSET_LIST_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/uobject.h" + +namespace unrealsdk::unreal::offsets { + +// Since this type is shared between dlls, also force consistent padding between compilers, even +// though this isn't an unreal type +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) X(UObject) + +#define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) name::Offsets name; +#define UNREALSDK_OFFSETS__NESTED_FROM(name) name::Offsets::from(), + +// NOLINTEND(cppcoreguidelines-macro-usage) + +struct OffsetList { + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS) + + template + static constexpr OffsetList from(void) { + return {UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__NESTED_FROM)}; + } +}; + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::unreal::offsets + +#endif /* UNREALSDK_UNREAL_OFFSET_LIST_H */ diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h new file mode 100644 index 0000000..3876c6f --- /dev/null +++ b/src/unrealsdk/unreal/offsets.h @@ -0,0 +1,121 @@ +#ifndef UNREALSDK_UNREAL_OFFSETS_H +#define UNREALSDK_UNREAL_OFFSETS_H + +#include "unrealsdk/pch.h" + +namespace unrealsdk::unreal::offsets { + +using offset_type = uint16_t; + +template +using copy_cv = std::conditional_t< + std::is_const_v, + std::add_const_t, std::add_volatile_t, To>>, + /* align */ std::conditional_t, std::add_volatile_t, To>>; + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +// Internal macros +#define UNREALSDK_OFFSETS__DEFINE_OFFSET_MEMBERS(type, name) \ + unrealsdk::unreal::offsets::offset_type name; +#define UNREALSDK_OFFSETS__OFFSETOF_ASSERTS(type, name) \ + static_assert(offsetof(T, name) \ + < std::numeric_limits::max()); +#define UNREALSDK_OFFSETS__OFFSETOF(type, name) \ + static_cast(offsetof(T, name)), +#define UNREALSDK_OFFSETS__DEFINE_GETTER(type, name) \ + template \ + [[nodiscard]] unrealsdk::unreal::offsets::copy_cv& name(this T& self) { \ + return *reinterpret_cast*>( \ + reinterpret_cast(&self) + Offsets::get(&Offsets::name)); \ + } + +/** + * @brief Header file macro to defines all the machinery for variable offset fields. + * @note Should be placed within the class definition. + * + * @param ClassName The name of the class these fields are being defined for. + * @param X_MACRO An X macro list of the fields to define. See the example below. + */ +#define UNREALSDK_DEFINE_FIELDS_HEADER(ClassName, X_MACRO) \ + public: \ + struct Offsets; \ + X_MACRO(UNREALSDK_OFFSETS__DEFINE_GETTER) \ + struct Offsets { \ + X_MACRO(UNREALSDK_OFFSETS__DEFINE_OFFSET_MEMBERS) \ + template \ + static constexpr Offsets from() { \ + X_MACRO(UNREALSDK_OFFSETS__OFFSETOF_ASSERTS); \ + return {X_MACRO(UNREALSDK_OFFSETS__OFFSETOF)}; \ + } \ + static unrealsdk::unreal::offsets::offset_type get( \ + unrealsdk::unreal::offsets::offset_type Offsets::* field); \ + } // deliberately no semicolon - forward declared earlier so that we could put this last + +/** + * @brief Source file macro to defines all the machinery for variable offset fields. + * @note Should be placed at the top level (but within the relevant namespace). + * + * @param ClassName The name of the class these fields are being defined for. + * @param X_MACRO An X macro list of the fields to define. See the example below. + */ +#define UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(ClassName, X_MACRO) \ + unrealsdk::unreal::offsets::offset_type ClassName::Offsets::get( \ + unrealsdk::unreal::offsets::offset_type ClassName::Offsets::* field) { \ + return unrealsdk::internal::get_offsets().ClassName.*field; \ + } + +#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if) + +// The above macros take an X macro field list. This is a macro function, which takes another +// macro function, and calls it for each field + +#define UOBJECT_FIELDS(X) \ + X(UClass*, Class) \ + X(UObject*, Outer) + +// The inner macro always takes the following args: +// - The field's type +// - The field's name + +// Calling the macro +UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UOBJECT_FIELDS); + +// Creates a class aproximately like the following: +struct Offsets { + offset_type Class; + offset_type Outer; + + template + static constexpr Offsets from() { + return { + offsetof(T, Class), + offsetof(T, Outer), + }; + } +}; + +// You can use a one-liner to fill this with all the relevant offsets from another type +auto bl2_offsets = Offsets::from(); + +// The macro also creates a bunch of getters like the following +template +[[nodiscard]] copy_cv& Class(this T& self) { + return *reinterpret_cast*>(reinterpret_cast(&self) + + Offsets::get(&Offsets::Class)); +} + +// Since these return a reference, they can be used pretty much the exact same way as a member. +auto cls = obj->Class()->find(L"MyField"_fn); +obj->Class() = some_other_class; + +// Storing the reference returned from these functions has the exact same sematics as taking a +// member reference - i.e. don't do it, it's only valid for the parent object's lifetime. + +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +} // namespace unrealsdk::unreal::offsets + +#endif /* UNREALSDK_UNREAL_OFFSETS_H */ diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index b24a3e3..ecd3c26 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -26,6 +26,12 @@ struct TemporaryFString; } // namespace unrealsdk::unreal +namespace unrealsdk::unreal::offsets { + +struct OffsetList; + +} + namespace unrealsdk { #ifndef UNREALSDK_IMPORTING @@ -212,6 +218,14 @@ void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& void fsoftobjectptr_assign(unreal::FSoftObjectPtr* ptr, const unreal::UObject* obj); void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj); +/** + * @brief Get the offsets list for the currently hooked game. + * @note This is valid to call during initalization. + * + * @return A reference to the offsets list. + */ +[[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void); + } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index d942798..3de1589 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -10,6 +10,7 @@ namespace. It is included directly in the other two files. #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/offset_list.h" namespace unrealsdk::unreal { @@ -64,6 +65,7 @@ UNREALSDK_CAPI([[nodiscard]] wchar_t*, uobject_path_name, const UObject* obj, si UNREALSDK_CAPI(void, ftext_as_culture_invariant, FText* text, TemporaryFString&& str); UNREALSDK_CAPI(void, fsoftobjectptr_assign, FSoftObjectPtr* ptr, const unreal::UObject* obj); UNREALSDK_CAPI(void, flazyobjectptr_assign, FLazyObjectPtr* ptr, const unreal::UObject* obj); +UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets); } // namespace internal diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index 6963a58..ddfe88a 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -196,6 +196,10 @@ UNREALSDK_CAPI(void, flazyobjectptr_assign, FLazyObjectPtr* ptr, const unreal::U hook_instance->flazyobjectptr_assign(ptr, obj); } +UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets) { + return &hook_instance->get_offsets(); +} + } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index fc73b31..1d2c080 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -104,6 +104,10 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o UNREALSDK_MANGLE(flazyobjectptr_assign)(ptr, obj); } +[[nodiscard]] const offsets::OffsetList& get_offsets() { + return *UNREALSDK_MANGLE(get_offsets)(); +} + } // namespace internal } // namespace unrealsdk From 12e5e476791c42d56487ea780c40e804f6d55586 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 16 Feb 2025 17:14:50 +1300 Subject: [PATCH 43/81] switch all members to function calls --- src/unrealsdk/game/bl2/console.cpp | 22 +++++++++---------- src/unrealsdk/hook_manager.cpp | 4 ++-- src/unrealsdk/unreal/cast.h | 6 ++--- src/unrealsdk/unreal/class_name.h | 2 +- .../persistent_object_ptr_property.cpp | 9 ++++---- .../classes/properties/uarrayproperty.cpp | 2 +- .../classes/properties/uclassproperty.cpp | 4 ++-- .../classes/properties/uinterfaceproperty.cpp | 2 +- .../properties/umulticastdelegateproperty.cpp | 2 +- .../classes/properties/uobjectproperty.cpp | 2 +- .../classes/properties/ustructproperty.cpp | 3 ++- .../properties/uweakobjectproperty.cpp | 2 +- src/unrealsdk/unreal/classes/uclass.cpp | 2 +- src/unrealsdk/unreal/classes/uobject.cpp | 6 ++--- src/unrealsdk/unreal/classes/uobject_funcs.h | 4 ++-- src/unrealsdk/unreal/classes/uproperty.cpp | 6 ++--- src/unrealsdk/unreal/classes/ustruct.cpp | 10 ++++----- src/unrealsdk/unreal/find_class.cpp | 4 ++-- src/unrealsdk/unreal/namedobjectcache.h | 2 +- .../unreal/structs/fscriptdelegate.cpp | 14 ++++++------ .../unreal/structs/tpersistentobjectptr.cpp | 8 +++---- .../unreal/wrappers/bound_function.h | 2 +- .../unreal/wrappers/property_proxy.cpp | 2 +- .../unreal/wrappers/weak_pointer.cpp | 13 ++++++----- src/unrealsdk/unreal/wrappers/wrapped_array.h | 2 +- .../wrappers/wrapped_multicast_delegate.cpp | 2 +- .../unreal/wrappers/wrapped_struct.cpp | 6 ++--- 27 files changed, 73 insertions(+), 70 deletions(-) diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index e89a2ab..e0a0d0a 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -74,7 +74,7 @@ bool say_bypass_hook(hook_manager::Details& hook) { */ static const auto console_command_func = - hook.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); + hook.obj->Class()->find_func_and_validate(L"ConsoleCommand"_fn); static const auto command_property = hook.args->type->find_prop_and_validate(L"Command"_fn); @@ -111,7 +111,7 @@ bool say_crash_fix_hook(hook_manager::Details& hook) { static const auto engine = unrealsdk::find_object(L"WillowGameEngine", L"Transient.WillowGameEngine_0"); static const auto spark_interface_prop = - engine->Class->find_prop_and_validate(L"SparkInterface"_fn); + engine->Class()->find_prop_and_validate(L"SparkInterface"_fn); static const auto is_spark_enabled_func = spark_interface_prop->get_interface_class()->find_func_and_validate(L"IsSparkEnabled"_fn); @@ -123,11 +123,11 @@ bool say_crash_fix_hook(hook_manager::Details& hook) { } static const auto get_timestamp_string_func = - hook.obj->Class->find_func_and_validate(L"GetTimestampString"_fn); + hook.obj->Class()->find_func_and_validate(L"GetTimestampString"_fn); static const auto default_save_game_manager = find_class(L"WillowSaveGameManager"_fn)->ClassDefaultObject(); static const auto time_format_prop = - default_save_game_manager->Class->find_prop_and_validate(L"TimeFormat"_fn); + default_save_game_manager->Class()->find_prop_and_validate(L"TimeFormat"_fn); auto timestamp = BoundFunction{.func = get_timestamp_string_func, .object = hook.obj} .call( @@ -145,7 +145,7 @@ bool say_crash_fix_hook(hook_manager::Details& hook) { player_name += timestamp; static const auto add_chat_message_internal_func = - hook.obj->Class->find_func_and_validate(L"AddChatMessageInternal"_fn); + hook.obj->Class()->find_func_and_validate(L"AddChatMessageInternal"_fn); static const auto msg_prop = hook.args->type->find_prop_and_validate(L"msg"_fn); BoundFunction{.func = add_chat_message_internal_func, .object = hook.obj} @@ -159,18 +159,18 @@ bool console_command_hook(hook_manager::Details& hook) { hook.args->type->find_prop_and_validate(L"Command"_fn); static const auto history_prop = - hook.obj->Class->find_prop_and_validate(L"History"_fn); + hook.obj->Class()->find_prop_and_validate(L"History"_fn); static const auto history_top_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryTop"_fn); static const auto history_bot_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryBot"_fn); static const auto history_cur_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryCur"_fn); static const UFunction* purge_command_func = - hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + hook.obj->Class()->find_func_and_validate(L"PurgeCommandFromHistory"_fn); static const UFunction* save_config_func = - hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); + hook.obj->Class()->find_func_and_validate(L"SaveConfig"_fn); auto line = hook.args->get(command_property); diff --git a/src/unrealsdk/hook_manager.cpp b/src/unrealsdk/hook_manager.cpp index 38979db..34f69ad 100644 --- a/src/unrealsdk/hook_manager.cpp +++ b/src/unrealsdk/hook_manager.cpp @@ -227,13 +227,13 @@ const List* preprocess_hook(std::wstring_view source, const UFunction* func, con } // Check if anything matches the function FName - auto fname_iter = hooks.find(func->Name); + auto fname_iter = hooks.find(func->Name()); if (fname_iter == hooks.end()) { return nullptr; } auto& path_name_map = fname_iter->second; if (path_name_map.empty()) { - hooks.erase(func->Name); + hooks.erase(func->Name()); return nullptr; } diff --git a/src/unrealsdk/unreal/cast.h b/src/unrealsdk/unreal/cast.h index 9662a5e..cada045 100644 --- a/src/unrealsdk/unreal/cast.h +++ b/src/unrealsdk/unreal/cast.h @@ -96,7 +96,7 @@ namespace { * @param obj The object which failed to cast. */ inline void default_cast_fallback(const UObject* obj) { - throw std::runtime_error("Unknown object type " + (std::string)obj->Class->Name); + throw std::runtime_error("Unknown object type " + (std::string)obj->Class()->Name()); } #ifdef __clang__ @@ -153,7 +153,7 @@ void cast_impl(InputType* obj, && (include_input_type || !std::is_same_v, cls>)) { // If the class name matches - if (working_class->Name == cls_fname()) { + if (working_class->Name() == cls_fname()) { // Run the callback if constexpr (std::is_const_v) { return func.template operator()(reinterpret_cast(obj)); @@ -249,7 +249,7 @@ void cast(InputType* obj, const Function& func, const Fallback& fallback = defau return cast_impl( - obj, obj->Class, func, fallback); + obj, obj->Class(), func, fallback); } } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/class_name.h b/src/unrealsdk/unreal/class_name.h index 1790aca..42acb4f 100644 --- a/src/unrealsdk/unreal/class_name.h +++ b/src/unrealsdk/unreal/class_name.h @@ -37,7 +37,7 @@ void throw_on_wrong_type(const UObject* obj) { throw std::invalid_argument("Tried to validate type of null object!"); } static const auto expected_cls_name = cls_fname(); - auto cls_name = obj->Class->Name; + auto cls_name = obj->Class()->Name(); if (cls_name != expected_cls_name) { throw std::invalid_argument("Object was of unexpected type " + (std::string)cls_name); } diff --git a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp index b67e0a6..49d031a 100644 --- a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp +++ b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp @@ -46,7 +46,7 @@ void PropTraits::set(const ULazyObjectProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } } @@ -59,7 +59,7 @@ void PropTraits::set(const USoftObjectProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } } @@ -72,11 +72,12 @@ void PropTraits::set(const USoftClassProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } auto meta_cls = prop->get_meta_class(); if (!value->inherits(meta_cls)) { - throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name); + throw std::runtime_error("Class does not inherit from " + + (std::string)meta_cls->Name()); } } diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp index f016d67..bae9e4a 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp @@ -38,7 +38,7 @@ void PropTraits::set(const UArrayProperty* prop, } if (value.type != inner) { throw std::runtime_error("Array does not contain fields of type " - + (std::string)inner->Name); + + (std::string)inner->Name()); } auto arr = reinterpret_cast*>(addr); diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp index ba6abd2..fe19d6c 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp @@ -24,11 +24,11 @@ void PropTraits::set(const UClassProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } auto meta_cls = prop->get_meta_class(); if (!value->inherits(meta_cls)) { - throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name); + throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name()); } } diff --git a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp index dcac46e..1ede371 100644 --- a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp @@ -37,7 +37,7 @@ void PropTraits::set(const UInterfaceProperty* prop, FImplementedInterface impl{}; if (!value->is_implementation(prop_iface, &impl)) { throw std::runtime_error("Object is not implementation of " - + (std::string)prop_iface->Name); + + (std::string)prop_iface->Name()); } pointer_offset = impl.get_pointer_offset(); } diff --git a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp index b11c4a0..e2145ae 100644 --- a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp @@ -25,7 +25,7 @@ void PropTraits::set(const UMulticastDelegatePropert const Value& value) { if (value.signature != prop->get_signature()) { throw std::runtime_error("Multicast delegate signature doesn't match existing signature of " - + (std::string)prop->get_signature()->Name); + + (std::string)prop->get_signature()->Name()); } auto arr = reinterpret_cast*>(addr); diff --git a/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp b/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp index 5121bdd..68b8d92 100644 --- a/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp @@ -24,7 +24,7 @@ void PropTraits::set(const UObjectProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } } diff --git a/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp b/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp index 7413e30..99ed774 100644 --- a/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp @@ -25,7 +25,8 @@ void PropTraits::set(const UStructProperty* prop, const Value& value) { auto this_struct = prop->get_inner_struct(); if (value.type != this_struct) { - throw std::runtime_error("Struct is not an instance of " + (std::string)this_struct->Name); + throw std::runtime_error("Struct is not an instance of " + + (std::string)this_struct->Name()); } copy_struct(addr, value); } diff --git a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp index 1defd65..eb83789 100644 --- a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp @@ -24,7 +24,7 @@ void PropTraits::set(const UWeakObjectProperty* prop, if (value != nullptr) { auto prop_cls = prop->get_property_class(); if (!value->is_instance(prop_cls)) { - throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name); + throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } } diff --git a/src/unrealsdk/unreal/classes/uclass.cpp b/src/unrealsdk/unreal/classes/uclass.cpp index 184220a..debc08e 100644 --- a/src/unrealsdk/unreal/classes/uclass.cpp +++ b/src/unrealsdk/unreal/classes/uclass.cpp @@ -35,7 +35,7 @@ decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) { [[nodiscard]] const decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) const { static const auto use_tps_offset = unrealsdk::config::get_bool("unrealsdk.__force_uclass_interfaces_tps_offset") - .value_or(this->Class->get_struct_size() == UCLASS_SIZE_TPS); + .value_or(this->Class()->get_struct_size() == UCLASS_SIZE_TPS); if (use_tps_offset) { return *reinterpret_cast( diff --git a/src/unrealsdk/unreal/classes/uobject.cpp b/src/unrealsdk/unreal/classes/uobject.cpp index 655b796..394b6cb 100644 --- a/src/unrealsdk/unreal/classes/uobject.cpp +++ b/src/unrealsdk/unreal/classes/uobject.cpp @@ -54,11 +54,11 @@ std::wstring UObject::get_path_name(void) const { #endif bool UObject::is_instance(const UClass* cls) const { - return this->Class->inherits(cls); + return this->Class()->inherits(cls); } bool UObject::is_implementation(const UClass* iface, FImplementedInterface* impl_out) const { - return this->Class->implements(iface, impl_out); + return this->Class()->implements(iface, impl_out); } template <> @@ -81,7 +81,7 @@ BoundFunction UObject::get(const UFunction* prop, size } template <> BoundFunction UObject::get(const FName& name, size_t idx) const { - return this->get(this->Class->find_func_and_validate(name), idx); + return this->get(this->Class()->find_func_and_validate(name), idx); } } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/uobject_funcs.h b/src/unrealsdk/unreal/classes/uobject_funcs.h index 9f8557f..9a15e39 100644 --- a/src/unrealsdk/unreal/classes/uobject_funcs.h +++ b/src/unrealsdk/unreal/classes/uobject_funcs.h @@ -22,7 +22,7 @@ template <> template [[nodiscard]] V UObject::get(const FName& name, size_t idx) const { - return this->get(this->Class->find_prop_and_validate(name), idx); + return this->get(this->Class()->find_prop_and_validate(name), idx); } template [[nodiscard]] V UObject::get(const T* prop, size_t idx) const { @@ -31,7 +31,7 @@ template template void UObject::set(const FName& name, size_t idx, const typename PropTraits::Value& value) { - this->set(this->Class->find_prop_and_validate(name), idx, value); + this->set(this->Class()->find_prop_and_validate(name), idx, value); } template void UObject::set(const T* prop, size_t idx, const typename PropTraits::Value& value) { diff --git a/src/unrealsdk/unreal/classes/uproperty.cpp b/src/unrealsdk/unreal/classes/uproperty.cpp index 2168813..e0f9533 100644 --- a/src/unrealsdk/unreal/classes/uproperty.cpp +++ b/src/unrealsdk/unreal/classes/uproperty.cpp @@ -26,11 +26,11 @@ size_t UProperty::class_size(void) { // Rather than bother with a findobject call, we can recover UProperty from any arbitrary object // UObject always has properties on it, we don't need to worry about what class we get auto obj = *unrealsdk::gobjects().begin(); - auto prop = obj->Class->PropertyLink; + auto prop = obj->Class()->PropertyLink; const UStruct* cls = nullptr; - for (auto superfield : prop->Class->superfields()) { - if (superfield->Name == L"Property"_fn) { + for (auto superfield : prop->Class()->superfields()) { + if (superfield->Name() == L"Property"_fn) { cls = superfield; break; } diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index a708444..8a398f4 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -32,13 +32,13 @@ size_t UStruct::class_size(void) { // First, find UClass auto obj = *unrealsdk::gobjects().begin(); - const UClass* class_cls = obj->Class; - for (; class_cls->Class != class_cls; class_cls = class_cls->Class) {} + const UClass* class_cls = obj->Class(); + for (; class_cls->Class() != class_cls; class_cls = class_cls->Class()) {} // Then look through it's superfields for UStruct const UStruct* struct_cls = nullptr; for (auto superfield : class_cls->superfields()) { - if (superfield->Name == L"Struct"_fn) { + if (superfield->Name() == L"Struct"_fn) { struct_cls = superfield; break; } @@ -183,7 +183,7 @@ size_t UStruct::get_struct_size(void) const { UField* UStruct::find(const FName& name) const { for (auto field : this->fields()) { - if (field->Name == name) { + if (field->Name() == name) { return field; } } @@ -193,7 +193,7 @@ UField* UStruct::find(const FName& name) const { UProperty* UStruct::find_prop(const FName& name) const { for (auto prop : this->properties()) { - if (prop->Name == name) { + if (prop->Name() == name) { return prop; } } diff --git a/src/unrealsdk/unreal/find_class.cpp b/src/unrealsdk/unreal/find_class.cpp index 45daba2..90be583 100644 --- a/src/unrealsdk/unreal/find_class.cpp +++ b/src/unrealsdk/unreal/find_class.cpp @@ -12,8 +12,8 @@ class NamedClassCache : public NamedObjectCache { // We can't exactly do a find class lookup here // Instead, just follow the class chain off of an arbitrary object // UClass is the only object whose class is itself - auto cls = (*unrealsdk::gobjects().begin())->Class; - for (; cls->Class != cls; cls = cls->Class) {} + auto cls = (*unrealsdk::gobjects().begin())->Class(); + for (; cls->Class() != cls; cls = cls->Class()) {} return cls; } }; diff --git a/src/unrealsdk/unreal/namedobjectcache.h b/src/unrealsdk/unreal/namedobjectcache.h index 5de4fb6..58b8d6d 100644 --- a/src/unrealsdk/unreal/namedobjectcache.h +++ b/src/unrealsdk/unreal/namedobjectcache.h @@ -45,7 +45,7 @@ class NamedObjectCache { * @return The cached value. */ virtual CacheType add_to_cache(ObjectType* obj) { - cache[obj->Name] = obj; + cache[obj->Name()] = obj; return obj; } diff --git a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp index 8f4491c..46b8924 100644 --- a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp +++ b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp @@ -37,7 +37,7 @@ void FScriptDelegate::set_object(UObject* obj) { return std::nullopt; } - return BoundFunction{.func = obj->Class->find_func_and_validate(this->func_name), + return BoundFunction{.func = obj->Class()->find_func_and_validate(this->func_name), .object = obj}; } @@ -49,7 +49,7 @@ void FScriptDelegate::bind(const std::optional& func) { } this->set_object(func->object); - this->func_name = func->func->Name; + this->func_name = func->func->Name(); } void FScriptDelegate::validate_signature(const std::optional& func, @@ -64,11 +64,11 @@ void FScriptDelegate::validate_signature(const std::optional& fun { UFunction* func_from_find = nullptr; try { - func_from_find = func->object->Class->find_func_and_validate(func->func->Name); + func_from_find = func->object->Class()->find_func_and_validate(func->func->Name()); } catch (const std::invalid_argument&) { throw std::invalid_argument(std::format( "Could not convert function to delegate: could not find function with name '{}'", - func->func->Name)); + func->func->Name())); } if (func_from_find != func->func) { throw std::invalid_argument(utils::narrow(std::format( @@ -97,13 +97,13 @@ void FScriptDelegate::validate_signature(const std::optional& fun auto [func_diff, sig_diff] = std::ranges::mismatch( func_props, sig_props, - [](UProperty* func, UProperty* sig) { return func->Class == sig->Class; }); + [](UProperty* func, UProperty* sig) { return func->Class() == sig->Class(); }); if (func_diff != func_props.end() && sig_diff != sig_props.end()) { throw std::invalid_argument(std::format( "Function signature does not match delegate: function's {} {} != delegate's {} {}", - (*func_diff)->Class->Name, (*func_diff)->Name, (*sig_diff)->Class->Name, - (*sig_diff)->Name)); + (*func_diff)->Class()->Name(), (*func_diff)->Name(), (*sig_diff)->Class()->Name(), + (*sig_diff)->Name())); } if (func_diff != func_props.end() && sig_diff == sig_props.end()) { throw std::invalid_argument( diff --git a/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp b/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp index 9b84365..ad142bb 100644 --- a/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp +++ b/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp @@ -40,9 +40,9 @@ const TPersistentObjectPtr* get_addr_from(const WrappedStruct& wrapped_struct template const TPersistentObjectPtr* get_addr_from_array(const WrappedArray& array, size_t idx) { - if (!array.type->Class->inherits(find_class())) { + if (!array.type->Class()->inherits(find_class())) { throw std::invalid_argument("WrappedArray property was of invalid type " - + (std::string)array.type->Class->Name); + + (std::string)array.type->Class()->Name()); } if (idx >= (size_t)array.base->count) { throw std::out_of_range("WrappedArray index out of range"); @@ -55,7 +55,7 @@ const TPersistentObjectPtr* get_addr_from_array(const WrappedArray& array, si } // namespace const FSoftObjectPath* FSoftObjectPath::get_from(const UObject* obj, FName name, size_t idx) { - return get_from(obj, obj->Class->find_prop_and_validate(name), idx); + return get_from(obj, obj->Class()->find_prop_and_validate(name), idx); } const FSoftObjectPath* FSoftObjectPath::get_from(const WrappedStruct& wrapped_struct, FName name, @@ -79,7 +79,7 @@ const FSoftObjectPath* FSoftObjectPath::get_from_array(const WrappedArray& array } const FLazyObjectPath* FLazyObjectPath::get_from(const UObject* obj, FName name, size_t idx) { - return get_from(obj, obj->Class->find_prop_and_validate(name), idx); + return get_from(obj, obj->Class()->find_prop_and_validate(name), idx); } const FLazyObjectPath* FLazyObjectPath::get_from(const WrappedStruct& wrapped_struct, FName name, diff --git a/src/unrealsdk/unreal/wrappers/bound_function.h b/src/unrealsdk/unreal/wrappers/bound_function.h index eac5037..f58528f 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.h +++ b/src/unrealsdk/unreal/wrappers/bound_function.h @@ -162,7 +162,7 @@ class BoundFunction { if (params.type != this->func) { throw std::runtime_error( "Tried to call function with pre-filled parameters of incorrect type: " - + (std::string)params.type->Name); + + (std::string)params.type->Name()); } this->call_with_params(params.base.get()); diff --git a/src/unrealsdk/unreal/wrappers/property_proxy.cpp b/src/unrealsdk/unreal/wrappers/property_proxy.cpp index 8f1be51..312de2a 100644 --- a/src/unrealsdk/unreal/wrappers/property_proxy.cpp +++ b/src/unrealsdk/unreal/wrappers/property_proxy.cpp @@ -23,7 +23,7 @@ PropertyProxy::PropertyProxy(const PropertyProxy& other) : prop(other.prop), ptr PropertyProxy& PropertyProxy::operator=(const PropertyProxy& other) { if (other.prop != this->prop) { throw std::runtime_error("Property proxy is not instance of " - + (std::string)this->prop->Name); + + (std::string)this->prop->Name()); } if (this->prop != nullptr) { cast(this->prop, [this, &other](const T* prop) { diff --git a/src/unrealsdk/unreal/wrappers/weak_pointer.cpp b/src/unrealsdk/unreal/wrappers/weak_pointer.cpp index 3307aed..8f175e7 100644 --- a/src/unrealsdk/unreal/wrappers/weak_pointer.cpp +++ b/src/unrealsdk/unreal/wrappers/weak_pointer.cpp @@ -14,12 +14,12 @@ WeakPointer::WeakPointer(const UObject* obj) : WeakPointer() { WeakPointer& WeakPointer::operator=(const UObject* obj) { this->obj_addr = reinterpret_cast(obj); if (obj != nullptr) { - this->index = obj->InternalIndex; + this->index = obj->InternalIndex(); - this->outer_addr = reinterpret_cast(obj->Outer); + this->outer_addr = reinterpret_cast(obj->Outer()); this->vftable_addr = reinterpret_cast(obj->vftable); - this->class_addr = reinterpret_cast(obj->Class); - this->name = obj->Name; + this->class_addr = reinterpret_cast(obj->Class()); + this->name = obj->Name(); } return *this; } @@ -37,9 +37,10 @@ UObject* WeakPointer::operator*(void) const { return nullptr; } - if (this->outer_addr != reinterpret_cast(obj->Outer) + if (this->outer_addr != reinterpret_cast(obj->Outer()) || this->vftable_addr != reinterpret_cast(obj->vftable) - || this->class_addr != reinterpret_cast(obj->Class) || this->name != obj->Name) { + || this->class_addr != reinterpret_cast(obj->Class()) + || this->name != obj->Name()) { return nullptr; } diff --git a/src/unrealsdk/unreal/wrappers/wrapped_array.h b/src/unrealsdk/unreal/wrappers/wrapped_array.h index ed4bd11..7c78f2c 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_array.h +++ b/src/unrealsdk/unreal/wrappers/wrapped_array.h @@ -70,7 +70,7 @@ class WrappedArray { */ template void validate_access(size_t idx) const { - auto property_class = this->type->Class->Name; + auto property_class = this->type->Class()->Name(); if (property_class != cls_fname()) { throw std::invalid_argument("WrappedArray property was of invalid type " + (std::string)property_class); diff --git a/src/unrealsdk/unreal/wrappers/wrapped_multicast_delegate.cpp b/src/unrealsdk/unreal/wrappers/wrapped_multicast_delegate.cpp index 5c1139d..89c5de3 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_multicast_delegate.cpp +++ b/src/unrealsdk/unreal/wrappers/wrapped_multicast_delegate.cpp @@ -37,7 +37,7 @@ WrappedMulticastDelegate::WrappedMulticastDelegate(const UFunction* signature, void WrappedMulticastDelegate::call(WrappedStruct& params) const { if (params.type != this->signature) { throw std::runtime_error("Tried to call delegate with parameters of incorrect type: " - + (std::string)params.type->Name); + + (std::string)params.type->Name()); } if (this->base->size() == 0) { diff --git a/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp b/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp index 02dcf1b..53e8425 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp +++ b/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp @@ -13,7 +13,7 @@ namespace unrealsdk::unreal { void copy_struct(uintptr_t dest, const WrappedStruct& src) { if (dest == reinterpret_cast(src.base.get())) { LOG(DEV_WARNING, "Refusing to copy struct of type {} to itself, at address {:p}", - src.type->Name, src.base.get()); + src.type->Name(), src.base.get()); return; } @@ -39,7 +39,7 @@ void destroy_struct(const UStruct* type, uintptr_t addr) { // on trying to destroy the rest // Log it to dev warning - don't want to use error to not freak out casusal users, this // should only really happen if a dev is messing with unsupported property types. - LOG(DEV_WARNING, "Error while destroying '{}' struct: {}", type->Name, ex.what()); + LOG(DEV_WARNING, "Error while destroying '{}' struct: {}", type->Name(), ex.what()); } } } @@ -60,7 +60,7 @@ WrappedStruct::WrappedStruct(WrappedStruct&& other) noexcept WrappedStruct& WrappedStruct::operator=(const WrappedStruct& other) { if (other.type != this->type) { - throw std::runtime_error("Struct is not an instance of " + (std::string)this->type->Name); + throw std::runtime_error("Struct is not an instance of " + (std::string)this->type->Name()); } if (this->base != nullptr && other.base != nullptr) { copy_struct(reinterpret_cast(this->base.get()), other); From e05bf30b986c93355314ce357d5abe35f1dc26da Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 16 Feb 2025 18:07:39 +1300 Subject: [PATCH 44/81] ci cleanup the mingw build is still broken because the version in the runner image is too old. going to need to docker-ize this --- .github/workflows/build.yml | 9 +++------ CMakeLists.txt | 17 ----------------- src/unrealsdk/game/bl2/offsets/uobject.h | 18 ++---------------- src/unrealsdk/pch.h | 1 - .../classes/properties/uclassproperty.cpp | 3 ++- src/unrealsdk/unreal/classes/uproperty.h | 2 +- src/unrealsdk/unreal/classes/ustruct.h | 4 ++-- src/unrealsdk/unreal/offsets.h | 4 ++-- src/unrealsdk/unrealsdk.h | 2 +- 9 files changed, 13 insertions(+), 47 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5024abc..9d7ea3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,8 +7,8 @@ on: [ ] env: - LLVM_MINGW_VERSION: llvm-mingw-20240619-msvcrt-ubuntu-20.04-x86_64 - LLVM_MINGW_DOWNLOAD: https://github.com/mstorsjo/llvm-mingw/releases/download/20240619/llvm-mingw-20240619-msvcrt-ubuntu-20.04-x86_64.tar.xz + LLVM_MINGW_VERSION: llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64 + LLVM_MINGW_DOWNLOAD: https://github.com/mstorsjo/llvm-mingw/releases/download/20250114/llvm-mingw-20250114-msvcrt-ubuntu-20.04-x86_64.tar.xz jobs: build-windows: @@ -52,8 +52,7 @@ jobs: run: cmake --build out/build/${{ matrix.preset }} build-ubuntu: - # Require at least 24 for the mingw build - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest strategy: fail-fast: false @@ -144,7 +143,6 @@ jobs: steps: - name: Setup Clang - if: startswith(matrix.preset, 'clang') uses: egor-tensin/setup-clang@v1 - name: Setup CMake and Ninja @@ -188,7 +186,6 @@ jobs: steps: - name: Setup Clang - if: startswith(matrix.preset, 'clang') uses: egor-tensin/setup-clang@v1 - name: Checkout repository diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af4492..ee4e418 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,17 +32,6 @@ if(MINGW) target_compile_options(_unrealsdk_interface INTERFACE -Werror) endif() -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) -try_compile(supports_format - SOURCE_FROM_CONTENT test.cpp " \ - #include \n \ - #ifndef __cpp_lib_format\n \ - #error\n \ - #endif \ - " - CXX_STANDARD 20 -) - set(CONFIGURE_FILES_DIR "${CMAKE_CURRENT_BINARY_DIR}/configure") configure_file( @@ -56,9 +45,6 @@ include(common_cmake/git_watcher.cmake) add_subdirectory(libs/minhook) add_subdirectory(libs/tomlplusplus) -if(NOT supports_format) - add_subdirectory(libs/fmt) -endif() # If using the clang windows cross compilation toolchain if((NOT CMAKE_HOST_WIN32) @@ -72,9 +58,6 @@ endif() target_include_directories(_unrealsdk_interface INTERFACE "src" ${CONFIGURE_FILES_DIR}) target_link_libraries(_unrealsdk_interface INTERFACE minhook tomlplusplus::tomlplusplus) -if(NOT ${supports_format}) - target_link_libraries(_unrealsdk_interface INTERFACE fmt) -endif() target_compile_definitions(_unrealsdk_interface INTERFACE "${UNREALSDK_UE_VERSION}" diff --git a/src/unrealsdk/game/bl2/offsets/uobject.h b/src/unrealsdk/game/bl2/offsets/uobject.h index 7701d33..f47ee2b 100644 --- a/src/unrealsdk/game/bl2/offsets/uobject.h +++ b/src/unrealsdk/game/bl2/offsets/uobject.h @@ -24,40 +24,26 @@ namespace unrealsdk::game::bl2 { #endif // NOLINTBEGIN(readability-identifier-naming) -class UObject { - public: - UObject() = delete; - UObject(const UObject&) = delete; - UObject(UObject&&) = delete; - UObject& operator=(const UObject&) = delete; - UObject& operator=(UObject&&) = delete; - ~UObject() = delete; - - private: +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct UObject { uintptr_t* vftable; void* HashNext; - public: uint64_t ObjectFlags; - private: void* HashOuterNext; void* StateFrame; unreal::UObject* _Linker; void* _LinkerIndex; - public: int32_t InternalIndex; - private: int32_t NetIndex; - public: UObject* Outer; unreal::FName Name; unreal::UClass* Class; - private: unreal::UObject* ObjectArchetype; }; diff --git a/src/unrealsdk/pch.h b/src/unrealsdk/pch.h index 89f4db1..e6141da 100644 --- a/src/unrealsdk/pch.h +++ b/src/unrealsdk/pch.h @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp index fe19d6c..57084cb 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp @@ -28,7 +28,8 @@ void PropTraits::set(const UClassProperty* prop, } auto meta_cls = prop->get_meta_class(); if (!value->inherits(meta_cls)) { - throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name()); + throw std::runtime_error("Class does not inherit from " + + (std::string)meta_cls->Name()); } } diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index e955fee..645d443 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -119,7 +119,7 @@ class UProperty : public UField { template >> - FieldType read_field(FieldType PropertyType::*field) const { + FieldType read_field(FieldType PropertyType::* field) const { #ifdef UE4 return reinterpret_cast(this)->*field; #else diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index 30353eb..b37580d 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -105,7 +105,7 @@ class UStruct : public UField { template >> - [[nodiscard]] const FieldType& get_field(FieldType SubType::*field) const { + [[nodiscard]] const FieldType& get_field(FieldType SubType::* field) const { #ifdef UE4 return reinterpret_cast(this)->*field; #else @@ -117,7 +117,7 @@ class UStruct : public UField { template >> - FieldType& get_field(FieldType SubType::*field) { + FieldType& get_field(FieldType SubType::* field) { return const_cast(const_cast(this)->get_field(field)); } diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h index 3876c6f..db1a51d 100644 --- a/src/unrealsdk/unreal/offsets.h +++ b/src/unrealsdk/unreal/offsets.h @@ -81,7 +81,7 @@ using copy_cv = std::conditional_t< // Calling the macro UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UOBJECT_FIELDS); -// Creates a class aproximately like the following: +// Creates a class approximately like the following: struct Offsets { offset_type Class; offset_type Outer; @@ -109,7 +109,7 @@ template auto cls = obj->Class()->find(L"MyField"_fn); obj->Class() = some_other_class; -// Storing the reference returned from these functions has the exact same sematics as taking a +// Storing the reference returned from these functions has the exact same semantics as taking a // member reference - i.e. don't do it, it's only valid for the parent object's lifetime. #endif diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index ecd3c26..380bdcd 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -220,7 +220,7 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o /** * @brief Get the offsets list for the currently hooked game. - * @note This is valid to call during initalization. + * @note This is valid to call during initialization. * * @return A reference to the offsets list. */ From 1528ee29f8acd40627cc32e6dbd55a5c95bbc6c2 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 25 Feb 2025 18:53:25 +1300 Subject: [PATCH 45/81] try containerize linux builds --- .cruft.json | 5 +- .devcontainer/.gitignore | 6 ++ .devcontainer/Dockerfile | 65 ++++++++++++++ .devcontainer/clang-cross/devcontainer.json | 7 ++ .devcontainer/llvm-mingw/devcontainer.json | 7 ++ .devcontainer/mingw/devcontainer.json | 7 ++ .github/workflows/build.yml | 96 +++++++-------------- CMakePresets.json | 6 ++ README.md | 23 +++-- src/unrealsdk/unreal/structs/fname.h | 4 +- 10 files changed, 150 insertions(+), 76 deletions(-) create mode 100644 .devcontainer/.gitignore create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/clang-cross/devcontainer.json create mode 100644 .devcontainer/llvm-mingw/devcontainer.json create mode 100644 .devcontainer/mingw/devcontainer.json diff --git a/.cruft.json b/.cruft.json index 83723c0..54007f0 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "git@github.com:bl-sdk/common_dotfiles.git", - "commit": "6b31480199099e9957b18918373a75d979951919", + "commit": "fb30097e1f28ac03961cc0b4d16168e939a43260", "checkout": null, "context": { "cookiecutter": { @@ -15,7 +15,8 @@ "__project_slug": "unrealsdk", "include_cpp": true, "include_py": false, - "_template": "git@github.com:bl-sdk/common_dotfiles.git" + "_template": "git@github.com:bl-sdk/common_dotfiles.git", + "_commit": "fb30097e1f28ac03961cc0b4d16168e939a43260" } }, "directory": null diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore new file mode 100644 index 0000000..670808b --- /dev/null +++ b/.devcontainer/.gitignore @@ -0,0 +1,6 @@ +# Ignore any custom folders outside of our predefined ones, to let you create your own +# One use might be using your own container mapping the install path onto your actual game folder +*/ +!clang-cross +!llvm-mingw +!mingw diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..a9aeee4 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,65 @@ +FROM alpine:latest AS clang-cross +CMD ["/bin/bash"] +RUN <> $GITHUB_PATH - - - name: Setup MinGW - if: startswith(matrix.preset, 'mingw') - uses: egor-tensin/setup-mingw@v2 - with: - platform: ${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'x64')] }} - - - name: Setup Clang - if: startswith(matrix.preset, 'clang-cross') - uses: egor-tensin/setup-clang@v1 - - - name: Restore win sdk cache - if: startswith(matrix.preset, 'clang-cross') - uses: actions/cache@v4 - id: cache-win-sdk - with: - path: ~/win-sdk - key: ${{ runner.os }}-win-sdk - - - name: Setup msitools - if: startswith(matrix.preset, 'clang-cross') && steps.cache-win-sdk.outputs.cache-hit != 'true' - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: msitools - version: ${{ runner.os }}-msitools - - - name: Setup win sdk - if: startswith(matrix.preset, 'clang-cross') && steps.cache-win-sdk.outputs.cache-hit != 'true' - run: | - git clone https://github.com/mstorsjo/msvc-wine.git - msvc-wine/vsdownload.py --accept-license --dest ~/win-sdk Microsoft.VisualStudio.Workload.VCTools - msvc-wine/install.sh ~/win-sdk - rm -r msvc-wine - - - name: Checkout repository and submodules - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Configure CMake - working-directory: ${{ env.GITHUB_WORKSPACE }} - # The extra msvc wine arg won't do anything if we're not cross compiling - run: > - cmake . - --preset ${{ matrix.preset }} - -G Ninja - -DMSVC_WINE_ENV_SCRIPT=$(readlink -f ~)/win-sdk/bin/${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'x64')] }}/msvcenv.sh + - name: Checkout repository and submodules + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build + uses: devcontainers/ci@v0.3 + with: + cacheFrom: ghcr.io/bl-sdk/${{ matrix.toolchain.container }}:latest + configFile: .devcontainer/${{ matrix.toolchain.container }}/devcontainer.json + push: never + # The git watcher cmake thinks something's unsafe? Doesn't happen to me locally. + runCmd: | + git config --global --add safe.directory `pwd` + cmake . --preset ${{ matrix.toolchain.preset }} -G Ninja + cmake --build out/build/${{ matrix.toolchain.preset }} - - name: Build - working-directory: ${{ env.GITHUB_WORKSPACE }} - run: cmake --build out/build/${{ matrix.preset }} # ============================================================================== diff --git a/CMakePresets.json b/CMakePresets.json index 17affae..8e1c658 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -40,6 +40,9 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, + "environment": { + "MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x86/msvcenv.sh" + }, "toolchainFile": "common_cmake/clang-cross-x86.cmake" }, { @@ -50,6 +53,9 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, + "environment": { + "MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x64/msvcenv.sh" + }, "toolchainFile": "common_cmake/clang-cross-x64.cmake" }, { diff --git a/README.md b/README.md index 53dd813..93344f6 100644 --- a/README.md +++ b/README.md @@ -140,13 +140,24 @@ This turns out to be a bit of a problem - MSVC and GNU have different exception both. Practically, this means when cross compiling, you should either compile everything from scratch, or setup Clang to build with the MSVC ABI. [See this blog post for more info](https://apple1417.dev/posts/2023-05-18-debugging-proton). -# Running the SDK by itself +# Building the SDK by itself The shared library is also useful when developing for the sdk itself, since it's the minimal -configuration to get it running. The CMake presets are set up to build this. - -Note that you will need to use some game specific plugin loader to get the dll loaded. It is not set -up to alias any system dlls (since when actually using it as a library you don't want that), you -can't just call it `d3d9.dll` and assume your game will load fine. +configuration to get it running. The CMake presets are set up to build this. There are currently +five supported toolchains, each of which have a few different sdk configurations: + +- MSVC +- Clang (Windows) +- Clang (Cross Compile) * +- MinGW * +- LLVM MinGW * + +The toolchains with an asterix are all cross compiling toolchains. These all also have an associated +dev container, which is the recommended way of building them. The `clang-cross-*` presets in +particular hardcode a path assuming they're running in the container. + +Note that you will need to use some game specific plugin loader to get the `unrealsdk.dll` loaded. +It is not set up to alias any system dlls (since when actually using it as a library you don't want +that), you can't just rename it to `d3d9.dll` and assume your game will load fine. To build: diff --git a/src/unrealsdk/unreal/structs/fname.h b/src/unrealsdk/unreal/structs/fname.h index 8da5ede..1764874 100644 --- a/src/unrealsdk/unreal/structs/fname.h +++ b/src/unrealsdk/unreal/structs/fname.h @@ -83,7 +83,9 @@ struct hash { size_t operator()(const unrealsdk::unreal::FName& name) const { static_assert(sizeof(unrealsdk::unreal::FName) == sizeof(uint64_t), "FName is not same size as a uint64"); - return hash()(*reinterpret_cast(&name)); + uint64_t val{}; + memcpy(&val, &name, sizeof(name)); + return hash()(val); } }; From 6d856a12a9c12d48557b1d22f6464095e3b7292d Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 25 Feb 2025 06:14:48 +0000 Subject: [PATCH 46/81] add dynamic offsets for bl3 --- src/unrealsdk/game/bl2/offsets/uobject.h | 9 +++++- src/unrealsdk/game/bl3/bl3.h | 1 + src/unrealsdk/game/bl3/console.cpp | 6 ++-- src/unrealsdk/game/bl3/object.cpp | 2 +- src/unrealsdk/game/bl3/offsets.cpp | 27 +++++++++++++++++ src/unrealsdk/game/bl3/offsets/uobject.h | 35 ++++++++++++++++++++++ src/unrealsdk/unreal/classes/uobject.h | 14 +++++---- src/unrealsdk/unreal/offsets.h | 12 ++++++++ src/unrealsdk/unreal/wrappers/gobjects.cpp | 2 +- 9 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/unrealsdk/game/bl3/offsets.cpp create mode 100644 src/unrealsdk/game/bl3/offsets/uobject.h diff --git a/src/unrealsdk/game/bl2/offsets/uobject.h b/src/unrealsdk/game/bl2/offsets/uobject.h index f47ee2b..d6a1f51 100644 --- a/src/unrealsdk/game/bl2/offsets/uobject.h +++ b/src/unrealsdk/game/bl2/offsets/uobject.h @@ -26,24 +26,31 @@ namespace unrealsdk::game::bl2 { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct UObject { + private: uintptr_t* vftable; void* HashNext; + public: uint64_t ObjectFlags; + private: void* HashOuterNext; void* StateFrame; unreal::UObject* _Linker; void* _LinkerIndex; + public: int32_t InternalIndex; + private: int32_t NetIndex; - UObject* Outer; + public: + unreal::UObject* Outer; unreal::FName Name; unreal::UClass* Class; + private: unreal::UObject* ObjectArchetype; }; diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index 040fa56..cb60709 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -118,6 +118,7 @@ class BL3Hook : public AbstractHook { const unreal::UObject* obj) const override; void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; + [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; }; template <> diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index ffc690e..ae462e5 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -163,12 +163,12 @@ bool inject_console_hook(hook_manager::Details& hook) { auto local_player = hook.obj->get(L"Player"_fn); auto viewport = local_player->get(L"ViewportClient"_fn); auto console_property = - viewport->Class->find_prop_and_validate(L"ViewportConsole"_fn); + viewport->Class()->find_prop_and_validate(L"ViewportConsole"_fn); console = viewport->get(console_property); if (console == nullptr) { auto default_console = console_property->get_property_class()->ClassDefaultObject(); - console = unrealsdk::construct_object(default_console->Class, default_console->Outer); + console = unrealsdk::construct_object(default_console->Class(), default_console->Outer()); viewport->set(L"ViewportConsole"_fn, console); } @@ -187,7 +187,7 @@ bool inject_console_hook(hook_manager::Details& hook) { // just search through gobjects for the default object ¯\_(ツ)_/¯ auto input_settings_fn = L"InputSettings"_fn; for (const auto& inner_obj : gobjects()) { - if (inner_obj->Class->Name != input_settings_fn) { + if (inner_obj->Class()->Name() != input_settings_fn) { continue; } diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index 49fc526..587fba5 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -58,7 +58,7 @@ UObject* BL3Hook::construct_object(UClass* cls, uint64_t flags, UObject* template_obj) const { if (flags > std::numeric_limits::max()) { - throw std::out_of_range("construct_object flags out of range, only 32-bits are supported") + throw std::out_of_range("construct_object flags out of range, only 32-bits are supported"); } return construct_obj_ptr(cls, outer, name, flags, 0, template_obj, 0 /* false */, nullptr, 0 /* false */); diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp new file mode 100644 index 0000000..d974c2d --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -0,0 +1,27 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl3/bl3.h" +#include "unrealsdk/game/bl3/offsets/uobject.h" +#include "unrealsdk/unreal/offset_list.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::unreal::offsets; + +namespace unrealsdk::game { + +namespace { + +struct OffsetClasses { + using UObject = bl3::UObject; +}; + +const auto OFFSETS = OffsetList::from(); + +} // namespace + +[[nodiscard]] const unreal::offsets::OffsetList& BL3Hook::get_offsets(void) const { + return OFFSETS; +} + +} // namespace unrealsdk::game +#endif diff --git a/src/unrealsdk/game/bl3/offsets/uobject.h b/src/unrealsdk/game/bl3/offsets/uobject.h new file mode 100644 index 0000000..b827588 --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets/uobject.h @@ -0,0 +1,35 @@ +#ifndef UNREALSDK_GAME_BL3_OFFSETS_UOBJECT_H +#define UNREALSDK_GAME_BL3_OFFSETS_UOBJECT_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/structs/fname.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UObject; +class UClass; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl3 { + +// NOLINTBEGIN(readability-identifier-naming) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct UObject { + int32_t ObjectFlags; + int32_t InternalIndex; + unreal::UClass* Class; + unreal::FName Name; + unreal::UObject* Outer; +}; + +// NOLINTEND(readability-identifier-naming) + +} // namespace unrealsdk::game::bl3 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H */ diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index 891c6cb..377827d 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -49,12 +49,14 @@ class UObject { UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECT_FIELDS); #if UE4 - uintptr_t* vftable; - int32_t ObjectFlags; - int32_t InternalIndex; - UClass* Class; - FName Name; - UObject* Outer; + private: + int32_t ObjectFlags_member; + int32_t InternalIndex_member; + UClass* Class_member; + FName Name_member; + UObject* Outer_member; + + public: #else private: diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h index db1a51d..c669629 100644 --- a/src/unrealsdk/unreal/offsets.h +++ b/src/unrealsdk/unreal/offsets.h @@ -30,6 +30,16 @@ using copy_cv = std::conditional_t< reinterpret_cast(&self) + Offsets::get(&Offsets::name)); \ } +#ifdef __MINGW32__ +#define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_PUSH \ + _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"") +#define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_POP _Pragma("GCC diagnostic pop") + +#else +#define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_PUSH +#define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_POP +#endif + /** * @brief Header file macro to defines all the machinery for variable offset fields. * @note Should be placed within the class definition. @@ -45,8 +55,10 @@ using copy_cv = std::conditional_t< X_MACRO(UNREALSDK_OFFSETS__DEFINE_OFFSET_MEMBERS) \ template \ static constexpr Offsets from() { \ + UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_PUSH \ X_MACRO(UNREALSDK_OFFSETS__OFFSETOF_ASSERTS); \ return {X_MACRO(UNREALSDK_OFFSETS__OFFSETOF)}; \ + UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_POP \ } \ static unrealsdk::unreal::offsets::offset_type get( \ unrealsdk::unreal::offsets::offset_type Offsets::* field); \ diff --git a/src/unrealsdk/unreal/wrappers/gobjects.cpp b/src/unrealsdk/unreal/wrappers/gobjects.cpp index 62f4fab..723d993 100644 --- a/src/unrealsdk/unreal/wrappers/gobjects.cpp +++ b/src/unrealsdk/unreal/wrappers/gobjects.cpp @@ -103,7 +103,7 @@ void GObjects::set_weak_object(FWeakObjectPtr* ptr, const UObject* obj) const { if (obj == nullptr) { *ptr = {}; } else { - ptr->object_index = obj->InternalIndex; + ptr->object_index = obj->InternalIndex(); auto obj_item = this->internal->ObjObjects.at(ptr->object_index); auto serial_number = obj_item->SerialNumber.load(); From a7ed7a7f737fceca643588c6569abb813a201097 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 1 Mar 2025 22:36:36 +1300 Subject: [PATCH 47/81] support ufield --- src/unrealsdk/game/bl2/offsets.cpp | 2 ++ src/unrealsdk/game/bl3/offsets.cpp | 2 ++ src/unrealsdk/unreal/classes/ufield.cpp | 10 ++++++++++ src/unrealsdk/unreal/classes/ufield.h | 20 +++++++++++++++++++- src/unrealsdk/unreal/classes/ustruct.cpp | 2 +- src/unrealsdk/unreal/offset_list.h | 5 ++++- src/unrealsdk/unreal/structs/fframe.cpp | 2 +- 7 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/unrealsdk/unreal/classes/ufield.cpp diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index bd8ec46..d869ec1 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -1,6 +1,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -13,6 +14,7 @@ namespace { struct OffsetClasses { using UObject = bl2::UObject; + using UField = generic::UField; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index d974c2d..0e73ff3 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -1,6 +1,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/game/bl3/offsets/uobject.h" +#include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" #if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) @@ -13,6 +14,7 @@ namespace { struct OffsetClasses { using UObject = bl3::UObject; + using UField = generic::UField; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/unreal/classes/ufield.cpp b/src/unrealsdk/unreal/classes/ufield.cpp new file mode 100644 index 0000000..9bccedf --- /dev/null +++ b/src/unrealsdk/unreal/classes/ufield.cpp @@ -0,0 +1,10 @@ +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" + +namespace unrealsdk::unreal { + +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UField, UNREALSDK_UFIELD_FIELDS); + +} diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index 11a20d8..3619646 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -10,6 +10,17 @@ namespace unrealsdk::unreal { #pragma pack(push, 0x4) #endif +namespace offsets::generic { + +template +class UField : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UField* Next; +}; + +} // namespace offsets::generic + class UField : public UObject { public: UField() = delete; @@ -19,8 +30,15 @@ class UField : public UObject { UField& operator=(UField&&) = delete; ~UField() = delete; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UFIELD_FIELDS(X) X(UField*, Next) + // NOLINTNEXTLINE(readability-identifier-naming) - UField* Next; + UNREALSDK_DEFINE_FIELDS_HEADER(UField, UNREALSDK_UFIELD_FIELDS); + + private: + // NOLINTNEXTLINE(readability-identifier-naming) + UField* Next_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 8a398f4..6621039 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -69,7 +69,7 @@ UStruct::FieldIterator::reference UStruct::FieldIterator::operator*() const { UStruct::FieldIterator& UStruct::FieldIterator::operator++() { if (this->field != nullptr) { - this->field = this->field->Next; + this->field = this->field->Next(); } while (this->field == nullptr && this->this_struct != nullptr) { this->this_struct = this->this_struct->SuperField; diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index ce2447d..7bfb506 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -2,6 +2,7 @@ #define UNREALSDK_UNREAL_OFFSET_LIST_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uobject.h" namespace unrealsdk::unreal::offsets { @@ -13,7 +14,9 @@ namespace unrealsdk::unreal::offsets { #endif // NOLINTBEGIN(cppcoreguidelines-macro-usage) -#define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) X(UObject) +#define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) \ + X(UObject) \ + X(UField) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM(name) name::Offsets::from(), diff --git a/src/unrealsdk/unreal/structs/fframe.cpp b/src/unrealsdk/unreal/structs/fframe.cpp index b9def65..75af19b 100644 --- a/src/unrealsdk/unreal/structs/fframe.cpp +++ b/src/unrealsdk/unreal/structs/fframe.cpp @@ -13,7 +13,7 @@ uint8_t* FFrame::extract_current_args(WrappedStruct& args) { for (auto prop = reinterpret_cast(args.type->Children); *this->Code != FFrame::EXPR_TOKEN_END_FUNCTION_PARAMS; - prop = reinterpret_cast(prop->Next)) { + prop = reinterpret_cast(prop->Next())) { if ((prop->PropertyFlags & UProperty::PROP_FLAG_RETURN) != 0) { continue; } From 86628e4546ad894fa48b2a4d1994506c012c8170 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 2 Mar 2025 11:00:58 +1300 Subject: [PATCH 48/81] support ustruct --- src/unrealsdk/game/bl2/offsets.cpp | 5 +- src/unrealsdk/game/bl2/offsets/ufield.h | 18 ++++++ src/unrealsdk/game/bl2/offsets/ustruct.h | 62 +++++++++++++++++++ src/unrealsdk/game/bl3/offsets.cpp | 5 +- src/unrealsdk/game/bl3/offsets/ufield.h | 18 ++++++ src/unrealsdk/game/bl3/offsets/ustruct.h | 55 ++++++++++++++++ src/unrealsdk/game/tps/offsets.cpp | 32 ++++++++++ src/unrealsdk/game/tps/offsets/ustruct.h | 62 +++++++++++++++++++ src/unrealsdk/game/tps/tps.h | 1 + src/unrealsdk/unreal/cast.h | 4 +- src/unrealsdk/unreal/classes/uproperty.cpp | 2 +- src/unrealsdk/unreal/classes/ustruct.cpp | 14 +++-- src/unrealsdk/unreal/classes/ustruct.h | 29 +++++---- src/unrealsdk/unreal/offset_list.h | 4 +- src/unrealsdk/unreal/offsets.h | 2 +- src/unrealsdk/unreal/structs/fframe.cpp | 2 +- .../unreal/wrappers/bound_function.h | 2 +- 17 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 src/unrealsdk/game/bl2/offsets/ufield.h create mode 100644 src/unrealsdk/game/bl2/offsets/ustruct.h create mode 100644 src/unrealsdk/game/bl3/offsets/ufield.h create mode 100644 src/unrealsdk/game/bl3/offsets/ustruct.h create mode 100644 src/unrealsdk/game/tps/offsets.cpp create mode 100644 src/unrealsdk/game/tps/offsets/ustruct.h diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index d869ec1..5cfde03 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -1,6 +1,8 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/bl2.h" +#include "unrealsdk/game/bl2/offsets/ufield.h" #include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/game/bl2/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -14,7 +16,8 @@ namespace { struct OffsetClasses { using UObject = bl2::UObject; - using UField = generic::UField; + using UField = bl2::UField; + using UStruct = bl2::UStruct; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/bl2/offsets/ufield.h b/src/unrealsdk/game/bl2/offsets/ufield.h new file mode 100644 index 0000000..4871f02 --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets/ufield.h @@ -0,0 +1,18 @@ +#ifndef UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H +#define UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H + +#include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offsets.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game::bl2 { + +using UField = unrealsdk::unreal::offsets::generic::UField; + +} + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H */ diff --git a/src/unrealsdk/game/bl2/offsets/ustruct.h b/src/unrealsdk/game/bl2/offsets/ustruct.h new file mode 100644 index 0000000..2691d63 --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets/ustruct.h @@ -0,0 +1,62 @@ +#ifndef UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H +#define UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H + +#include "unrealsdk/game/bl2/offsets/ufield.h" +#include "unrealsdk/unreal/structs/tarray.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; +class UStruct; +class UField; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl2 { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UStruct : public bl2::UField { + private: + uint8_t UnknownData00[0x8]; + + public: + unreal::UStruct* SuperField; + unreal::UField* Children; + + private: + uint16_t PropertySize; + uint8_t UnknownData01[0x1A]; + + public: + unreal::UProperty* PropertyLink; + + private: + uint8_t UnknownData02[0x10]; + + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::bl2 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index 0e73ff3..fb4a82c 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -1,6 +1,8 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl3/bl3.h" +#include "unrealsdk/game/bl3/offsets/ufield.h" #include "unrealsdk/game/bl3/offsets/uobject.h" +#include "unrealsdk/game/bl3/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -14,7 +16,8 @@ namespace { struct OffsetClasses { using UObject = bl3::UObject; - using UField = generic::UField; + using UField = bl3::UField; + using UStruct = bl3::UStruct; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/bl3/offsets/ufield.h b/src/unrealsdk/game/bl3/offsets/ufield.h new file mode 100644 index 0000000..376ed4a --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets/ufield.h @@ -0,0 +1,18 @@ +#ifndef UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H +#define UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H + +#include "unrealsdk/game/bl3/offsets/uobject.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offsets.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game::bl3 { + +using UField = unrealsdk::unreal::offsets::generic::UField; + +} + +#endif + +#endif /* UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H */ diff --git a/src/unrealsdk/game/bl3/offsets/ustruct.h b/src/unrealsdk/game/bl3/offsets/ustruct.h new file mode 100644 index 0000000..7e0d100 --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets/ustruct.h @@ -0,0 +1,55 @@ +#ifndef UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H +#define UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H + +#include "unrealsdk/game/bl3/offsets/ufield.h" +#include "unrealsdk/unreal/structs/tarray.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; +class UStruct; +class UField; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl3 { + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UStruct : public bl3::UField { + public: + unreal::UStruct* SuperField; + unreal::UField* Children; + + private: + int32_t PropertySize; + int32_t MinAlignment; + unreal::TArray Script; + + public: + unreal::UProperty* PropertyLink; + + private: + unreal::UProperty* RefLink; + unreal::UProperty* DestructorLink; + unreal::UProperty* PostConstructLink; + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +} // namespace unrealsdk::game::bl3 + +#endif + +#endif /* UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/tps/offsets.cpp b/src/unrealsdk/game/tps/offsets.cpp new file mode 100644 index 0000000..7e8d896 --- /dev/null +++ b/src/unrealsdk/game/tps/offsets.cpp @@ -0,0 +1,32 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/offsets/ufield.h" +#include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/game/tps/offsets/ustruct.h" +#include "unrealsdk/game/tps/tps.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offset_list.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::unreal::offsets; + +namespace unrealsdk::game { + +namespace { + +struct OffsetClasses { + using UObject = bl2::UObject; + using UField = bl2::UField; + using UStruct = tps::UStruct; +}; + +const auto OFFSETS = OffsetList::from(); + +} // namespace + +[[nodiscard]] const unreal::offsets::OffsetList& TPSHook::get_offsets(void) const { + return OFFSETS; +} + +} // namespace unrealsdk::game +#endif diff --git a/src/unrealsdk/game/tps/offsets/ustruct.h b/src/unrealsdk/game/tps/offsets/ustruct.h new file mode 100644 index 0000000..ca4f7ae --- /dev/null +++ b/src/unrealsdk/game/tps/offsets/ustruct.h @@ -0,0 +1,62 @@ +#ifndef UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H +#define UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H + +#include "unrealsdk/game/bl2/offsets/ufield.h" +#include "unrealsdk/unreal/structs/tarray.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; +class UStruct; +class UField; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::tps { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UStruct : public bl2::UField { + private: + uint8_t UnknownData00[0x8]; + + public: + unreal::UStruct* SuperField; + unreal::UField* Children; + + private: + uint16_t PropertySize; + uint8_t UnknownData01[0x1A]; + + public: + unreal::UProperty* PropertyLink; + + private: + uint8_t UnknownData02[0x4]; + + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::tps + +#endif + +#endif /* UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/tps/tps.h b/src/unrealsdk/game/tps/tps.h index 4d2c3c2..1a304ae 100644 --- a/src/unrealsdk/game/tps/tps.h +++ b/src/unrealsdk/game/tps/tps.h @@ -16,6 +16,7 @@ class TPSHook : public BL2Hook { public: void fname_init(unreal::FName* name, const wchar_t* str, int32_t number) const override; + [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; }; template <> diff --git a/src/unrealsdk/unreal/cast.h b/src/unrealsdk/unreal/cast.h index cada045..7d5b6c5 100644 --- a/src/unrealsdk/unreal/cast.h +++ b/src/unrealsdk/unreal/cast.h @@ -133,11 +133,11 @@ void cast_impl(InputType* obj, if constexpr (i >= std::tuple_size_v) { // But we're supposed to check inherited types, and we have a super field if constexpr (check_inherited_types) { - if (working_class->SuperField != nullptr) { + if (working_class->SuperField() != nullptr) { // Jump back to the start of the tuple, but use the super field return cast_impl( - obj, working_class->SuperField, func, fallback); + obj, working_class->SuperField(), func, fallback); } } diff --git a/src/unrealsdk/unreal/classes/uproperty.cpp b/src/unrealsdk/unreal/classes/uproperty.cpp index e0f9533..da6eea9 100644 --- a/src/unrealsdk/unreal/classes/uproperty.cpp +++ b/src/unrealsdk/unreal/classes/uproperty.cpp @@ -26,7 +26,7 @@ size_t UProperty::class_size(void) { // Rather than bother with a findobject call, we can recover UProperty from any arbitrary object // UObject always has properties on it, we don't need to worry about what class we get auto obj = *unrealsdk::gobjects().begin(); - auto prop = obj->Class()->PropertyLink; + auto prop = obj->Class()->PropertyLink(); const UStruct* cls = nullptr; for (auto superfield : prop->Class()->superfields()) { diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 6621039..85a4f91 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -6,12 +6,16 @@ #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uproperty.h" #include "unrealsdk/unreal/classes/ustruct.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/wrappers/bound_function.h" #include "unrealsdk/unreal/wrappers/gobjects.h" #include "unrealsdk/utils.h" namespace unrealsdk::unreal { +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UStruct, UNREALSDK_USTRUCT_FIELDS); + #ifdef UE3 size_t UStruct::class_size(void) { @@ -72,10 +76,10 @@ UStruct::FieldIterator& UStruct::FieldIterator::operator++() { this->field = this->field->Next(); } while (this->field == nullptr && this->this_struct != nullptr) { - this->this_struct = this->this_struct->SuperField; + this->this_struct = this->this_struct->SuperField(); if (this->this_struct != nullptr) { - this->field = this->this_struct->Children; + this->field = this->this_struct->Children(); } } @@ -95,7 +99,7 @@ bool UStruct::FieldIterator::operator!=(const UStruct::FieldIterator& rhs) const }; utils::IteratorProxy UStruct::fields(void) const { - FieldIterator begin{this, this->Children}; + FieldIterator begin{this, this->Children()}; // If we start out pointing at null (because this struct has no direct children), increment once // to find the actual first field @@ -135,7 +139,7 @@ bool UStruct::PropertyIterator::operator!=(const UStruct::PropertyIterator& rhs) }; utils::IteratorProxy UStruct::properties(void) const { - return {{this->PropertyLink}, {}}; + return {{this->PropertyLink()}, {}}; } #pragma endregion @@ -151,7 +155,7 @@ UStruct::SuperFieldIterator::reference UStruct::SuperFieldIterator::operator*() } UStruct::SuperFieldIterator& UStruct::SuperFieldIterator::operator++() { - this->this_struct = this->this_struct->SuperField; + this->this_struct = this->this_struct->SuperField(); return *this; } UStruct::SuperFieldIterator UStruct::SuperFieldIterator::operator++(int) { diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index b37580d..501d602 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -30,15 +30,24 @@ class UStruct : public UField { UStruct& operator=(UStruct&&) = delete; ~UStruct() = delete; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_USTRUCT_FIELDS(X) \ + X(UStruct*, SuperField) \ + X(UField*, Children) \ + X(UProperty*, PropertyLink) + + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK_DEFINE_FIELDS_HEADER(UStruct, UNREALSDK_USTRUCT_FIELDS); + // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) #ifdef UE4 + private: /* Struct this inherits from, may be null */ - UStruct* SuperField; + UStruct* SuperField_member; /* Pointer to start of linked list of child fields */ - UField* Children; + UField* Children_member; - private: /* Total size of all UProperties, the allocated structure may be larger due to alignment */ int32_t PropertySize; /* Alignment of structure in memory, structure will be at least this large */ @@ -46,11 +55,9 @@ class UStruct : public UField { /* Script bytecode associated with this object */ TArray Script; - public: /* In memory only: Linked list of properties from most-derived to base */ - UProperty* PropertyLink; + UProperty* PropertyLink_member; - private: /* In memory only: Linked list of object reference properties from most-derived to base */ UProperty* RefLink; /* In memory only: Linked list of properties requiring destruction. Note this does not include @@ -65,18 +72,14 @@ class UStruct : public UField { private: uint8_t UnknownData00[0x8]; - public: - UStruct* SuperField; - UField* Children; + UStruct* SuperField_member; + UField* Children_member; - private: uint16_t PropertySize; uint8_t UnknownData01[0x1A]; - public: - UProperty* PropertyLink; + UProperty* PropertyLink_member; - private: uint8_t UnknownData02[0x10]; TArray ScriptObjectReferences; diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 7bfb506..f7472ed 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -4,6 +4,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/classes/ustruct.h" namespace unrealsdk::unreal::offsets { @@ -16,7 +17,8 @@ namespace unrealsdk::unreal::offsets { // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) \ X(UObject) \ - X(UField) + X(UField) \ + X(UStruct) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM(name) name::Offsets::from(), diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h index c669629..3fa9f27 100644 --- a/src/unrealsdk/unreal/offsets.h +++ b/src/unrealsdk/unreal/offsets.h @@ -30,7 +30,7 @@ using copy_cv = std::conditional_t< reinterpret_cast(&self) + Offsets::get(&Offsets::name)); \ } -#ifdef __MINGW32__ +#if defined(__MINGW32__) || defined(__clang__) #define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_PUSH \ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"") #define UNREALSDK_OFFSETS__OFFSETOF_PRAGMA_POP _Pragma("GCC diagnostic pop") diff --git a/src/unrealsdk/unreal/structs/fframe.cpp b/src/unrealsdk/unreal/structs/fframe.cpp index 75af19b..ab8c3eb 100644 --- a/src/unrealsdk/unreal/structs/fframe.cpp +++ b/src/unrealsdk/unreal/structs/fframe.cpp @@ -11,7 +11,7 @@ uint8_t* FFrame::extract_current_args(WrappedStruct& args) { auto args_addr = reinterpret_cast(args.base.get()); uint8_t* original_code = this->Code; - for (auto prop = reinterpret_cast(args.type->Children); + for (auto prop = reinterpret_cast(args.type->Children()); *this->Code != FFrame::EXPR_TOKEN_END_FUNCTION_PARAMS; prop = reinterpret_cast(prop->Next())) { if ((prop->PropertyFlags & UProperty::PROP_FLAG_RETURN) != 0) { diff --git a/src/unrealsdk/unreal/wrappers/bound_function.h b/src/unrealsdk/unreal/wrappers/bound_function.h index f58528f..fd25510 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.h +++ b/src/unrealsdk/unreal/wrappers/bound_function.h @@ -78,7 +78,7 @@ void set_param(WrappedStruct& params, */ template void write_params(WrappedStruct& params, const typename PropTraits::Value&... args) { - UProperty* prop = params.type->PropertyLink; + UProperty* prop = params.type->PropertyLink(); if (prop != nullptr && (prop->PropertyFlags & UProperty::PROP_FLAG_PARAM) == 0) { prop = impl::get_next_param(prop); } From ddbc02626f5be4b86c6714f04ee293939c515ca3 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 2 Mar 2025 11:24:52 +1300 Subject: [PATCH 49/81] support uproperty --- src/unrealsdk/game/bl2/console.cpp | 4 +- src/unrealsdk/game/bl2/hooks.cpp | 4 +- src/unrealsdk/game/bl2/offsets.cpp | 2 + src/unrealsdk/game/bl2/offsets/uproperty.h | 55 ++++++++++++++++++ src/unrealsdk/game/bl3/console.cpp | 2 +- src/unrealsdk/game/bl3/hooks.cpp | 4 +- src/unrealsdk/game/bl3/offsets.cpp | 2 + src/unrealsdk/game/bl3/offsets/uproperty.h | 58 +++++++++++++++++++ src/unrealsdk/game/tps/offsets.cpp | 2 + src/unrealsdk/game/tps/offsets/uproperty.h | 55 ++++++++++++++++++ .../classes/properties/uarrayproperty.cpp | 14 ++--- src/unrealsdk/unreal/classes/ufunction.cpp | 2 +- src/unrealsdk/unreal/classes/uproperty.cpp | 4 ++ src/unrealsdk/unreal/classes/uproperty.h | 47 +++++++++------ src/unrealsdk/unreal/classes/ustruct.cpp | 2 +- src/unrealsdk/unreal/offset_list.h | 4 +- src/unrealsdk/unreal/prop_traits.h | 18 +++--- src/unrealsdk/unreal/structs/fframe.cpp | 4 +- .../unreal/structs/fimplementedinterface.cpp | 2 +- .../unreal/structs/tpersistentobjectptr.cpp | 12 ++-- .../unreal/wrappers/bound_function.cpp | 14 ++--- .../unreal/wrappers/bound_function.h | 6 +- .../unreal/wrappers/property_proxy.cpp | 8 +-- .../unreal/wrappers/unreal_pointer.cpp | 4 +- .../unreal/wrappers/unreal_pointer_funcs.h | 4 +- .../unreal/wrappers/wrapped_array.cpp | 10 ++-- src/unrealsdk/unreal/wrappers/wrapped_array.h | 7 ++- .../unreal/wrappers/wrapped_struct.cpp | 8 +-- 28 files changed, 274 insertions(+), 84 deletions(-) create mode 100644 src/unrealsdk/game/bl2/offsets/uproperty.h create mode 100644 src/unrealsdk/game/bl3/offsets/uproperty.h create mode 100644 src/unrealsdk/game/tps/offsets/uproperty.h diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index e0a0d0a..41f6f7e 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -196,7 +196,7 @@ bool console_command_hook(hook_manager::Details& hook) { hook.obj->set(history_prop, history_top, line); // Increment top - history_top = (history_top + 1) % history_prop->ArrayDim; + history_top = (history_top + 1) % history_prop->ArrayDim(); hook.obj->set(history_top_prop, history_top); // And set current hook.obj->set(history_cur_prop, history_top); @@ -205,7 +205,7 @@ bool console_command_hook(hook_manager::Details& hook) { auto history_bot = hook.obj->get(history_bot_prop); if ((history_bot == -1) || history_bot == history_top) { hook.obj->set(history_bot_prop, - (history_bot + 1) % history_prop->ArrayDim); + (history_bot + 1) % history_prop->ArrayDim()); } hook.obj->get(save_config_func).call(); diff --git a/src/unrealsdk/game/bl2/hooks.cpp b/src/unrealsdk/game/bl2/hooks.cpp index 75ab223..a99f01f 100644 --- a/src/unrealsdk/game/bl2/hooks.cpp +++ b/src/unrealsdk/game/bl2/hooks.cpp @@ -203,7 +203,7 @@ void __fastcall call_function_hook(UObject* obj, if (hook.ret.has_value()) { // Result is a pointer directly to where the property should go, remove the offset hook.ret.copy_to(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!hook_manager::impl::has_post_hooks(*data)) { @@ -212,7 +212,7 @@ void __fastcall call_function_hook(UObject* obj, if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { hook.ret.copy_from(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!block_execution) { diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index 5cfde03..ca30487 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -2,6 +2,7 @@ #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/game/bl2/offsets/ufield.h" #include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/game/bl2/offsets/uproperty.h" #include "unrealsdk/game/bl2/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -18,6 +19,7 @@ struct OffsetClasses { using UObject = bl2::UObject; using UField = bl2::UField; using UStruct = bl2::UStruct; + using UProperty = bl2::UProperty; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/bl2/offsets/uproperty.h b/src/unrealsdk/game/bl2/offsets/uproperty.h new file mode 100644 index 0000000..3f5fc0f --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets/uproperty.h @@ -0,0 +1,55 @@ +#ifndef UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H +#define UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H + +#include "unrealsdk/game/bl2/offsets/ufield.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl2 { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UProperty : public bl2::UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint32_t PropertyFlags; + + private: + uint8_t UnknownData00[0x14]; + + public: + int32_t Offset_Internal; + unreal::UProperty* PropertyLinkNext; + + private: + uint8_t UnknownData01[0x18]; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::bl2 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H */ diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index ae462e5..29bf4ca 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -103,7 +103,7 @@ void console_command_hook(UObject* console_obj, UnmanagedFString* raw_line) { // anything, just lower the count if (dropped_idx != (history_size - 1)) { auto data = reinterpret_cast(history_buffer.base->data); - auto element_size = history_buffer.type->ElementSize; + auto element_size = history_buffer.type->ElementSize(); auto dest = data + (dropped_idx * element_size); auto remaining_size = (history_size - dropped_idx) * element_size; diff --git a/src/unrealsdk/game/bl3/hooks.cpp b/src/unrealsdk/game/bl3/hooks.cpp index 7797540..8a71943 100644 --- a/src/unrealsdk/game/bl3/hooks.cpp +++ b/src/unrealsdk/game/bl3/hooks.cpp @@ -173,7 +173,7 @@ void call_function_hook(UObject* obj, FFrame* stack, void* result, UFunction* fu if (hook.ret.has_value()) { // Result is a pointer directly to where the property should go, remove the offset hook.ret.copy_to(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!hook_manager::impl::has_post_hooks(*data)) { @@ -182,7 +182,7 @@ void call_function_hook(UObject* obj, FFrame* stack, void* result, UFunction* fu if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { hook.ret.copy_from(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!block_execution) { diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index fb4a82c..7491d10 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -2,6 +2,7 @@ #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/game/bl3/offsets/ufield.h" #include "unrealsdk/game/bl3/offsets/uobject.h" +#include "unrealsdk/game/bl3/offsets/uproperty.h" #include "unrealsdk/game/bl3/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -18,6 +19,7 @@ struct OffsetClasses { using UObject = bl3::UObject; using UField = bl3::UField; using UStruct = bl3::UStruct; + using UProperty = bl3::UProperty; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/bl3/offsets/uproperty.h b/src/unrealsdk/game/bl3/offsets/uproperty.h new file mode 100644 index 0000000..1797b32 --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets/uproperty.h @@ -0,0 +1,58 @@ +#ifndef UNREALSDK_GAME_BL3_OFFSETS_UPROPERTY_H +#define UNREALSDK_GAME_BL3_OFFSETS_UPROPERTY_H + +#include "unrealsdk/game/bl3/offsets/ufield.h" +#include "unrealsdk/unreal/structs/fname.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl3 { + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UProperty : public bl3::UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint64_t PropertyFlags; + + private: + uint16_t RepIndex; + uint8_t BlueprintReplicationCondition; + + public: + int32_t Offset_Internal; + + private: + unreal::FName RepNotifyFunc; + + public: + unreal::UProperty* PropertyLinkNext; + + private: + unreal::UProperty* NextRef; + unreal::UProperty* DestructorLinkNext; + unreal::UProperty* PostConstructLinkNext; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +} // namespace unrealsdk::game::bl3 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/tps/offsets.cpp b/src/unrealsdk/game/tps/offsets.cpp index 7e8d896..824a91c 100644 --- a/src/unrealsdk/game/tps/offsets.cpp +++ b/src/unrealsdk/game/tps/offsets.cpp @@ -1,6 +1,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/offsets/ufield.h" #include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/game/tps/offsets/uproperty.h" #include "unrealsdk/game/tps/offsets/ustruct.h" #include "unrealsdk/game/tps/tps.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -18,6 +19,7 @@ struct OffsetClasses { using UObject = bl2::UObject; using UField = bl2::UField; using UStruct = tps::UStruct; + using UProperty = tps::UProperty; }; const auto OFFSETS = OffsetList::from(); diff --git a/src/unrealsdk/game/tps/offsets/uproperty.h b/src/unrealsdk/game/tps/offsets/uproperty.h new file mode 100644 index 0000000..5db82ba --- /dev/null +++ b/src/unrealsdk/game/tps/offsets/uproperty.h @@ -0,0 +1,55 @@ +#ifndef UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H +#define UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H + +#include "unrealsdk/game/bl2/offsets/ufield.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UProperty; + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::tps { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +class UProperty : public bl2::UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint32_t PropertyFlags; + + private: + uint8_t UnknownData00[0x14]; + + public: + int32_t Offset_Internal; + unreal::UProperty* PropertyLinkNext; + + private: + uint8_t UnknownData01[0xC]; +}; + +// NOLINTEND(readability-identifier-naming, readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::tps + +#endif + +#endif /* UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H */ diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp index 47fda56..0025ba4 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp @@ -20,7 +20,7 @@ PropTraits::Value PropTraits::get( uintptr_t addr, const UnrealPointer& parent) { auto inner = prop->get_inner(); - if (prop->ArrayDim > 1) { + if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); } @@ -32,7 +32,7 @@ void PropTraits::set(const UArrayProperty* prop, uintptr_t addr, const Value& value) { auto inner = prop->get_inner(); - if (prop->ArrayDim > 1) { + if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); } @@ -51,11 +51,11 @@ void PropTraits::set(const UArrayProperty* prop, cast(inner, [prop, &arr, &value](const T* inner) { auto new_size = value.size(); - arr->resize(new_size, prop->ElementSize); + arr->resize(new_size, prop->ElementSize()); for (size_t i = 0; i < new_size; i++) { set_property(inner, 0, - reinterpret_cast(arr->data) + (inner->ElementSize * i), + reinterpret_cast(arr->data) + (inner->ElementSize() * i), value.get_at(i)); } }); @@ -63,7 +63,7 @@ void PropTraits::set(const UArrayProperty* prop, void PropTraits::destroy(const UArrayProperty* prop, uintptr_t addr) { auto inner = prop->get_inner(); - if (prop->ArrayDim > 1) { + if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); } @@ -72,8 +72,8 @@ void PropTraits::destroy(const UArrayProperty* prop, uintptr_t a cast(inner, [arr](const T* inner) { for (size_t i = 0; i < arr->size(); i++) { - destroy_property(inner, 0, - reinterpret_cast(arr->data) + (inner->ElementSize * i)); + destroy_property( + inner, 0, reinterpret_cast(arr->data) + (inner->ElementSize() * i)); } }); diff --git a/src/unrealsdk/unreal/classes/ufunction.cpp b/src/unrealsdk/unreal/classes/ufunction.cpp index 8202260..116d31e 100644 --- a/src/unrealsdk/unreal/classes/ufunction.cpp +++ b/src/unrealsdk/unreal/classes/ufunction.cpp @@ -34,7 +34,7 @@ decltype(UFunction::ReturnValueOffset_internal)& UFunction::ReturnValueOffset(vo UProperty* UFunction::find_return_param(void) const { for (auto prop : this->properties()) { - if ((prop->PropertyFlags & UProperty::PROP_FLAG_RETURN) != 0) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_RETURN) != 0) { return prop; } } diff --git a/src/unrealsdk/unreal/classes/uproperty.cpp b/src/unrealsdk/unreal/classes/uproperty.cpp index da6eea9..3c59ef5 100644 --- a/src/unrealsdk/unreal/classes/uproperty.cpp +++ b/src/unrealsdk/unreal/classes/uproperty.cpp @@ -3,12 +3,16 @@ #include "unrealsdk/config.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/wrappers/gobjects.h" #include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UProperty, UNREALSDK_UPROPERTY_FIELDS); + #ifdef UE3 size_t UProperty::class_size(void) { diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index 645d443..36e7c78 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -54,26 +54,38 @@ class UProperty : public UField { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) -#ifdef UE4 - int32_t ArrayDim; - int32_t ElementSize; - uint64_t PropertyFlags; +#if UE4 + using property_flags_type = uint64_t; +#else + using property_flags_type = uint32_t; +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UPROPERTY_FIELDS(X) \ + X(int32_t, ArrayDim) \ + X(int32_t, ElementSize) \ + X(property_flags_type, PropertyFlags) \ + X(int32_t, Offset_Internal) \ + X(UProperty*, PropertyLinkNext) + UNREALSDK_DEFINE_FIELDS_HEADER(UProperty, UNREALSDK_UPROPERTY_FIELDS); + +#ifdef UE4 private: + int32_t ArrayDim_member; + int32_t ElementSize_member; + uint64_t PropertyFlags_member; + uint16_t RepIndex; uint8_t BlueprintReplicationCondition; - public: - int32_t Offset_Internal; + int32_t Offset_Internal_member; - private: FName RepNotifyFunc; - public: /** In memory only: Linked list of properties from most-derived to base **/ - UProperty* PropertyLinkNext; + UProperty* PropertyLinkNext_member; - private: /** In memory only: Linked list of object reference properties from most-derived to base **/ UProperty* NextRef; /** In memory only: Linked list of properties requiring destruction. Note this does not include @@ -82,19 +94,16 @@ class UProperty : public UField { /** In memory only: Linked list of properties requiring post constructor initialization.**/ UProperty* PostConstructLinkNext; // 0x0030(0x0040) MISSED OFFSET #else - - int32_t ArrayDim; - int32_t ElementSize; - uint32_t PropertyFlags; - private: + int32_t ArrayDim_member; + int32_t ElementSize_member; + uint32_t PropertyFlags_member; + uint8_t UnknownData00[0x14]; - public: - int32_t Offset_Internal; - UProperty* PropertyLinkNext; + int32_t Offset_Internal_member; + UProperty* PropertyLinkNext_member; - private: uint8_t UnknownData01[0x18]; /** diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 85a4f91..7b15133 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -122,7 +122,7 @@ UStruct::PropertyIterator::reference UStruct::PropertyIterator::operator*() cons } UStruct::PropertyIterator& UStruct::PropertyIterator::operator++() { - prop = prop->PropertyLinkNext; + prop = prop->PropertyLinkNext(); return *this; } UStruct::PropertyIterator UStruct::PropertyIterator::operator++(int) { diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index f7472ed..3cc750a 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -4,6 +4,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/classes/uproperty.h" #include "unrealsdk/unreal/classes/ustruct.h" namespace unrealsdk::unreal::offsets { @@ -18,7 +19,8 @@ namespace unrealsdk::unreal::offsets { #define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) \ X(UObject) \ X(UField) \ - X(UStruct) + X(UStruct) \ + X(UProperty) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM(name) name::Offsets::from(), diff --git a/src/unrealsdk/unreal/prop_traits.h b/src/unrealsdk/unreal/prop_traits.h index dc08ec5..ee644a3 100644 --- a/src/unrealsdk/unreal/prop_traits.h +++ b/src/unrealsdk/unreal/prop_traits.h @@ -67,11 +67,11 @@ template uintptr_t base_addr, const UnrealPointer& parent = { nullptr}) { - if (idx >= (size_t)prop->ArrayDim) { + if (idx >= (size_t)prop->ArrayDim()) { throw std::out_of_range("Property index out of range"); } - return PropTraits::get(prop, base_addr + prop->Offset_Internal + (idx * prop->ElementSize), - parent); + return PropTraits::get( + prop, base_addr + prop->Offset_Internal() + (idx * prop->ElementSize()), parent); } /** @@ -88,11 +88,11 @@ void set_property(const T* prop, size_t idx, uintptr_t base_addr, const typename PropTraits::Value& value) { - if (idx >= (size_t)prop->ArrayDim) { + if (idx >= (size_t)prop->ArrayDim()) { throw std::out_of_range("Property index out of range"); } - return PropTraits::set(prop, base_addr + prop->Offset_Internal + (idx * prop->ElementSize), - value); + return PropTraits::set( + prop, base_addr + prop->Offset_Internal() + (idx * prop->ElementSize()), value); } /** @@ -105,11 +105,11 @@ void set_property(const T* prop, */ template void destroy_property(const T* prop, size_t idx, uintptr_t base_addr) { - if (idx >= (size_t)prop->ArrayDim) { + if (idx >= (size_t)prop->ArrayDim()) { throw std::out_of_range("Property index out of range"); } - return PropTraits::destroy(prop, - base_addr + prop->Offset_Internal + (idx * prop->ElementSize)); + return PropTraits::destroy( + prop, base_addr + prop->Offset_Internal() + (idx * prop->ElementSize())); } } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/structs/fframe.cpp b/src/unrealsdk/unreal/structs/fframe.cpp index ab8c3eb..215ec74 100644 --- a/src/unrealsdk/unreal/structs/fframe.cpp +++ b/src/unrealsdk/unreal/structs/fframe.cpp @@ -14,12 +14,12 @@ uint8_t* FFrame::extract_current_args(WrappedStruct& args) { for (auto prop = reinterpret_cast(args.type->Children()); *this->Code != FFrame::EXPR_TOKEN_END_FUNCTION_PARAMS; prop = reinterpret_cast(prop->Next())) { - if ((prop->PropertyFlags & UProperty::PROP_FLAG_RETURN) != 0) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_RETURN) != 0) { continue; } unrealsdk::internal::fframe_step( - this, this->Object, reinterpret_cast(args_addr + prop->Offset_Internal)); + this, this->Object, reinterpret_cast(args_addr + prop->Offset_Internal())); } return original_code; diff --git a/src/unrealsdk/unreal/structs/fimplementedinterface.cpp b/src/unrealsdk/unreal/structs/fimplementedinterface.cpp index d49b6aa..d22adff 100644 --- a/src/unrealsdk/unreal/structs/fimplementedinterface.cpp +++ b/src/unrealsdk/unreal/structs/fimplementedinterface.cpp @@ -9,7 +9,7 @@ size_t FImplementedInterface::get_pointer_offset() const { #if UE4 return this->isNative ? 0 : this->PointerOffset; #else - return this->VFTableProperty == nullptr ? 0 : this->VFTableProperty->Offset_Internal; + return this->VFTableProperty == nullptr ? 0 : this->VFTableProperty->Offset_Internal(); #endif } diff --git a/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp b/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp index ad142bb..c3f8bb1 100644 --- a/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp +++ b/src/unrealsdk/unreal/structs/tpersistentobjectptr.cpp @@ -17,24 +17,24 @@ template const TPersistentObjectPtr* get_addr_from(const UObject* obj, const UObjectProperty* prop, size_t idx) { - if (idx >= (size_t)prop->ArrayDim) { + if (idx >= (size_t)prop->ArrayDim()) { throw std::out_of_range("Property index out of range"); } auto addr = - reinterpret_cast(obj) + prop->Offset_Internal + (idx * prop->ElementSize); + reinterpret_cast(obj) + prop->Offset_Internal() + (idx * prop->ElementSize()); return reinterpret_cast*>(addr); } template const TPersistentObjectPtr* get_addr_from(const WrappedStruct& wrapped_struct, const UObjectProperty* prop, size_t idx) { - if (idx >= (size_t)prop->ArrayDim) { + if (idx >= (size_t)prop->ArrayDim()) { throw std::out_of_range("Property index out of range"); } - auto addr = reinterpret_cast(wrapped_struct.base.get()) + prop->Offset_Internal - + (idx * prop->ElementSize); + auto addr = reinterpret_cast(wrapped_struct.base.get()) + prop->Offset_Internal() + + (idx * prop->ElementSize()); return reinterpret_cast*>(addr); } @@ -48,7 +48,7 @@ const TPersistentObjectPtr* get_addr_from_array(const WrappedArray& array, si throw std::out_of_range("WrappedArray index out of range"); } - auto addr = reinterpret_cast(array.base->data) + (array.type->ElementSize * idx); + auto addr = reinterpret_cast(array.base->data) + (array.type->ElementSize() * idx); return reinterpret_cast*>(addr); } diff --git a/src/unrealsdk/unreal/wrappers/bound_function.cpp b/src/unrealsdk/unreal/wrappers/bound_function.cpp index 7a95a5a..7d2b17a 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.cpp +++ b/src/unrealsdk/unreal/wrappers/bound_function.cpp @@ -13,23 +13,23 @@ namespace unrealsdk::unreal { namespace func_params::impl { UProperty* get_next_param(UProperty* prop) { - prop = prop->PropertyLinkNext; - while (prop != nullptr && (prop->PropertyFlags & UProperty::PROP_FLAG_PARAM) == 0) { - prop = prop->PropertyLinkNext; + prop = prop->PropertyLinkNext(); + while (prop != nullptr && (prop->PropertyFlags() & UProperty::PROP_FLAG_PARAM) == 0) { + prop = prop->PropertyLinkNext(); } return prop; } void validate_no_more_params(UProperty* prop) { - for (; prop != nullptr; prop = prop->PropertyLinkNext) { - if ((prop->PropertyFlags & UProperty::PROP_FLAG_PARAM) == 0) { + for (; prop != nullptr; prop = prop->PropertyLinkNext()) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_PARAM) == 0) { continue; } - if ((prop->PropertyFlags & UProperty::PROP_FLAG_RETURN) != 0) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_RETURN) != 0) { continue; } #ifdef UE3 - if ((prop->PropertyFlags & UProperty::PROP_FLAG_OPTIONAL) != 0) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_OPTIONAL) != 0) { continue; } #endif diff --git a/src/unrealsdk/unreal/wrappers/bound_function.h b/src/unrealsdk/unreal/wrappers/bound_function.h index fd25510..853c2f4 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.h +++ b/src/unrealsdk/unreal/wrappers/bound_function.h @@ -52,7 +52,7 @@ void set_param(WrappedStruct& params, if (prop == nullptr) { throw std::runtime_error("Too many parameters to function call!"); } - if (prop->ArrayDim > 1) { + if (prop->ArrayDim() > 1) { throw std::runtime_error( "Function has static array argument - unsure how to handle, aborting!"); } @@ -79,7 +79,7 @@ void set_param(WrappedStruct& params, template void write_params(WrappedStruct& params, const typename PropTraits::Value&... args) { UProperty* prop = params.type->PropertyLink(); - if (prop != nullptr && (prop->PropertyFlags & UProperty::PROP_FLAG_PARAM) == 0) { + if (prop != nullptr && (prop->PropertyFlags() & UProperty::PROP_FLAG_PARAM) == 0) { prop = impl::get_next_param(prop); } @@ -112,7 +112,7 @@ return_type get_return_value(const UFunction* func, const WrappedStruct& para if (ret == nullptr) { throw std::runtime_error("Couldn't find return param!"); } - if (ret->ArrayDim > 1) { + if (ret->ArrayDim() > 1) { throw std::runtime_error( "Function has static array return param - unsure how to handle, aborting!"); } diff --git a/src/unrealsdk/unreal/wrappers/property_proxy.cpp b/src/unrealsdk/unreal/wrappers/property_proxy.cpp index 312de2a..58c7d69 100644 --- a/src/unrealsdk/unreal/wrappers/property_proxy.cpp +++ b/src/unrealsdk/unreal/wrappers/property_proxy.cpp @@ -14,7 +14,7 @@ PropertyProxy::PropertyProxy(UProperty* prop) : prop(prop), ptr(nullptr) {} PropertyProxy::PropertyProxy(const PropertyProxy& other) : prop(other.prop), ptr(nullptr) { if (this->prop != nullptr && other.has_value()) { cast(this->prop, [this, &other](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { this->set(i, other.get(i)); } }); @@ -27,7 +27,7 @@ PropertyProxy& PropertyProxy::operator=(const PropertyProxy& other) { } if (this->prop != nullptr) { cast(this->prop, [this, &other](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { this->set(i, other.get(i)); } }); @@ -49,7 +49,7 @@ void PropertyProxy::copy_to(uintptr_t addr) const { } if (this->prop != nullptr) { cast(this->prop, [this, addr](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { set_property(prop, i, addr, this->get(i)); } }); @@ -62,7 +62,7 @@ void PropertyProxy::copy_from(uintptr_t addr) { } cast(this->prop, [this, addr](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { this->set(i, get_property(prop, i, addr)); } }); diff --git a/src/unrealsdk/unreal/wrappers/unreal_pointer.cpp b/src/unrealsdk/unreal/wrappers/unreal_pointer.cpp index ba06e1f..dccf3eb 100644 --- a/src/unrealsdk/unreal/wrappers/unreal_pointer.cpp +++ b/src/unrealsdk/unreal/wrappers/unreal_pointer.cpp @@ -40,10 +40,10 @@ void UnrealPointerControl::destroy_object(void) { if (prop != nullptr) { // Need to reconstruct the base address, since our pointer may be elsewhere auto prop_base = reinterpret_cast(this) - + sizeof(impl::UnrealPointerControl) - prop->Offset_Internal; + + sizeof(impl::UnrealPointerControl) - prop->Offset_Internal(); cast(prop, [prop_base](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { destroy_property(prop, i, prop_base); } }); diff --git a/src/unrealsdk/unreal/wrappers/unreal_pointer_funcs.h b/src/unrealsdk/unreal/wrappers/unreal_pointer_funcs.h index fc51fac..a4d7241 100644 --- a/src/unrealsdk/unreal/wrappers/unreal_pointer_funcs.h +++ b/src/unrealsdk/unreal/wrappers/unreal_pointer_funcs.h @@ -74,7 +74,7 @@ UnrealPointer::UnrealPointer(const UProperty* prop) requires std::is_void_v : control(nullptr), ptr(nullptr) { // If malloc throws, it should have handled freeing memory if required - auto buf = unrealsdk::u_malloc((prop->ElementSize * prop->ArrayDim) + auto buf = unrealsdk::u_malloc((prop->ElementSize() * prop->ArrayDim()) + sizeof(impl::UnrealPointerControl)); // Otherwise, if we throw during initialization we need to free manually @@ -83,7 +83,7 @@ UnrealPointer::UnrealPointer(const UProperty* prop) this->control = new (buf) impl::UnrealPointerControl(prop); this->ptr = reinterpret_cast(reinterpret_cast(this->control + 1) - - prop->Offset_Internal); + - prop->Offset_Internal()); } catch (const std::exception& ex) { unrealsdk::u_free(buf); LOG(ERROR, "Exception in unreal pointer constructor: {}", ex.what()); diff --git a/src/unrealsdk/unreal/wrappers/wrapped_array.cpp b/src/unrealsdk/unreal/wrappers/wrapped_array.cpp index 61e5590..d94d330 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_array.cpp +++ b/src/unrealsdk/unreal/wrappers/wrapped_array.cpp @@ -12,7 +12,7 @@ WrappedArray::WrappedArray(const UProperty* type, TArray* base, const UnrealPointer& parent) : type(type), base(parent, base) { - if (type->ArrayDim > 1) { + if (type->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); } @@ -30,7 +30,7 @@ void WrappedArray::reserve(size_t new_cap) const { if (new_cap < this->size()) { throw std::invalid_argument("Can't decrease array capacity below it's current size."); } - this->base->reserve(new_cap, this->type->ElementSize); + this->base->reserve(new_cap, this->type->ElementSize()); } void WrappedArray::resize(size_t new_size) { @@ -42,13 +42,13 @@ void WrappedArray::resize(size_t new_size) { } }); - this->base->resize(new_size, this->type->ElementSize); + this->base->resize(new_size, this->type->ElementSize()); // 0-initialize any new entries if (new_size > old_size) { auto data_ptr = reinterpret_cast(this->base->data); - auto start = data_ptr + (old_size * this->type->ElementSize); - auto length = (new_size - old_size) * this->type->ElementSize; + auto start = data_ptr + (old_size * this->type->ElementSize()); + auto length = (new_size - old_size) * this->type->ElementSize(); memset(reinterpret_cast(start), 0, length); } } diff --git a/src/unrealsdk/unreal/wrappers/wrapped_array.h b/src/unrealsdk/unreal/wrappers/wrapped_array.h index 7c78f2c..381b1df 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_array.h +++ b/src/unrealsdk/unreal/wrappers/wrapped_array.h @@ -94,7 +94,7 @@ class WrappedArray { this->validate_access(idx); return get_property( reinterpret_cast(this->type), 0, - reinterpret_cast(this->base->data) + (this->type->ElementSize * idx), + reinterpret_cast(this->base->data) + (this->type->ElementSize() * idx), this->base); } @@ -110,7 +110,8 @@ class WrappedArray { this->validate_access(idx); set_property( reinterpret_cast(this->type), 0, - reinterpret_cast(this->base->data) + (this->type->ElementSize * idx), value); + reinterpret_cast(this->base->data) + (this->type->ElementSize() * idx), + value); } /** @@ -124,7 +125,7 @@ class WrappedArray { this->validate_access(idx); destroy_property( reinterpret_cast(this->type), 0, - reinterpret_cast(this->base->data) + (this->type->ElementSize * idx)); + reinterpret_cast(this->base->data) + (this->type->ElementSize() * idx)); } }; diff --git a/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp b/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp index 53e8425..b41c1d7 100644 --- a/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp +++ b/src/unrealsdk/unreal/wrappers/wrapped_struct.cpp @@ -19,7 +19,7 @@ void copy_struct(uintptr_t dest, const WrappedStruct& src) { for (const auto& prop : src.type->properties()) { cast(prop, [dest, &src](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { set_property(prop, i, dest, src.get(prop, i)); } }); @@ -30,7 +30,7 @@ void destroy_struct(const UStruct* type, uintptr_t addr) { for (const auto& prop : type->properties()) { try { cast(prop, [addr](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { destroy_property(prop, i, addr); } }); @@ -81,12 +81,12 @@ WrappedStruct WrappedStruct::copy_params_only(void) const { auto dest = reinterpret_cast(new_struct.base.get()); for (const auto& prop : this->type->properties()) { - if ((prop->PropertyFlags & UProperty::PROP_FLAG_PARAM) == 0) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_PARAM) == 0) { continue; } cast(prop, [dest, this](const T* prop) { - for (size_t i = 0; i < (size_t)prop->ArrayDim; i++) { + for (size_t i = 0; i < (size_t)prop->ArrayDim(); i++) { set_property(prop, i, dest, this->get(prop, i)); } }); From ca29e8d7fbcbc5ce02d9b4d7fedae1cd5381718d Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 2 Mar 2025 12:55:52 +1300 Subject: [PATCH 50/81] few misc fixups - add vftable back to bl3 uobject (whoops) - supress unused private field warning in ufield - fix wrong name in support settings toml --- src/unrealsdk/game/bl3/offsets/uobject.h | 11 +++++++++++ src/unrealsdk/unreal/classes/ufield.h | 8 ++++++++ supported_settings.toml | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/unrealsdk/game/bl3/offsets/uobject.h b/src/unrealsdk/game/bl3/offsets/uobject.h index b827588..49ad825 100644 --- a/src/unrealsdk/game/bl3/offsets/uobject.h +++ b/src/unrealsdk/game/bl3/offsets/uobject.h @@ -15,10 +15,18 @@ class UClass; namespace unrealsdk::game::bl3 { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif // NOLINTBEGIN(readability-identifier-naming) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct UObject { + private: + uintptr_t* vftable; + + public: int32_t ObjectFlags; int32_t InternalIndex; unreal::UClass* Class; @@ -27,6 +35,9 @@ struct UObject { }; // NOLINTEND(readability-identifier-naming) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif } // namespace unrealsdk::game::bl3 diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index 3619646..de5918a 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -10,6 +10,11 @@ namespace unrealsdk::unreal { #pragma pack(push, 0x4) #endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + namespace offsets::generic { template @@ -46,6 +51,9 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"Field"; }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif #if defined(_MSC_VER) && defined(ARCH_X86) #pragma pack(pop) #endif diff --git a/supported_settings.toml b/supported_settings.toml index e2a4e36..c5f0747 100644 --- a/supported_settings.toml +++ b/supported_settings.toml @@ -60,4 +60,4 @@ log_all_calls_file = "unrealsdk.calls.tsv" # Overrides the virtual function index used when calling `UObject::PostEditChangeProperty`. uobject_post_edit_change_property_vf_index = -1 # Overrides the virtual function index used when calling `UObject::PostEditChangeChainProperty`. -uobject_post_edit_change_chain property_vf_index = -1 +uobject_post_edit_change_chain_property_vf_index = -1 From 1defc143732849fde05db004f5ab0485786a4146 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 4 Mar 2025 20:38:54 +1300 Subject: [PATCH 51/81] formatting pass --- src/unrealsdk/game/bl1/antidebug.cpp | 147 ++++++++++++----------- src/unrealsdk/game/bl1/bl1.h | 3 +- src/unrealsdk/game/selector.cpp | 6 +- src/unrealsdk/unreal/classes/uclass.h | 8 +- src/unrealsdk/unreal/classes/ufunction.h | 2 +- src/unrealsdk/unreal/classes/ustruct.h | 14 +-- 6 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp index 10da82b..353643e 100644 --- a/src/unrealsdk/game/bl1/antidebug.cpp +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -7,33 +7,34 @@ #include "unrealsdk/game/bl1/bl1.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ + && defined(UNREALSDK_GAME_BL1) namespace unrealsdk::game { - namespace { +namespace { // NOLINTBEGIN(readability-identifier-naming) // NOLINTNEXTLINE(modernize-use-using) - need a typedef for calling conventions in msvc - typedef NTSTATUS(WINAPI* NtSetInformationThread_func)( - HANDLE ThreadHandle, - THREAD_INFORMATION_CLASS ThreadInformationClass, - PVOID ThreadInformation, - ULONG ThreadInformationLength); +typedef NTSTATUS(WINAPI* NtSetInformationThread_func)( + HANDLE ThreadHandle, + THREAD_INFORMATION_CLASS ThreadInformationClass, + PVOID ThreadInformation, + ULONG ThreadInformationLength); // NOLINTNEXTLINE(modernize-use-using) - typedef NTSTATUS(WINAPI* NtQueryInformationProcess_func)(HANDLE ProcessHandle, - PROCESSINFOCLASS ProcessInformationClass, - PVOID ProcessInformation, - ULONG ProcessInformationLength, - PULONG ReturnLength); +typedef NTSTATUS(WINAPI* NtQueryInformationProcess_func)(HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength); #if defined(__clang__) - #pragma clang diagnostic push +#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" #endif - constexpr auto ThreadHideFromDebugger = static_cast(17); - constexpr auto ProcessDebugObjectHandle = static_cast(30); +constexpr auto ThreadHideFromDebugger = static_cast(17); +constexpr auto ProcessDebugObjectHandle = static_cast(30); #if defined(__clang__) #pragma clang diagnostic pop @@ -42,75 +43,75 @@ namespace unrealsdk::game { // NOLINTEND(readability-identifier-naming) // NOLINTBEGIN(readability-identifier-naming) - NtSetInformationThread_func NtSetInformationThread_ptr; - NTSTATUS NTAPI NtSetInformationThread_hook(HANDLE ThreadHandle, - THREAD_INFORMATION_CLASS ThreadInformationClass, - PVOID ThreadInformation, - ULONG ThreadInformationLength) { - // NOLINTEND(readability-identifier-naming) - if (ThreadInformationClass == ThreadHideFromDebugger) { - return STATUS_SUCCESS; - } - - return NtSetInformationThread_ptr(ThreadHandle, ThreadInformationClass, ThreadInformation, - ThreadInformationLength); - } - static_assert(std::is_same_v, - "NtSetInformationThread signature is incorrect"); - -// NOLINTBEGIN(readability-identifier-naming) - NtQueryInformationProcess_func NtQueryInformationProcess_ptr; - NTSTATUS WINAPI NtQueryInformationProcess_hook(HANDLE ProcessHandle, - PROCESSINFOCLASS ProcessInformationClass, - PVOID ProcessInformation, - ULONG ProcessInformationLength, - PULONG ReturnLength) { - // NOLINTEND(readability-identifier-naming) - if (ProcessInformationClass == ProcessDebugObjectHandle) { - return STATUS_PORT_NOT_SET; - } - - return NtQueryInformationProcess_ptr(ProcessHandle, ProcessInformationClass, ProcessInformation, - ProcessInformationLength, ReturnLength); - } - static_assert( - std::is_same_v, - "NtQueryInformationProcess signature is incorrect"); +NtSetInformationThread_func NtSetInformationThread_ptr; +NTSTATUS NTAPI NtSetInformationThread_hook(HANDLE ThreadHandle, + THREAD_INFORMATION_CLASS ThreadInformationClass, + PVOID ThreadInformation, + ULONG ThreadInformationLength) { + // NOLINTEND(readability-identifier-naming) + if (ThreadInformationClass == ThreadHideFromDebugger) { + return STATUS_SUCCESS; + } - } // namespace + return NtSetInformationThread_ptr(ThreadHandle, ThreadInformationClass, ThreadInformation, + ThreadInformationLength); +} +static_assert(std::is_same_v, + "NtSetInformationThread signature is incorrect"); - void BL1Hook::hook_antidebug(void) { - MH_STATUS status = MH_OK; +// NOLINTBEGIN(readability-identifier-naming) +NtQueryInformationProcess_func NtQueryInformationProcess_ptr; +NTSTATUS WINAPI NtQueryInformationProcess_hook(HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength) { + // NOLINTEND(readability-identifier-naming) + if (ProcessInformationClass == ProcessDebugObjectHandle) { + return STATUS_PORT_NOT_SET; + } - LPVOID target = nullptr; - status = MH_CreateHookApiEx(L"ntdll", "NtSetInformationThread", - reinterpret_cast(NtSetInformationThread_hook), - reinterpret_cast(&NtSetInformationThread_ptr), &target); + return NtQueryInformationProcess_ptr(ProcessHandle, ProcessInformationClass, ProcessInformation, + ProcessInformationLength, ReturnLength); +} +static_assert( + std::is_same_v, + "NtQueryInformationProcess signature is incorrect"); + +} // namespace + +void BL1Hook::hook_antidebug(void) { + MH_STATUS status = MH_OK; + + LPVOID target = nullptr; + status = MH_CreateHookApiEx(L"ntdll", "NtSetInformationThread", + reinterpret_cast(NtSetInformationThread_hook), + reinterpret_cast(&NtSetInformationThread_ptr), &target); + if (status != MH_OK) { + LOG(ERROR, "Failed to create NtSetInformationThread hook: {:x}", + static_cast(status)); + } else { + status = MH_EnableHook(target); if (status != MH_OK) { - LOG(ERROR, "Failed to create NtSetInformationThread hook: {:x}", + LOG(ERROR, "Failed to enable NtSetInformationThread hook: {:x}", static_cast(status)); - } else { - status = MH_EnableHook(target); - if (status != MH_OK) { - LOG(ERROR, "Failed to enable NtSetInformationThread hook: {:x}", - static_cast(status)); - } } + } - status = MH_CreateHookApiEx(L"ntdll", "NtQueryInformationProcess", - reinterpret_cast(NtQueryInformationProcess_hook), - reinterpret_cast(&NtQueryInformationProcess_ptr), &target); + status = MH_CreateHookApiEx(L"ntdll", "NtQueryInformationProcess", + reinterpret_cast(NtQueryInformationProcess_hook), + reinterpret_cast(&NtQueryInformationProcess_ptr), &target); + if (status != MH_OK) { + LOG(ERROR, "Failed to create NtQueryInformationProcess hook: {:x}", + static_cast(status)); + } else { + status = MH_EnableHook(target); if (status != MH_OK) { - LOG(ERROR, "Failed to create NtQueryInformationProcess hook: {:x}", + LOG(ERROR, "Failed to enable NtQueryInformationProcess hook: {:x}", static_cast(status)); - } else { - status = MH_EnableHook(target); - if (status != MH_OK) { - LOG(ERROR, "Failed to enable NtQueryInformationProcess hook: {:x}", - static_cast(status)); - } } } +} } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 6db486e..7e02ecf 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -3,8 +3,8 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/game/bl1/bl1_config.h" #include "unrealsdk/game/abstract_hook.h" +#include "unrealsdk/game/bl1/bl1_config.h" #include "unrealsdk/game/selector.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ @@ -13,7 +13,6 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { - protected: /** * @brief Hex edits out the `obj dump` array limit message. diff --git a/src/unrealsdk/game/selector.cpp b/src/unrealsdk/game/selector.cpp index cb08886..7eb02ae 100644 --- a/src/unrealsdk/game/selector.cpp +++ b/src/unrealsdk/game/selector.cpp @@ -33,10 +33,10 @@ using all_known_games = std::tuple; using all_known_games = std::tuple; #else using all_known_games = std::tuple; -#endif // else defined(UNREALSDK_GAME_BL1) +#endif // else defined(UNREALSDK_GAME_BL1) -#endif // else !UE4 -#endif // else !ARCH_X64 +#endif // else !UE4 +#endif // else !ARCH_X64 /** * @brief Recursive helper function to find the right game hook. diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index 8547325..a846784 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -36,7 +36,7 @@ class UClass : public UStruct { // ; 2025/01/19 (YYYY/MM/DD) // - - - - private: + private: #ifdef UE4 uint8_t UnknownData00[0x70]; UObject* ClassDefaultObject_internal; @@ -66,15 +66,15 @@ class UClass : public UStruct { public: TArray Interfaces_internal; -#else // defined(UNREALSDK_GAME_BL1) +#else // defined(UNREALSDK_GAME_BL1) // NOTE: I don't know if the class has changed size; Will do a scan in ghidra for 1AC public: uint8_t UnknownData00[0xC0]; - UObject* ClassDefaultObject_internal; // 340b + UObject* ClassDefaultObject_internal; // 340b uint8_t UnknownData01[0x48]; - TArray Interfaces_internal; // 416b + TArray Interfaces_internal; // 416b #endif diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index 9efab9c..66d884d 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -53,7 +53,7 @@ class UFunction : public UStruct { uint8_t UnknownData00[0x6]; #if defined(UNREALSDK_GAME_BL1) - public: // Used in a console command to get address of function + public: // Used in a console command to get address of function #endif void* Func; #endif diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index a7815f1..8a5510c 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -93,16 +93,16 @@ class UStruct : public UField { TArray ScriptObjectReferences; -#else // defined(UNREALSDK_GAME_BL1) +#else // defined(UNREALSDK_GAME_BL1) public: uint8_t UnknownData00[0x08]; - UField* Children; // 76b - uint16_t PropertySize; // 80b - uint8_t UnknownData01[0x1C + 0x02]; // +2 explicit padding - UProperty* PropertyLink; // 112b + UField* Children; // 76b + uint16_t PropertySize; // 80b + uint8_t UnknownData01[0x1C + 0x02]; // +2 explicit padding + UProperty* PropertyLink; // 112b uint8_t UnknownData02[0x10]; - TArray ScriptObjectReferences; // 132b + TArray ScriptObjectReferences; // 132b uint8_t UnknownData03[0x04]; #endif @@ -119,7 +119,7 @@ class UStruct : public UField { */ [[nodiscard]] static size_t class_size(void); -protected: + protected: /** * @brief Reads a field on a UStruct subclass, taking into account it's variable length. * From 53504195d615aef48e362fdfe703deab8f5fdc15 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 4 Mar 2025 21:16:14 +1300 Subject: [PATCH 52/81] compile bl1 into ue3 builds --- CMakeLists.txt | 8 - CMakePresets.json | 28 ---- src/unrealsdk/game/bl1/antidebug.cpp | 3 +- src/unrealsdk/game/bl1/bl1.cpp | 140 +++--------------- src/unrealsdk/game/bl1/bl1.h | 7 +- src/unrealsdk/game/bl1/bl1_config.cpp | 59 -------- src/unrealsdk/game/bl1/bl1_config.h | 26 ---- src/unrealsdk/game/bl1/console.cpp | 26 ++-- src/unrealsdk/game/bl1/globals.cpp | 3 +- src/unrealsdk/game/bl1/hexedits.cpp | 3 +- src/unrealsdk/game/bl1/hooks.cpp | 8 +- src/unrealsdk/game/bl1/memory.cpp | 5 +- src/unrealsdk/game/bl1/object.cpp | 19 +-- src/unrealsdk/game/bl1/offsets.cpp | 35 +++++ .../game/bl1/presistentobjectptr.cpp | 3 +- src/unrealsdk/game/selector.cpp | 6 +- 16 files changed, 82 insertions(+), 297 deletions(-) delete mode 100644 src/unrealsdk/game/bl1/bl1_config.cpp delete mode 100644 src/unrealsdk/game/bl1/bl1_config.h create mode 100644 src/unrealsdk/game/bl1/offsets.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c1a0aa0..324c0e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,9 +6,6 @@ set(UNREALSDK_UE_VERSION "UE4" CACHE STRING "The unreal engine version to build set(UNREALSDK_ARCH "x64" CACHE STRING "The architecture to build the sdk for. One of 'x86' or 'x64'.") set(UNREALSDK_SHARED False CACHE BOOL "If set, compiles as a shared library instead of as an object.") -# NOTE: Temporary -set(UNREALSDK_GAME "NONE" CACHE STRING "Game to build for if any") - add_library(_unrealsdk_interface INTERFACE) target_compile_features(_unrealsdk_interface INTERFACE cxx_std_23) @@ -17,11 +14,6 @@ set_target_properties(_unrealsdk_interface PROPERTIES INTERPROCEDURAL_OPTIMIZATION True ) -# NOTE: This is temporary -if (UNREALSDK_GAME STREQUAL "BL1") - target_compile_definitions(_unrealsdk_interface INTERFACE -DUNREALSDK_GAME_BL1=1) -endif () - if(MSVC) # Under MSVC, enable edit and continue in debug - which conflicts with LTO set_target_properties(${target_name} PROPERTIES diff --git a/CMakePresets.json b/CMakePresets.json index 4b7f21e..8e1c658 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -378,34 +378,6 @@ "_x64", "_release" ] - }, - { - "name": "msvc-bl1-x86-debug", - "displayName": "BL1 x86 Debug (MSVC)", - "inherits": [ - "_base", - "_msvc", - "_ue3", - "_x86", - "_debug" - ], - "cacheVariables": { - "UNREALSDK_GAME": "BL1" - } - }, - { - "name": "msvc-bl1-x86-release", - "displayName": "BL1 x86 Release (MSVC)", - "inherits": [ - "_base", - "_msvc", - "_ue3", - "_x86", - "_release" - ], - "cacheVariables": { - "UNREALSDK_GAME": "BL1" - } } ] } diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp index 353643e..bc0c401 100644 --- a/src/unrealsdk/game/bl1/antidebug.cpp +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -7,8 +7,7 @@ #include "unrealsdk/game/bl1/bl1.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 3da4f6d..930dc05 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -6,8 +6,7 @@ #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -21,8 +20,6 @@ std::atomic_bool bl1_has_initialised{false}; using Clock = std::chrono::steady_clock; -void hook_save_package(void); -void hook_resolve_error(void); bool hook_init_func(void); } // namespace @@ -36,16 +33,6 @@ void BL1Hook::hook(void) { hook_process_event(); hook_call_function(); - if (bl1_cfg::is_log_save_package()) { - hook_save_package(); - } - - // A lot of these types of functions don't belong here and can be implemented as native python - // modules. That will happen eventually. - if (false) { - hook_resolve_error(); - } - // Grabbing these asap seems fine find_fname_init(); find_fframe_step(); @@ -61,6 +48,7 @@ void BL1Hook::hook(void) { // This ensures that the unrealscript is initialised when we exit/return while (!bl1_has_initialised.load(std::memory_order_relaxed)) { + // NOLINTNEXTLINE(readability-magic-numbers) std::this_thread::sleep_for(std::chrono::milliseconds{50}); } @@ -117,7 +105,7 @@ void BL1Hook::find_fframe_step(void) { LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); } -void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject*, void* param) const { +void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject* /*obj*/, void* param) const { ((*fframe_step_gnatives)[*frame->Code++])(frame, param); } @@ -158,99 +146,6 @@ void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number fname_init_ptr(name, str, number, 1, 1); } -// ############################################################################// -// | SAVE PACKAGE | -// ############################################################################// - -namespace { - -// - NOTE - -// This is an function editor it might be useful much later on but right now I will leave it here -// so that it is known. -// - -// NOLINTNEXTLINE(modernize-use-using) -typedef int32_t (*save_package_func)(UObject* InOuter, - UObject* Base, - int64_t TopLevelFlags, - wchar_t* Filename, - void* Error, // non-null - void* Conform, - bool bForceByteSwapping, - bool bWarnOfLongFilename, - uint32_t SaveFlags, - UObject* TargetPlatform, // ? - void* FinalTimeStamp, - int Unknown_00 /* pointer? */); - -save_package_func save_package_ptr = nullptr; - -const constinit Pattern<51> SAVE_PACKAGE_SIG{ - "55 8D AC 24 D4 F3 FF FF 81 EC 2C 0C 00 00 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 81 EC A0" - "03 00 00 A1 ?? ?? ?? ?? 33 C5 89 85 28 0C 00 00 53 56 57 50"}; - -int32_t hook_save_package_detour(UObject* InOuter, - UObject* Base, - int64_t TopLevelFlags, - wchar_t* Filename, - void* Error, - void* Conform, - bool bForceByteSwapping, - bool bWarnOfLongFilename, - uint32_t SaveFlags, - UObject* TargetPlatform, - void* FinalTimeStamp, - int Unknown_00) { - LOG(MISC, L"Saving Package: {:p}, {:p}, {:#016x}, {:#08x}, '{}'", - reinterpret_cast(InOuter), reinterpret_cast(Base), TopLevelFlags, SaveFlags, - Filename); - - int32_t result = save_package_ptr(InOuter, Base, TopLevelFlags, Filename, Error, Conform, - bForceByteSwapping, bWarnOfLongFilename, SaveFlags, - TargetPlatform, FinalTimeStamp, Unknown_00); - return result; -} - -void hook_save_package(void) { - detour(SAVE_PACKAGE_SIG, &hook_save_package_detour, &save_package_ptr, "bl1_hook_save_package"); -} - -} // namespace - -// ############################################################################// -// | EXTENDED DEBUGGING | -// ############################################################################// - -namespace { - -// - NOTE - -// I've seen this function get called with static strings quite a fair bit in ghidra could be useful -// for identifying soft/critical errors. -// -// Disabled for now; -Ry - -const constinit Pattern<28> EXTENDED_DEBUGGING_SIG{ - "51 8B 44 24 14 8B 4C 24 10 8B 54 24 0C 56 8B 74 24 0C 6A 00 50 51 52 68 ?? ?? ?? ??"}; - -// NOLINTNEXTLINE(modernize-use-using) -typedef wchar_t**(__cdecl* resolve_error)(wchar_t**, wchar_t*, wchar_t*, int32_t); - -resolve_error resolve_error_ptr = nullptr; - -wchar_t** resolve_error_detour(wchar_t** obj, wchar_t* error, wchar_t* ctx, int32_t flags) { - wchar_t** msg = resolve_error_ptr(obj, error, ctx, flags); - // [RESOLVE_ERR] Core::ObjectNotFound | 0x0 'Object not found ...' - // LOG(WARNING, L"[RESOLVE_ERR] {}::{} | {:#08x} '{}'", ctx, error, flags, *msg); - return msg; -} - -void hook_resolve_error(void) { - detour(EXTENDED_DEBUGGING_SIG, &resolve_error_detour, &resolve_error_ptr, - "bl1_hook_raise_error"); -} - -} // namespace - // ############################################################################// // | CUSTOM HOOKS | // ############################################################################// @@ -314,11 +209,11 @@ typedef void(__fastcall* init_function)(void* ecx, void* edx); init_function init_func_ptr = nullptr; void __fastcall detour_init_func(void* ecx, void* edx) { - using std::chrono::duration; auto start = Clock::now(); LOG(INFO, "Init function called"); init_func_ptr(ecx, edx); - LOG(INFO, "Init function took {}s to execute", duration(Clock::now() - start).count()); + LOG(INFO, "Init function took {}s to execute", + std::chrono::duration(Clock::now() - start).count()); // When this is true the unrealscript game engine has been created bl1_has_initialised.store(true, std::memory_order_relaxed); @@ -328,12 +223,12 @@ bool hook_init_func(void) { // - NOTE - // I don't think is actually has to be in a loop. // - constexpr uintptr_t INVALID_ADDRESS{0}; - constexpr float INIT_FUNC_TIMEOUT_SECONDS{15.0F}; + constexpr float init_func_timeout_seconds{15.0F}; // Note: The time requested is still constrained to the OS; i.e., min on windows is 16ms // this should just be 'as fast as possible' so 20ms for all systems should suffice. - auto delay = std::chrono::milliseconds{bl1_cfg::init_func_poll_rate_ms()}; + constexpr std::chrono::milliseconds delay{20}; + LOG(INFO, "Attempting to hook init function with polling rate: {}ms", delay.count()); auto start = Clock::now(); @@ -343,9 +238,8 @@ bool hook_init_func(void) { // Steam uintptr_t addr = INIT_FUNC_STEAM_SIG.sigscan_nullable(); - if (addr != INVALID_ADDRESS) { - bool ok = detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_steam_init_func"); - if (!ok) { + if (addr != 0) { + if (!detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_steam_init_func")) { continue; } return true; @@ -353,17 +247,16 @@ bool hook_init_func(void) { // UDK 141 addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); - if (addr != INVALID_ADDRESS) { - bool ok = detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_udk_init_func"); - if (!ok) { + if (addr != 0) { + if (!detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_udk_init_func")) { continue; } return true; } // This should never really be hit adding it just incase though - using std::chrono::duration; - if (duration(Clock::now() - start).count() > INIT_FUNC_TIMEOUT_SECONDS) { + if (std::chrono::duration(Clock::now() - start).count() + > init_func_timeout_seconds) { LOG(ERROR, "It has taken too long to hook the init function; Aborting..."); return false; } @@ -376,10 +269,11 @@ bool hook_init_func(void) { // | NOT IMPLEMENTED | // ############################################################################// -void BL1Hook::ftext_as_culture_invariant(unreal::FText*, TemporaryFString&&) const { +void BL1Hook::ftext_as_culture_invariant(unreal::FText* /*text*/, + TemporaryFString&& /*str*/) const { throw_version_error("FTexts are not implemented in UE3"); } } // namespace unrealsdk::game -#endif \ No newline at end of file +#endif diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 7e02ecf..f4bd22a 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -4,11 +4,9 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/abstract_hook.h" -#include "unrealsdk/game/bl1/bl1_config.h" #include "unrealsdk/game/selector.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { @@ -108,7 +106,7 @@ class BL1Hook : public AbstractHook { [[nodiscard]] unreal::UObject* construct_object(unreal::UClass* cls, unreal::UObject* outer, const unreal::FName& name, - decltype(unreal::UObject::ObjectFlags) flags, + uint64_t flags, unreal::UObject* template_obj) const override; [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const override; @@ -128,6 +126,7 @@ class BL1Hook : public AbstractHook { const unreal::UObject* obj) const override; void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; + [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; }; template <> diff --git a/src/unrealsdk/game/bl1/bl1_config.cpp b/src/unrealsdk/game/bl1/bl1_config.cpp deleted file mode 100644 index 3b95bdb..0000000 --- a/src/unrealsdk/game/bl1/bl1_config.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Date : 24/11/2024 -// Project : unrealsdk -// Author : -Ry -// - -#include "unrealsdk/game/bl1/bl1_config.h" -#include "unrealsdk/config.h" - -namespace unrealsdk::game::bl1_cfg { - -// ############################################################################// -// | TOML DEF | -// ############################################################################// - -namespace { -using TomlKeyType = std::string_view; - -// clang-format off - -// USDK -constexpr TomlKeyType CONSOLE_KEY = "unrealsdk.console_key"; - -// BL1SDK -constexpr TomlKeyType LOG_LOAD_PACKAGE = "unrealsdk.bl1.log_load_package"; -constexpr TomlKeyType LOCKING_CONSOLE_WRITE = "unrealsdk.bl1.locking_console_write"; - -constexpr TomlKeyType INIT_FUNC_POLL_RATE_MS = "unrealsdk.bl1.init_func_poll_rate_ms"; -constexpr int64_t DEFAULT_POLL_RATE_MS = 20; - -// clang-format on - -} // namespace - -// ############################################################################// -// | FUNCTION IMPLS | -// ############################################################################// - -std::string console_key(void) { - return std::string{config::get_str(CONSOLE_KEY).value_or("Tilde")}; -} - -bool is_locking_console_write(void) { - return config::get_bool(LOCKING_CONSOLE_WRITE).value_or(false); -} - -bool is_log_load_package(void) { - return config::get_bool(LOG_LOAD_PACKAGE).value_or(false); -} - -bool is_log_save_package(void) { - return config::get_bool(LOG_LOAD_PACKAGE).value_or(false); -} - -int64_t init_func_poll_rate_ms(void) { - return config::get_int(INIT_FUNC_POLL_RATE_MS).value_or(DEFAULT_POLL_RATE_MS); -} - -} // namespace unrealsdk::game::bl1_cfg \ No newline at end of file diff --git a/src/unrealsdk/game/bl1/bl1_config.h b/src/unrealsdk/game/bl1/bl1_config.h deleted file mode 100644 index 425311d..0000000 --- a/src/unrealsdk/game/bl1/bl1_config.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Date : 24/11/2024 -// Project : unrealsdk -// Author : -Ry -// -#ifndef UNREALSDK_BL1_CONFIG_H -#define UNREALSDK_BL1_CONFIG_H - -#include "unrealsdk/pch.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) - -namespace unrealsdk::game::bl1_cfg { - -std::string console_key(void); - -bool is_log_load_package(void); -bool is_log_save_package(void); -int64_t init_func_poll_rate_ms(void); - -} // namespace unrealsdk::game::bl1_cfg - -#endif - -#endif diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index bc54174..261372b 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -3,14 +3,16 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/commands.h" +#include "unrealsdk/config.h" #include "unrealsdk/hook_manager.h" #include "unrealsdk/unreal/classes/properties/copyable_property.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/properties/ustrproperty.h" +#include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/classes/uobject_funcs.h" #include "unrealsdk/unreal/find_class.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; @@ -31,7 +33,7 @@ BoundFunction console_output_text{}; bool say_bypass_hook(const hook_manager::Details& hook) { static const auto console_command_func = - hook.obj->Class->find_func_and_validate(L"ConsoleCommand"_fn); + hook.obj->Class()->find_func_and_validate(L"ConsoleCommand"_fn); static const auto command_property = hook.args->type->find_prop_and_validate(L"Command"_fn); @@ -45,18 +47,18 @@ bool console_command_hook(const hook_manager::Details& hook) { hook.args->type->find_prop_and_validate(L"Command"_fn); static const auto history_prop = - hook.obj->Class->find_prop_and_validate(L"History"_fn); + hook.obj->Class()->find_prop_and_validate(L"History"_fn); static const auto history_top_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryTop"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryTop"_fn); static const auto history_bot_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryBot"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryBot"_fn); static const auto history_cur_prop = - hook.obj->Class->find_prop_and_validate(L"HistoryCur"_fn); + hook.obj->Class()->find_prop_and_validate(L"HistoryCur"_fn); static const UFunction* purge_command_func = - hook.obj->Class->find_func_and_validate(L"PurgeCommandFromHistory"_fn); + hook.obj->Class()->find_func_and_validate(L"PurgeCommandFromHistory"_fn); static const UFunction* save_config_func = - hook.obj->Class->find_func_and_validate(L"SaveConfig"_fn); + hook.obj->Class()->find_func_and_validate(L"SaveConfig"_fn); auto line = hook.args->get(command_property); @@ -82,7 +84,7 @@ bool console_command_hook(const hook_manager::Details& hook) { hook.obj->set(history_prop, history_top, line); // Increment top - history_top = (history_top + 1) % history_prop->ArrayDim; + history_top = (history_top + 1) % history_prop->ArrayDim(); hook.obj->set(history_top_prop, history_top); // And set current hook.obj->set(history_cur_prop, history_top); @@ -91,7 +93,7 @@ bool console_command_hook(const hook_manager::Details& hook) { auto history_bot = hook.obj->get(history_bot_prop); if ((history_bot == -1) || history_bot == history_top) { hook.obj->set(history_bot_prop, - (history_bot + 1) % history_prop->ArrayDim); + (history_bot + 1) % history_prop->ArrayDim()); } hook.obj->get(save_config_func).call(); @@ -166,7 +168,7 @@ bool inject_console_hook(const hook_manager::Details& hook) { if (existing_console_key != L"None"_fn || existing_console_key == L"Undefine"_fn) { LOG(MISC, "Console key is already set to '{}'", existing_console_key); } else { - auto wanted_console_key = bl1_cfg::console_key(); + std::string wanted_console_key{config::get_str("unrealsdk.console_key").value_or("Tilde")}; console->set(L"ConsoleKey"_fn, FName{wanted_console_key}); LOG(MISC, "Set console key to '{}'", wanted_console_key); diff --git a/src/unrealsdk/game/bl1/globals.cpp b/src/unrealsdk/game/bl1/globals.cpp index 84a5411..05f46f9 100644 --- a/src/unrealsdk/game/bl1/globals.cpp +++ b/src/unrealsdk/game/bl1/globals.cpp @@ -6,8 +6,7 @@ #include "unrealsdk/unreal/wrappers/gnames.h" #include "unrealsdk/unreal/wrappers/gobjects.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp index e79d524..412942c 100644 --- a/src/unrealsdk/game/bl1/hexedits.cpp +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -3,8 +3,7 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/memory.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 172389b..916f065 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -2,7 +2,6 @@ #include "unrealsdk/game/bl1/bl1.h" -#include #include "unrealsdk/hook_manager.h" #include "unrealsdk/locks.h" #include "unrealsdk/memory.h" @@ -10,8 +9,7 @@ #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/unreal/wrappers/wrapped_struct.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -186,7 +184,7 @@ void __fastcall call_function_hook(UObject* obj, if (hook.ret.has_value()) { // Result is a pointer directly to where the property should go, remove the offset hook.ret.copy_to(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!has_post_hooks(*data)) { @@ -195,7 +193,7 @@ void __fastcall call_function_hook(UObject* obj, if (hook.ret.prop != nullptr && !hook.ret.has_value() && !block_execution) { hook.ret.copy_from(reinterpret_cast(result) - - hook.ret.prop->Offset_Internal); + - hook.ret.prop->Offset_Internal()); } if (!block_execution) { diff --git a/src/unrealsdk/game/bl1/memory.cpp b/src/unrealsdk/game/bl1/memory.cpp index 0dc99d9..55b8f54 100644 --- a/src/unrealsdk/game/bl1/memory.cpp +++ b/src/unrealsdk/game/bl1/memory.cpp @@ -4,8 +4,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/alignment.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -63,7 +62,7 @@ void BL1Hook::find_gmalloc(void) { // always be non-null. gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); - if (!gmalloc) { + if (gmalloc == nullptr) { // This happens if we try to hook it too early. throw std::runtime_error("GMalloc is null"); } diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp index 1dae9be..dfc9c5d 100644 --- a/src/unrealsdk/game/bl1/object.cpp +++ b/src/unrealsdk/game/bl1/object.cpp @@ -5,8 +5,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/structs/fstring.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; @@ -62,7 +61,7 @@ void BL1Hook::find_construct_object(void) { UObject* BL1Hook::construct_object(UClass* cls, UObject* outer, const FName& name, - decltype(UObject::ObjectFlags) flags, + uint64_t flags, UObject* template_obj) const { return construct_obj_ptr(cls, outer, name, flags, template_obj, nullptr, nullptr, 0 /* false */); @@ -198,20 +197,8 @@ const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ } // namespace -namespace { -UObject* bl1_load_package_detour(const UObject* outer, const wchar_t* name, uint32_t flags) { - LOG(INFO, L"[LOAD_PACKAGE] ~ {:p}, '{}', {:#010x}", (void*)outer, name, flags); - return load_package_ptr(outer, name, flags); -} -} // namespace - void BL1Hook::find_load_package(void) { - if (bl1_cfg::is_log_load_package()) { - detour(LOAD_PACKAGE_PATTERN, &bl1_load_package_detour, &load_package_ptr, - "bl1_load_package_detour"); - } else { - load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); - } + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan_nullable(); LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); } diff --git a/src/unrealsdk/game/bl1/offsets.cpp b/src/unrealsdk/game/bl1/offsets.cpp new file mode 100644 index 0000000..a03e2ba --- /dev/null +++ b/src/unrealsdk/game/bl1/offsets.cpp @@ -0,0 +1,35 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl1/bl1.h" +#include "unrealsdk/game/bl2/offsets/ufield.h" +#include "unrealsdk/game/bl2/offsets/uobject.h" +#include "unrealsdk/game/bl2/offsets/uproperty.h" +#include "unrealsdk/game/bl2/offsets/ustruct.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offset_list.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::unreal::offsets; + +namespace unrealsdk::game { + +namespace { + +struct OffsetClasses { + // TODO + using UObject = bl2::UObject; + using UField = bl2::UField; + using UStruct = bl2::UStruct; + using UProperty = bl2::UProperty; +}; + +const auto OFFSETS = OffsetList::from(); + +} // namespace + +[[nodiscard]] const unreal::offsets::OffsetList& BL1Hook::get_offsets(void) const { + return OFFSETS; +} + +} // namespace unrealsdk::game +#endif diff --git a/src/unrealsdk/game/bl1/presistentobjectptr.cpp b/src/unrealsdk/game/bl1/presistentobjectptr.cpp index 5bc8e5c..19a67fe 100644 --- a/src/unrealsdk/game/bl1/presistentobjectptr.cpp +++ b/src/unrealsdk/game/bl1/presistentobjectptr.cpp @@ -3,8 +3,7 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) \ - && defined(UNREALSDK_GAME_BL1) +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/selector.cpp b/src/unrealsdk/game/selector.cpp index 7eb02ae..9c10f80 100644 --- a/src/unrealsdk/game/selector.cpp +++ b/src/unrealsdk/game/selector.cpp @@ -29,11 +29,7 @@ using all_known_games = std::tuple; #error No known games for UE4 x86 #else -#if !defined(UNREALSDK_GAME_BL1) -using all_known_games = std::tuple; -#else -using all_known_games = std::tuple; -#endif // else defined(UNREALSDK_GAME_BL1) +using all_known_games = std::tuple; #endif // else !UE4 #endif // else !ARCH_X64 From 2b8cd5c0077570e915510567910eacbd37f5c07f Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 4 Mar 2025 21:32:32 +1300 Subject: [PATCH 53/81] linting fixups --- src/unrealsdk/game/bl1/bl1.cpp | 10 ++++++++++ src/unrealsdk/game/bl1/hooks.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 930dc05..514710c 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -113,6 +113,11 @@ void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject* /*obj*/, void* // | FNAME INIT | // ############################################################################// +#if defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" // thiscall on non-class +#endif + namespace { const constinit Pattern<34> FNAME_INIT_SIG{ @@ -146,6 +151,10 @@ void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number fname_init_ptr(name, str, number, 1, 1); } +#if defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif + // ############################################################################// // | CUSTOM HOOKS | // ############################################################################// @@ -204,6 +213,7 @@ const constinit Pattern<45> INIT_FUNC_141_UDK_SIG{ }; // Not sure if this is a __thiscall or an actual __fastcall; ecx is used. +// NOLINTNEXTLINE(modernize-use-using) typedef void(__fastcall* init_function)(void* ecx, void* edx); init_function init_func_ptr = nullptr; diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 916f065..ff05f90 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -122,8 +122,8 @@ static_assert(std::is_same_v, } // namespace void BL1Hook::hook_call_function(void) { - bool locking = locks::FunctionCall::enabled(); - detour(CALL_FUNCTION_SIG, locking ? locking_call_function_hook : call_function_hook, + detour(CALL_FUNCTION_SIG, + locks::FunctionCall::enabled() ? locking_call_function_hook : call_function_hook, &call_function_ptr, "CallFunction"); } From 23f81e01ece12aa4e34a16cd61ef0be3075c166c Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 4 Mar 2025 21:47:38 +1300 Subject: [PATCH 54/81] update cruft --- .cruft.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index 54007f0..a2be77e 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "git@github.com:bl-sdk/common_dotfiles.git", - "commit": "fb30097e1f28ac03961cc0b4d16168e939a43260", + "commit": "42b55e9819c8f5f23d67c6ef97a5764a616ec080", "checkout": null, "context": { "cookiecutter": { @@ -16,7 +16,7 @@ "include_cpp": true, "include_py": false, "_template": "git@github.com:bl-sdk/common_dotfiles.git", - "_commit": "fb30097e1f28ac03961cc0b4d16168e939a43260" + "_commit": "42b55e9819c8f5f23d67c6ef97a5764a616ec080" } }, "directory": null From 1c32c89877f653cb01c20b67c5eb781ed114232d Mon Sep 17 00:00:00 2001 From: apple1417 Date: Thu, 6 Mar 2025 17:25:28 +1300 Subject: [PATCH 55/81] rearrange game specific offsets, add bl1 ones this is based off of the thought that most types will be generic, so it's less overhead than creating 40 files per game --- src/unrealsdk/game/bl1/offsets.cpp | 22 ++-- src/unrealsdk/game/bl1/offsets.h | 90 ++++++++++++++++ src/unrealsdk/game/bl2/offsets.cpp | 21 ++-- src/unrealsdk/game/bl2/offsets.h | 114 +++++++++++++++++++++ src/unrealsdk/game/bl2/offsets/ufield.h | 18 ---- src/unrealsdk/game/bl2/offsets/uobject.h | 69 ------------- src/unrealsdk/game/bl2/offsets/uproperty.h | 55 ---------- src/unrealsdk/game/bl2/offsets/ustruct.h | 62 ----------- src/unrealsdk/game/bl3/offsets.cpp | 21 ++-- src/unrealsdk/game/bl3/offsets.h | 98 ++++++++++++++++++ src/unrealsdk/game/bl3/offsets/ufield.h | 18 ---- src/unrealsdk/game/bl3/offsets/uobject.h | 46 --------- src/unrealsdk/game/bl3/offsets/uproperty.h | 58 ----------- src/unrealsdk/game/bl3/offsets/ustruct.h | 55 ---------- src/unrealsdk/game/tps/offsets.cpp | 22 ++-- src/unrealsdk/game/tps/offsets.h | 78 ++++++++++++++ src/unrealsdk/game/tps/offsets/uproperty.h | 55 ---------- src/unrealsdk/game/tps/offsets/ustruct.h | 62 ----------- src/unrealsdk/unreal/classes/ufield.h | 10 -- src/unrealsdk/unreal/classes/uproperty.h | 14 --- src/unrealsdk/unreal/classes/ustruct.h | 22 ---- src/unrealsdk/unreal/offset_list.h | 28 ++++- 22 files changed, 429 insertions(+), 609 deletions(-) create mode 100644 src/unrealsdk/game/bl1/offsets.h create mode 100644 src/unrealsdk/game/bl2/offsets.h delete mode 100644 src/unrealsdk/game/bl2/offsets/ufield.h delete mode 100644 src/unrealsdk/game/bl2/offsets/uobject.h delete mode 100644 src/unrealsdk/game/bl2/offsets/uproperty.h delete mode 100644 src/unrealsdk/game/bl2/offsets/ustruct.h create mode 100644 src/unrealsdk/game/bl3/offsets.h delete mode 100644 src/unrealsdk/game/bl3/offsets/ufield.h delete mode 100644 src/unrealsdk/game/bl3/offsets/uobject.h delete mode 100644 src/unrealsdk/game/bl3/offsets/uproperty.h delete mode 100644 src/unrealsdk/game/bl3/offsets/ustruct.h create mode 100644 src/unrealsdk/game/tps/offsets.h delete mode 100644 src/unrealsdk/game/tps/offsets/uproperty.h delete mode 100644 src/unrealsdk/game/tps/offsets/ustruct.h diff --git a/src/unrealsdk/game/bl1/offsets.cpp b/src/unrealsdk/game/bl1/offsets.cpp index a03e2ba..6028b46 100644 --- a/src/unrealsdk/game/bl1/offsets.cpp +++ b/src/unrealsdk/game/bl1/offsets.cpp @@ -1,9 +1,6 @@ #include "unrealsdk/pch.h" +#include "unrealsdk/game/bl1/offsets.h" #include "unrealsdk/game/bl1/bl1.h" -#include "unrealsdk/game/bl2/offsets/ufield.h" -#include "unrealsdk/game/bl2/offsets/uobject.h" -#include "unrealsdk/game/bl2/offsets/uproperty.h" -#include "unrealsdk/game/bl2/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -12,23 +9,16 @@ using namespace unrealsdk::unreal::offsets; namespace unrealsdk::game { - +namespace bl1 { namespace { -struct OffsetClasses { - // TODO - using UObject = bl2::UObject; - using UField = bl2::UField; - using UStruct = bl2::UStruct; - using UProperty = bl2::UProperty; -}; - -const auto OFFSETS = OffsetList::from(); +constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); -} // namespace +} +} // namespace bl1 [[nodiscard]] const unreal::offsets::OffsetList& BL1Hook::get_offsets(void) const { - return OFFSETS; + return bl1::OFFSETS; } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h new file mode 100644 index 0000000..815b89d --- /dev/null +++ b/src/unrealsdk/game/bl1/offsets.h @@ -0,0 +1,90 @@ +#ifndef UNREALSDK_GAME_BL1_OFFSETS_H +#define UNREALSDK_GAME_BL1_OFFSETS_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/offsets.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game::bl1 { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) + +class UStruct; + +using UObject = bl2::UObject; + +class UField : public UObject { + public: + // Superfield is considered to be part of UStruct in the rest of the sdk + // Luckily, we can just expose it here and it'll all get picked up right + UStruct* SuperField; + UField* Next; +}; + +class UProperty : public UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint32_t PropertyFlags; + + private: + uint8_t UnknownData00[0x14]; + + public: + int32_t Offset_Internal; + UProperty* PropertyLinkNext; + + private: + uint8_t UnknownData01[0x20]; +}; + +class UStruct : public UField { + private: + uint8_t UnknownData00[0x08]; + + public: + UField* Children; + uint16_t PropertySize; + + private: + uint8_t UnknownData01[0x1C + 0x02]; + + public: + UProperty* PropertyLink; + + private: + uint8_t UnknownData02[0x10]; + + public: + unreal::TArray ScriptObjectReferences; + + private: + uint8_t UnknownData03[0x04]; +}; + +// NOLINTEND(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::bl1 + +#endif + +#endif /* UNREALSDK_GAME_BL1_OFFSETS_H */ diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index ca30487..fbc1abd 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -1,9 +1,6 @@ #include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/game/bl2/bl2.h" -#include "unrealsdk/game/bl2/offsets/ufield.h" -#include "unrealsdk/game/bl2/offsets/uobject.h" -#include "unrealsdk/game/bl2/offsets/uproperty.h" -#include "unrealsdk/game/bl2/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -12,22 +9,16 @@ using namespace unrealsdk::unreal::offsets; namespace unrealsdk::game { - +namespace bl2 { namespace { -struct OffsetClasses { - using UObject = bl2::UObject; - using UField = bl2::UField; - using UStruct = bl2::UStruct; - using UProperty = bl2::UProperty; -}; - -const auto OFFSETS = OffsetList::from(); +constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); -} // namespace +} +} // namespace bl2 [[nodiscard]] const unreal::offsets::OffsetList& BL2Hook::get_offsets(void) const { - return OFFSETS; + return bl2::OFFSETS; } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h new file mode 100644 index 0000000..cd1ca00 --- /dev/null +++ b/src/unrealsdk/game/bl2/offsets.h @@ -0,0 +1,114 @@ +#ifndef UNREALSDK_GAME_BL2_OFFSETS_H +#define UNREALSDK_GAME_BL2_OFFSETS_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/tarray.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UClass; + +} + +namespace unrealsdk::game::bl2 { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) + +struct UObject { + private: + uintptr_t* vftable; + void* HashNext; + + public: + uint64_t ObjectFlags; + + private: + void* HashOuterNext; + void* StateFrame; + UObject* _Linker; + void* _LinkerIndex; + + public: + int32_t InternalIndex; + + private: + int32_t NetIndex; + + public: + UObject* Outer; + unreal::FName Name; + unreal::UClass* Class; // TODO + + private: + UObject* ObjectArchetype; +}; + +using UField = unreal::offsets::generic::UField; + +class UProperty : public UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint32_t PropertyFlags; + + private: + uint8_t UnknownData00[0x14]; + + public: + int32_t Offset_Internal; + unreal::UProperty* PropertyLinkNext; + + private: + uint8_t UnknownData01[0x18]; +}; + +class UStruct : public UField { + private: + uint8_t UnknownData00[0x8]; + + public: + UStruct* SuperField; + UField* Children; + + private: + uint16_t PropertySize; + uint8_t UnknownData01[0x1A]; + + public: + UProperty* PropertyLink; + + private: + uint8_t UnknownData02[0x10]; + + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::bl2 + +#endif + +#endif /* UNREALSDK_GAME_BL2_OFFSETS_H */ diff --git a/src/unrealsdk/game/bl2/offsets/ufield.h b/src/unrealsdk/game/bl2/offsets/ufield.h deleted file mode 100644 index 4871f02..0000000 --- a/src/unrealsdk/game/bl2/offsets/ufield.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H -#define UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H - -#include "unrealsdk/game/bl2/offsets/uobject.h" -#include "unrealsdk/unreal/classes/ufield.h" -#include "unrealsdk/unreal/offsets.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::game::bl2 { - -using UField = unrealsdk::unreal::offsets::generic::UField; - -} - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_UFIELD_H */ diff --git a/src/unrealsdk/game/bl2/offsets/uobject.h b/src/unrealsdk/game/bl2/offsets/uobject.h deleted file mode 100644 index d6a1f51..0000000 --- a/src/unrealsdk/game/bl2/offsets/uobject.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H -#define UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H - -#include "unrealsdk/pch.h" -#include "unrealsdk/unreal/structs/fname.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UObject; -class UClass; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl2 { - -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(push, 0x4) -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -struct UObject { - private: - uintptr_t* vftable; - void* HashNext; - - public: - uint64_t ObjectFlags; - - private: - void* HashOuterNext; - void* StateFrame; - unreal::UObject* _Linker; - void* _LinkerIndex; - - public: - int32_t InternalIndex; - - private: - int32_t NetIndex; - - public: - unreal::UObject* Outer; - unreal::FName Name; - unreal::UClass* Class; - - private: - unreal::UObject* ObjectArchetype; -}; - -// NOLINTEND(readability-identifier-naming) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(pop) -#endif - -} // namespace unrealsdk::game::bl2 - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H */ diff --git a/src/unrealsdk/game/bl2/offsets/uproperty.h b/src/unrealsdk/game/bl2/offsets/uproperty.h deleted file mode 100644 index 3f5fc0f..0000000 --- a/src/unrealsdk/game/bl2/offsets/uproperty.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H -#define UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H - -#include "unrealsdk/game/bl2/offsets/ufield.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl2 { - -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(push, 0x4) -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UProperty : public bl2::UField { - public: - int32_t ArrayDim; - int32_t ElementSize; - uint32_t PropertyFlags; - - private: - uint8_t UnknownData00[0x14]; - - public: - int32_t Offset_Internal; - unreal::UProperty* PropertyLinkNext; - - private: - uint8_t UnknownData01[0x18]; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(pop) -#endif - -} // namespace unrealsdk::game::bl2 - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_UPROPERTY_H */ diff --git a/src/unrealsdk/game/bl2/offsets/ustruct.h b/src/unrealsdk/game/bl2/offsets/ustruct.h deleted file mode 100644 index 2691d63..0000000 --- a/src/unrealsdk/game/bl2/offsets/ustruct.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H -#define UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H - -#include "unrealsdk/game/bl2/offsets/ufield.h" -#include "unrealsdk/unreal/structs/tarray.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; -class UStruct; -class UField; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl2 { - -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(push, 0x4) -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UStruct : public bl2::UField { - private: - uint8_t UnknownData00[0x8]; - - public: - unreal::UStruct* SuperField; - unreal::UField* Children; - - private: - uint16_t PropertySize; - uint8_t UnknownData01[0x1A]; - - public: - unreal::UProperty* PropertyLink; - - private: - uint8_t UnknownData02[0x10]; - - unreal::TArray ScriptObjectReferences; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(pop) -#endif - -} // namespace unrealsdk::game::bl2 - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index 7491d10..87f59cc 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -1,9 +1,6 @@ #include "unrealsdk/pch.h" +#include "unrealsdk/game/bl3/offsets.h" #include "unrealsdk/game/bl3/bl3.h" -#include "unrealsdk/game/bl3/offsets/ufield.h" -#include "unrealsdk/game/bl3/offsets/uobject.h" -#include "unrealsdk/game/bl3/offsets/uproperty.h" -#include "unrealsdk/game/bl3/offsets/ustruct.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" @@ -12,22 +9,16 @@ using namespace unrealsdk::unreal::offsets; namespace unrealsdk::game { - +namespace bl3 { namespace { -struct OffsetClasses { - using UObject = bl3::UObject; - using UField = bl3::UField; - using UStruct = bl3::UStruct; - using UProperty = bl3::UProperty; -}; - -const auto OFFSETS = OffsetList::from(); +constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); -} // namespace +} +} // namespace bl3 [[nodiscard]] const unreal::offsets::OffsetList& BL3Hook::get_offsets(void) const { - return OFFSETS; + return bl3::OFFSETS; } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h new file mode 100644 index 0000000..5bfa3a4 --- /dev/null +++ b/src/unrealsdk/game/bl3/offsets.h @@ -0,0 +1,98 @@ +#ifndef UNREALSDK_GAME_BL3_OFFSETS_H +#define UNREALSDK_GAME_BL3_OFFSETS_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/tarray.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::unreal { + +class UClass; // TODO + +} // namespace unrealsdk::unreal + +namespace unrealsdk::game::bl3 { + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) + +struct UObject { + private: + uintptr_t* vftable; + + public: + int32_t ObjectFlags; + int32_t InternalIndex; + unreal::UClass* Class; // TODO + unreal::FName Name; + UObject* Outer; +}; + +using UField = unreal::offsets::generic::UField; + +class UProperty : public UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint64_t PropertyFlags; + + private: + uint16_t RepIndex; + uint8_t BlueprintReplicationCondition; + + public: + int32_t Offset_Internal; + + private: + unreal::FName RepNotifyFunc; + + public: + UProperty* PropertyLinkNext; + + private: + UProperty* NextRef; + UProperty* DestructorLinkNext; + UProperty* PostConstructLinkNext; +}; + +class UStruct : public bl3::UField { + public: + UStruct* SuperField; + UField* Children; + + private: + int32_t PropertySize; + int32_t MinAlignment; + unreal::TArray Script; + + public: + UProperty* PropertyLink; + + private: + UProperty* RefLink; + UProperty* DestructorLink; + UProperty* PostConstructLink; + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +} // namespace unrealsdk::game::bl3 + +#endif + +#endif /* UNREALSDK_GAME_BL3_OFFSETS_H */ diff --git a/src/unrealsdk/game/bl3/offsets/ufield.h b/src/unrealsdk/game/bl3/offsets/ufield.h deleted file mode 100644 index 376ed4a..0000000 --- a/src/unrealsdk/game/bl3/offsets/ufield.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H -#define UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H - -#include "unrealsdk/game/bl3/offsets/uobject.h" -#include "unrealsdk/unreal/classes/ufield.h" -#include "unrealsdk/unreal/offsets.h" - -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::game::bl3 { - -using UField = unrealsdk::unreal::offsets::generic::UField; - -} - -#endif - -#endif /* UNREALSDK_GAME_BL3_OFFSETS_UFIELD_H */ diff --git a/src/unrealsdk/game/bl3/offsets/uobject.h b/src/unrealsdk/game/bl3/offsets/uobject.h deleted file mode 100644 index 49ad825..0000000 --- a/src/unrealsdk/game/bl3/offsets/uobject.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef UNREALSDK_GAME_BL3_OFFSETS_UOBJECT_H -#define UNREALSDK_GAME_BL3_OFFSETS_UOBJECT_H - -#include "unrealsdk/pch.h" -#include "unrealsdk/unreal/structs/fname.h" - -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UObject; -class UClass; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl3 { - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -struct UObject { - private: - uintptr_t* vftable; - - public: - int32_t ObjectFlags; - int32_t InternalIndex; - unreal::UClass* Class; - unreal::FName Name; - unreal::UObject* Outer; -}; - -// NOLINTEND(readability-identifier-naming) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -} // namespace unrealsdk::game::bl3 - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_UOBJECT_H */ diff --git a/src/unrealsdk/game/bl3/offsets/uproperty.h b/src/unrealsdk/game/bl3/offsets/uproperty.h deleted file mode 100644 index 1797b32..0000000 --- a/src/unrealsdk/game/bl3/offsets/uproperty.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef UNREALSDK_GAME_BL3_OFFSETS_UPROPERTY_H -#define UNREALSDK_GAME_BL3_OFFSETS_UPROPERTY_H - -#include "unrealsdk/game/bl3/offsets/ufield.h" -#include "unrealsdk/unreal/structs/fname.h" - -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl3 { - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UProperty : public bl3::UField { - public: - int32_t ArrayDim; - int32_t ElementSize; - uint64_t PropertyFlags; - - private: - uint16_t RepIndex; - uint8_t BlueprintReplicationCondition; - - public: - int32_t Offset_Internal; - - private: - unreal::FName RepNotifyFunc; - - public: - unreal::UProperty* PropertyLinkNext; - - private: - unreal::UProperty* NextRef; - unreal::UProperty* DestructorLinkNext; - unreal::UProperty* PostConstructLinkNext; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -} // namespace unrealsdk::game::bl3 - -#endif - -#endif /* UNREALSDK_GAME_BL2_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/bl3/offsets/ustruct.h b/src/unrealsdk/game/bl3/offsets/ustruct.h deleted file mode 100644 index 7e0d100..0000000 --- a/src/unrealsdk/game/bl3/offsets/ustruct.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H -#define UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H - -#include "unrealsdk/game/bl3/offsets/ufield.h" -#include "unrealsdk/unreal/structs/tarray.h" - -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; -class UStruct; -class UField; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::bl3 { - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UStruct : public bl3::UField { - public: - unreal::UStruct* SuperField; - unreal::UField* Children; - - private: - int32_t PropertySize; - int32_t MinAlignment; - unreal::TArray Script; - - public: - unreal::UProperty* PropertyLink; - - private: - unreal::UProperty* RefLink; - unreal::UProperty* DestructorLink; - unreal::UProperty* PostConstructLink; - unreal::TArray ScriptObjectReferences; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -} // namespace unrealsdk::game::bl3 - -#endif - -#endif /* UNREALSDK_GAME_BL3_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/game/tps/offsets.cpp b/src/unrealsdk/game/tps/offsets.cpp index 824a91c..35b2e81 100644 --- a/src/unrealsdk/game/tps/offsets.cpp +++ b/src/unrealsdk/game/tps/offsets.cpp @@ -1,10 +1,6 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/game/bl2/offsets/ufield.h" -#include "unrealsdk/game/bl2/offsets/uobject.h" -#include "unrealsdk/game/tps/offsets/uproperty.h" -#include "unrealsdk/game/tps/offsets/ustruct.h" +#include "unrealsdk/game/tps/offsets.h" #include "unrealsdk/game/tps/tps.h" -#include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -12,22 +8,16 @@ using namespace unrealsdk::unreal::offsets; namespace unrealsdk::game { - +namespace tps { namespace { -struct OffsetClasses { - using UObject = bl2::UObject; - using UField = bl2::UField; - using UStruct = tps::UStruct; - using UProperty = tps::UProperty; -}; - -const auto OFFSETS = OffsetList::from(); +constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); -} // namespace +} +} // namespace tps [[nodiscard]] const unreal::offsets::OffsetList& TPSHook::get_offsets(void) const { - return OFFSETS; + return tps::OFFSETS; } } // namespace unrealsdk::game diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h new file mode 100644 index 0000000..71775f2 --- /dev/null +++ b/src/unrealsdk/game/tps/offsets.h @@ -0,0 +1,78 @@ +#ifndef UNREALSDK_GAME_TPS_OFFSETS_H +#define UNREALSDK_GAME_TPS_OFFSETS_H + +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/offsets.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +namespace unrealsdk::game::tps { + +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(push, 0x4) +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif +// NOLINTBEGIN(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) + +using UObject = bl2::UObject; +using UField = bl2::UField; + +class UProperty : public UField { + public: + int32_t ArrayDim; + int32_t ElementSize; + uint32_t PropertyFlags; + + private: + uint8_t UnknownData00[0x14]; + + public: + int32_t Offset_Internal; + unreal::UProperty* PropertyLinkNext; + + private: + uint8_t UnknownData01[0xC]; +}; + +class UStruct : public UField { + private: + uint8_t UnknownData00[0x8]; + + public: + UStruct* SuperField; + UField* Children; + + private: + uint16_t PropertySize; + uint8_t UnknownData01[0x1A]; + + public: + UProperty* PropertyLink; + + private: + uint8_t UnknownData02[0x4]; + + unreal::TArray ScriptObjectReferences; +}; + +// NOLINTEND(cppcoreguidelines-pro-type-member-init, +// readability-identifier-naming, +// readability-magic-numbers) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#if defined(_MSC_VER) && defined(ARCH_X86) +#pragma pack(pop) +#endif + +} // namespace unrealsdk::game::tps + +#endif + +#endif /* UNREALSDK_GAME_TPS_OFFSETS_H */ diff --git a/src/unrealsdk/game/tps/offsets/uproperty.h b/src/unrealsdk/game/tps/offsets/uproperty.h deleted file mode 100644 index 5db82ba..0000000 --- a/src/unrealsdk/game/tps/offsets/uproperty.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H -#define UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H - -#include "unrealsdk/game/bl2/offsets/ufield.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::tps { - -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(push, 0x4) -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UProperty : public bl2::UField { - public: - int32_t ArrayDim; - int32_t ElementSize; - uint32_t PropertyFlags; - - private: - uint8_t UnknownData00[0x14]; - - public: - int32_t Offset_Internal; - unreal::UProperty* PropertyLinkNext; - - private: - uint8_t UnknownData01[0xC]; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(pop) -#endif - -} // namespace unrealsdk::game::tps - -#endif - -#endif /* UNREALSDK_GAME_TPS_OFFSETS_UPROPERTY_H */ diff --git a/src/unrealsdk/game/tps/offsets/ustruct.h b/src/unrealsdk/game/tps/offsets/ustruct.h deleted file mode 100644 index ca4f7ae..0000000 --- a/src/unrealsdk/game/tps/offsets/ustruct.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H -#define UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H - -#include "unrealsdk/game/bl2/offsets/ufield.h" -#include "unrealsdk/unreal/structs/tarray.h" - -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) - -namespace unrealsdk::unreal { - -class UProperty; -class UStruct; -class UField; - -} // namespace unrealsdk::unreal - -namespace unrealsdk::game::tps { - -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(push, 0x4) -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif -// NOLINTBEGIN(readability-identifier-naming, readability-magic-numbers) - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) -class UStruct : public bl2::UField { - private: - uint8_t UnknownData00[0x8]; - - public: - unreal::UStruct* SuperField; - unreal::UField* Children; - - private: - uint16_t PropertySize; - uint8_t UnknownData01[0x1A]; - - public: - unreal::UProperty* PropertyLink; - - private: - uint8_t UnknownData02[0x4]; - - unreal::TArray ScriptObjectReferences; -}; - -// NOLINTEND(readability-identifier-naming, readability-magic-numbers) -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#if defined(_MSC_VER) && defined(ARCH_X86) -#pragma pack(pop) -#endif - -} // namespace unrealsdk::game::tps - -#endif - -#endif /* UNREALSDK_GAME_TPS_OFFSETS_USTRUCT_H */ diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index 582b528..de5918a 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -35,8 +35,6 @@ class UField : public UObject { UField& operator=(UField&&) = delete; ~UField() = delete; -#if !defined(UNREALSDK_GAME_BL1) - // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UFIELD_FIELDS(X) X(UField*, Next) @@ -46,14 +44,6 @@ class UField : public UObject { private: // NOLINTNEXTLINE(readability-identifier-naming) UField* Next_member; - -#else // defined(UNREALSDK_GAME_BL1) - - public: - UStruct* SuperField; - UField* Next; - -#endif }; template <> diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index 8d92f0b..412735b 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -95,7 +95,6 @@ class UProperty : public UField { UProperty* PostConstructLinkNext; // 0x0030(0x0040) MISSED OFFSET #else -#if !defined(UNREALSDK_GAME_BL1) private: int32_t ArrayDim_member; int32_t ElementSize_member; @@ -108,19 +107,6 @@ class UProperty : public UField { uint8_t UnknownData01[0x18]; -#else // defined(UNREALSDK_GAME_BL1) - - public: // Total Size: 140b - int32_t ArrayDim; // 68b - int32_t ElementSize; // 72b - uint32_t PropertyFlags; // 76b - uint8_t UnknownData00[0x14]; - int32_t Offset_Internal; // 100b - UProperty* PropertyLinkNext; // 104b - uint8_t UnknownData01[0x20]; - -#endif - /** * @brief Gets the size of this class. * diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index 8a5510c..1c846fb 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -41,12 +41,6 @@ class UStruct : public UField { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) - // - NOTE: Merge Conflicts (ustruct.h and uclass.h) - // Commit: 7803d86de171a5e19e66688074d8809329e42bb8 - // Manually resolved; only tested compilation for BL1, UE3, and UE4 but no more than that. - // ; 2025/01/19 (YYYY/MM/DD) - // - - - - #ifdef UE4 private: /* Struct this inherits from, may be null */ @@ -76,8 +70,6 @@ class UStruct : public UField { TArray ScriptObjectReferences; #else -#if !defined(UNREALSDK_GAME_BL1) - private: uint8_t UnknownData00[0x8]; @@ -93,20 +85,6 @@ class UStruct : public UField { TArray ScriptObjectReferences; -#else // defined(UNREALSDK_GAME_BL1) - - public: - uint8_t UnknownData00[0x08]; - UField* Children; // 76b - uint16_t PropertySize; // 80b - uint8_t UnknownData01[0x1C + 0x02]; // +2 explicit padding - UProperty* PropertyLink; // 112b - uint8_t UnknownData02[0x10]; - TArray ScriptObjectReferences; // 132b - uint8_t UnknownData03[0x04]; - -#endif - #endif // See the description in 'uproperty.h', we have the same issue here. `UnknownData02` is 0x10 in diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 3cc750a..30979c6 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -22,8 +22,11 @@ namespace unrealsdk::unreal::offsets { X(UStruct) \ X(UProperty) -#define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) name::Offsets name; -#define UNREALSDK_OFFSETS__NESTED_FROM(name) name::Offsets::from(), +#define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; +#define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ + unrealsdk::unreal::name::Offsets::from(), +#define UNREALSDK_OFFSETS__NESTED_FROM_NAMESPACE(name) \ + unrealsdk::unreal::name::Offsets::from(), // NOLINTEND(cppcoreguidelines-macro-usage) @@ -31,12 +34,31 @@ struct OffsetList { // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS) + /** + * @brief Creates a full offset list based off of the templated type. + * @note Assumes all the game specific types are subtypes - i.e. `T::UObject` is valid. + * + * @tparam T The templated type holding all the game-specific types. + * @return A new offset list. + */ template static constexpr OffsetList from(void) { - return {UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__NESTED_FROM)}; + return {UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__NESTED_FROM_TYPE)}; } }; +/** + * @brief Macro to create a full offset list within the current namespace. + * @note Assumes all the game specific types are directly accessible - i.e just `UObject` is valid. + * + * @return A new offset list. + */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define OFFSET_LIST_FROM_NAMESPACE() \ + unrealsdk::unreal::offsets::OffsetList { \ + UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__NESTED_FROM_NAMESPACE) \ + } + #if defined(_MSC_VER) && defined(ARCH_X86) #pragma pack(pop) #endif From c344f55e0421395fbf179fa9d06558124cfb5ecf Mon Sep 17 00:00:00 2001 From: apple1417 Date: Thu, 6 Mar 2025 17:51:00 +1300 Subject: [PATCH 56/81] convert all ustruct subclasses to new system this means we now fully cover bl1's changes too --- src/unrealsdk/game/bl1/offsets.h | 27 +++++++- src/unrealsdk/game/bl2/offsets.h | 68 +++++++++++++++++-- src/unrealsdk/game/bl3/offsets.h | 43 ++++++++++-- src/unrealsdk/game/tps/offsets.h | 21 +++++- src/unrealsdk/unreal/classes/uclass.cpp | 52 ++------------ src/unrealsdk/unreal/classes/uclass.h | 45 +++--------- src/unrealsdk/unreal/classes/ufunction.cpp | 30 ++------ src/unrealsdk/unreal/classes/ufunction.h | 24 +++---- .../unreal/classes/uscriptstruct.cpp | 15 ++-- src/unrealsdk/unreal/classes/uscriptstruct.h | 24 +++++-- src/unrealsdk/unreal/classes/ustruct.cpp | 45 ------------ src/unrealsdk/unreal/classes/ustruct.h | 45 +----------- src/unrealsdk/unreal/offset_list.h | 8 ++- supported_settings.toml | 2 - 14 files changed, 205 insertions(+), 244 deletions(-) diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 815b89d..a84262e 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -3,10 +3,17 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +namespace unrealsdk::unreal { + +struct FImplementedInterface; + +} + namespace unrealsdk::game::bl1 { #if defined(_MSC_VER) && defined(ARCH_X86) @@ -21,8 +28,9 @@ namespace unrealsdk::game::bl1 { // readability-magic-numbers) class UStruct; +class UClass; -using UObject = bl2::UObject; +using UObject = bl2::generic::UObject; class UField : public UObject { public: @@ -73,6 +81,23 @@ class UStruct : public UField { uint8_t UnknownData03[0x04]; }; +class UClass : public UStruct { + private: + uint8_t UnknownData00[0xC0]; + + public: + UObject* ClassDefaultObject; + + private: + uint8_t UnknownData01[0x48]; + + public: + unreal::TArray Interfaces; +}; + +using UScriptStruct = unreal::offsets::generic::UScriptStruct; +using UFunction = bl2::generic::UFunction; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index cd1ca00..d294a6d 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -3,6 +3,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/tarray.h" @@ -11,7 +12,7 @@ namespace unrealsdk::unreal { -class UClass; +struct FImplementedInterface; } @@ -28,7 +29,14 @@ namespace unrealsdk::game::bl2 { // readability-identifier-naming, // readability-magic-numbers) -struct UObject { +class UClass; +class UProperty; + +namespace generic { + +// This is generic just so that we can reuse it in bl1+tps, but swap out the type of Class +template +class UObject { private: uintptr_t* vftable; void* HashNext; @@ -39,7 +47,7 @@ struct UObject { private: void* HashOuterNext; void* StateFrame; - UObject* _Linker; + UObject* _Linker; void* _LinkerIndex; public: @@ -49,14 +57,38 @@ struct UObject { int32_t NetIndex; public: - UObject* Outer; + UObject* Outer; unreal::FName Name; - unreal::UClass* Class; // TODO + UClass* Class; + + private: + UObject* ObjectArchetype; +}; + +template +class UFunction : public T { + public: + uint32_t FunctionFlags; + + private: + uint16_t iNative; + uint16_t RepOffset; + unreal::FName FriendlyName; + uint8_t OperPrecedence; + + public: + uint8_t NumParams; + uint16_t ParamsSize; + uint16_t ReturnValueOffset; private: - UObject* ObjectArchetype; + uint8_t UnknownData00[0x6]; + void* Func; }; +} // namespace generic + +using UObject = bl2::generic::UObject; using UField = unreal::offsets::generic::UField; class UProperty : public UField { @@ -97,6 +129,30 @@ class UStruct : public UField { unreal::TArray ScriptObjectReferences; }; +class UClass : public UStruct { + // Misc Fields I found within this block in BL2, but which we really don't care about + + // 0xE8: TArray ClassReps; + // 0xF4: TArray NetFields; + // 0x100: TArray HideCategories; + // 0x10C: TArray AutoExpandCategories; + + private: + uint8_t UnknownData00[0xCC]; + + public: + UObject* ClassDefaultObject; + + private: + uint8_t UnknownData01[0x48]; + + public: + unreal::TArray Interfaces; +}; + +using UScriptStruct = unreal::offsets::generic::UScriptStruct; +using UFunction = bl2::generic::UFunction; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 5bfa3a4..eb6f7e1 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -3,6 +3,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/tarray.h" @@ -11,7 +12,7 @@ namespace unrealsdk::unreal { -class UClass; // TODO +struct FImplementedInterface; } // namespace unrealsdk::unreal @@ -25,14 +26,16 @@ namespace unrealsdk::game::bl3 { // readability-identifier-naming, // readability-magic-numbers) -struct UObject { +class UClass; + +class UObject { private: uintptr_t* vftable; public: int32_t ObjectFlags; int32_t InternalIndex; - unreal::UClass* Class; // TODO + UClass* Class; unreal::FName Name; UObject* Outer; }; @@ -64,7 +67,7 @@ class UProperty : public UField { UProperty* PostConstructLinkNext; }; -class UStruct : public bl3::UField { +class UStruct : public UField { public: UStruct* SuperField; UField* Children; @@ -84,6 +87,38 @@ class UStruct : public bl3::UField { unreal::TArray ScriptObjectReferences; }; +class UClass : public UStruct { + private: + uint8_t UnknownData00[0x70]; + + public: + UObject* ClassDefaultObject; + + private: + uint8_t UnknownData01[0xA0]; + + public: + TArray Interfaces; +}; + +using UScriptStruct = unreal::offsets::generic::UScriptStruct; + +class UFunction : public UStruct { + public: + uint32_t FunctionFlags; + uint8_t NumParams; + uint16_t ParamsSize; + uint16_t ReturnValueOffset; + + private: + uint16_t RPCId; + uint16_t RPCResponseId; + UProperty* FirstPropertyToInit; + UFunction* EventGraphFunction; + int32_t EventGraphCallOffset; + void* Func; +}; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index 71775f2..df2c7c8 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -20,7 +20,9 @@ namespace unrealsdk::game::tps { // readability-identifier-naming, // readability-magic-numbers) -using UObject = bl2::UObject; +class UClass; + +using UObject = bl2::generic::UObject; using UField = bl2::UField; class UProperty : public UField { @@ -61,6 +63,23 @@ class UStruct : public UField { unreal::TArray ScriptObjectReferences; }; +class UClass : public UStruct { + private: + uint8_t UnknownData00[0xCC]; + + public: + UObject* ClassDefaultObject; + + private: + uint8_t UnknownData01[0x14]; + + public: + unreal::TArray Interfaces; +}; + +using UScriptStruct = unreal::offsets::generic::UScriptStruct; +using UFunction = bl2::generic::UFunction; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/unreal/classes/uclass.cpp b/src/unrealsdk/unreal/classes/uclass.cpp index debc08e..87ff5af 100644 --- a/src/unrealsdk/unreal/classes/uclass.cpp +++ b/src/unrealsdk/unreal/classes/uclass.cpp @@ -1,59 +1,15 @@ #include "unrealsdk/pch.h" -#include "unrealsdk/config.h" #include "unrealsdk/unreal/class_name.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/find_class.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -decltype(UClass::ClassDefaultObject_internal)& UClass::ClassDefaultObject(void) { - return this->get_field(&UClass::ClassDefaultObject_internal); -} -[[nodiscard]] const decltype(UClass::ClassDefaultObject_internal)& UClass::ClassDefaultObject( - void) const { - return this->get_field(&UClass::ClassDefaultObject_internal); -} - -// To further complicate things, UClass::Interfaces also shifts between BL2 + TPS -#ifdef UE3 - -// This is awful hacky code to get a working release out sooner, the whole system needs a rework. -// Check if the size of UClass is that we've observed in TPS, and if so use it's hardcoded offset. -namespace { - -const constexpr auto UCLASS_SIZE_TPS = 0x18C; -const constexpr auto UCLASS_INTERFACES_OFFSET_TPS = 0x160; - -} // namespace - -decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - return const_cast( - const_cast(this)->Interfaces()); -} -[[nodiscard]] const decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) const { - static const auto use_tps_offset = - unrealsdk::config::get_bool("unrealsdk.__force_uclass_interfaces_tps_offset") - .value_or(this->Class()->get_struct_size() == UCLASS_SIZE_TPS); - - if (use_tps_offset) { - return *reinterpret_cast( - reinterpret_cast(this) + UCLASS_INTERFACES_OFFSET_TPS); - } - - return this->get_field(&UClass::Interfaces_internal); -} -#else - -decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) { - return this->get_field(&UClass::Interfaces_internal); -} -[[nodiscard]] const decltype(UClass::Interfaces_internal)& UClass::Interfaces(void) const { - return this->get_field(&UClass::Interfaces_internal); -} - -#endif +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UClass, UNREALSDK_UCLASS_FIELDS); bool UClass::implements(const UClass* iface, FImplementedInterface* impl_out) const { // For each class in the inheritance chain diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index a846784..3a26966 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -28,13 +28,15 @@ class UClass : public UStruct { UClass& operator=(UClass&&) = delete; ~UClass() = delete; - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UCLASS_FIELDS(X) \ + X(UObject*, ClassDefaultObject) \ + X(TArray, Interfaces) + + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK_DEFINE_FIELDS_HEADER(UClass, UNREALSDK_UCLASS_FIELDS); - // - NOTE: Merge Conflicts (ustruct.h and uclass.h) - // Commit: 7803d86de171a5e19e66688074d8809329e42bb8 - // Manually resolved; only tested compilation for BL1, UE3, and UE4 but no more than that. - // ; 2025/01/19 (YYYY/MM/DD) - // - - - + // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) private: #ifdef UE4 @@ -43,10 +45,6 @@ class UClass : public UStruct { uint8_t UnknownData01[0xA0]; TArray Interfaces_internal; #else - -#if !defined(UNREALSDK_GAME_BL1) - - private: // Misc Fields I found within this block in BL2, but which we don't care about enough for me to // find in UE4, or to want to increase the compile times by including @@ -56,38 +54,13 @@ class UClass : public UStruct { // 0x10C: TArray AutoExpandCategories; uint8_t UnknownData00[0xCC]; - - public: UObject* ClassDefaultObject_internal; - - private: uint8_t UnknownData01[0x48]; - - public: TArray Interfaces_internal; -#else // defined(UNREALSDK_GAME_BL1) - - // NOTE: I don't know if the class has changed size; Will do a scan in ghidra for 1AC - - public: - uint8_t UnknownData00[0xC0]; - UObject* ClassDefaultObject_internal; // 340b - uint8_t UnknownData01[0x48]; - TArray Interfaces_internal; // 416b - -#endif - #endif - - public: - decltype(ClassDefaultObject_internal)& ClassDefaultObject(void); - [[nodiscard]] const decltype(ClassDefaultObject_internal)& ClassDefaultObject(void) const; - decltype(Interfaces_internal)& Interfaces(void); - [[nodiscard]] const decltype(Interfaces_internal)& Interfaces(void) const; - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) - + public: /** * @brief Checks if this class implements an interface. * diff --git a/src/unrealsdk/unreal/classes/ufunction.cpp b/src/unrealsdk/unreal/classes/ufunction.cpp index 116d31e..18549ff 100644 --- a/src/unrealsdk/unreal/classes/ufunction.cpp +++ b/src/unrealsdk/unreal/classes/ufunction.cpp @@ -2,35 +2,13 @@ #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -decltype(UFunction::FunctionFlags_internal)& UFunction::FunctionFlags(void) { - return this->get_field(&UFunction::FunctionFlags_internal); -} -[[nodiscard]] const decltype(UFunction::FunctionFlags_internal)& UFunction::FunctionFlags( - void) const { - return this->get_field(&UFunction::FunctionFlags_internal); -} -decltype(UFunction::NumParams_internal)& UFunction::NumParams(void) { - return this->get_field(&UFunction::NumParams_internal); -} -[[nodiscard]] const decltype(UFunction::NumParams_internal)& UFunction::NumParams(void) const { - return this->get_field(&UFunction::NumParams_internal); -} -decltype(UFunction::ParamsSize_internal)& UFunction::ParamsSize(void) { - return this->get_field(&UFunction::ParamsSize_internal); -} -[[nodiscard]] const decltype(UFunction::ParamsSize_internal)& UFunction::ParamsSize(void) const { - return this->get_field(&UFunction::ParamsSize_internal); -} -decltype(UFunction::ReturnValueOffset_internal)& UFunction::ReturnValueOffset(void) { - return this->get_field(&UFunction::ReturnValueOffset_internal); -} -[[nodiscard]] const decltype(UFunction::ReturnValueOffset_internal)& UFunction::ReturnValueOffset( - void) const { - return this->get_field(&UFunction::ReturnValueOffset_internal); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UFunction, UNREALSDK_UFUNCTION_FIELDS); UProperty* UFunction::find_return_param(void) const { for (auto prop : this->properties()) { diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index 66d884d..dbed0d8 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -27,6 +27,16 @@ class UFunction : public UStruct { UFunction& operator=(UFunction&&) = delete; ~UFunction() = delete; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UFUNCTION_FIELDS(X) \ + X(uint32_t, FunctionFlags) \ + X(uint8_t, NumParams) \ + X(uint16_t, ParamsSize) \ + X(uint16_t, ReturnValueOffset) + + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK_DEFINE_FIELDS_HEADER(UFunction, UNREALSDK_UFUNCTION_FIELDS); + // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) private: @@ -51,22 +61,10 @@ class UFunction : public UStruct { uint16_t ParamsSize_internal; uint16_t ReturnValueOffset_internal; uint8_t UnknownData00[0x6]; - -#if defined(UNREALSDK_GAME_BL1) - public: // Used in a console command to get address of function -#endif void* Func; + #endif public: - decltype(FunctionFlags_internal)& FunctionFlags(void); - [[nodiscard]] const decltype(FunctionFlags_internal)& FunctionFlags(void) const; - decltype(NumParams_internal)& NumParams(void); - [[nodiscard]] const decltype(NumParams_internal)& NumParams(void) const; - decltype(ParamsSize_internal)& ParamsSize(void); - [[nodiscard]] const decltype(ParamsSize_internal)& ParamsSize(void) const; - decltype(ReturnValueOffset_internal)& ReturnValueOffset(void); - [[nodiscard]] const decltype(ReturnValueOffset_internal)& ReturnValueOffset(void) const; - /** * @brief Finds the return param for this function (if it exists). * diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.cpp b/src/unrealsdk/unreal/classes/uscriptstruct.cpp index 496ff01..9036cc6 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.cpp +++ b/src/unrealsdk/unreal/classes/uscriptstruct.cpp @@ -1,15 +1,10 @@ -#include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" -#include "unrealsdk/unreal/classes/ustruct.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -decltype(UScriptStruct::StructFlags_internal)& UScriptStruct::StructFlags(void) { - return this->get_field(&UScriptStruct::StructFlags_internal); -} -[[nodiscard]] const decltype(UScriptStruct::StructFlags_internal)& UScriptStruct::StructFlags( - void) const { - return this->get_field(&UScriptStruct::StructFlags_internal); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UScriptStruct, UNREALSDK_USCRIPTSTRUCT_FIELDS); -} // namespace unrealsdk::unreal +} diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index b1503a0..28f9595 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -10,6 +10,17 @@ namespace unrealsdk::unreal { #pragma pack(push, 0x4) #endif +namespace offsets::generic { + +template +class UScriptStruct : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t StructFlags; +}; + +} // namespace offsets::generic + class UScriptStruct : public UStruct { public: UScriptStruct() = delete; @@ -19,16 +30,15 @@ class UScriptStruct : public UStruct { UScriptStruct& operator=(UScriptStruct&&) = delete; ~UScriptStruct() = delete; - // NOLINTBEGIN(readability-identifier-naming) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_USCRIPTSTRUCT_FIELDS(X) X(uint32_t, StructFlags) + + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK_DEFINE_FIELDS_HEADER(UScriptStruct, UNREALSDK_USCRIPTSTRUCT_FIELDS); private: + // NOLINTNEXTLINE(readability-identifier-naming) uint32_t StructFlags_internal; - - public: - decltype(StructFlags_internal)& StructFlags(void); - [[nodiscard]] const decltype(StructFlags_internal)& StructFlags(void) const; - - // NOLINTEND(readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 7b15133..f5f923b 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -16,51 +16,6 @@ namespace unrealsdk::unreal { UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UStruct, UNREALSDK_USTRUCT_FIELDS); -#ifdef UE3 - -size_t UStruct::class_size(void) { - static size_t size = 0; - if (size != 0) { - return size; - } - - auto config_size = config::get_int("unrealsdk.ustruct_size"); - if (config_size.has_value()) { - size = (size_t)*config_size; - return size; - } - - // Rather than bother with a find object/class, we can recover UStruct from any arbitrary object - // This just avoids extra dependencies, especially since theoretically find class might depend - // on this - - // First, find UClass - auto obj = *unrealsdk::gobjects().begin(); - const UClass* class_cls = obj->Class(); - for (; class_cls->Class() != class_cls; class_cls = class_cls->Class()) {} - - // Then look through it's superfields for UStruct - const UStruct* struct_cls = nullptr; - for (auto superfield : class_cls->superfields()) { - if (superfield->Name() == L"Struct"_fn) { - struct_cls = superfield; - break; - } - } - - // If we couldn't find the class, default to our actual size - if (struct_cls == nullptr) { - size = sizeof(UStruct); - LOG(WARNING, "Couldn't find UStruct class size, defaulting to: {:#x}", size); - } else { - size = struct_cls->get_struct_size(); - LOG(MISC, "UStruct class size: {:#x}", size); - } - return size; -} - -#endif - #pragma region Field Iterator UStruct::FieldIterator::FieldIterator(void) : this_struct(nullptr), field(nullptr) {} diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index 1c846fb..c7d1621 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -39,10 +39,9 @@ class UStruct : public UField { // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UStruct, UNREALSDK_USTRUCT_FIELDS); + private: // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) - #ifdef UE4 - private: /* Struct this inherits from, may be null */ UStruct* SuperField_member; /* Pointer to start of linked list of child fields */ @@ -69,8 +68,6 @@ class UStruct : public UField { * garbage collection code */ TArray ScriptObjectReferences; #else - - private: uint8_t UnknownData00[0x8]; UStruct* SuperField_member; @@ -86,46 +83,6 @@ class UStruct : public UField { TArray ScriptObjectReferences; #endif - - // See the description in 'uproperty.h', we have the same issue here. `UnknownData02` is 0x10 in - // BL2, but 0x4 in TPS. Since we need it this time, we also make provisions for setters. - - /** - * @brief Gets the size of this class. - * - * @return The size of this class. - */ - [[nodiscard]] static size_t class_size(void); - - protected: - /** - * @brief Reads a field on a UStruct subclass, taking into account it's variable length. - * - * @tparam SubType The subclass of UStruct to read the field off of (should be picked up - * automatically). - * @tparam FieldType The type of the field being read (should be picked up automatically). - * @param field Pointer to member of the field to read. - * @return A reference to the field. - */ - template >> - [[nodiscard]] const FieldType& get_field(FieldType SubType::* field) const { -#ifdef UE4 - return reinterpret_cast(this)->*field; -#else - return *reinterpret_cast( - reinterpret_cast(&(reinterpret_cast(this)->*field)) - - sizeof(UStruct) + UStruct::class_size()); -#endif - } - template >> - FieldType& get_field(FieldType SubType::* field) { - return const_cast(const_cast(this)->get_field(field)); - } - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) public: diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 30979c6..7bc73c2 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -2,9 +2,12 @@ #define UNREALSDK_UNREAL_OFFSET_LIST_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/ufield.h" +#include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/classes/uproperty.h" +#include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/classes/ustruct.h" namespace unrealsdk::unreal::offsets { @@ -20,7 +23,10 @@ namespace unrealsdk::unreal::offsets { X(UObject) \ X(UField) \ X(UStruct) \ - X(UProperty) + X(UProperty) \ + X(UClass) \ + X(UScriptStruct) \ + X(UFunction) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ diff --git a/supported_settings.toml b/supported_settings.toml index c5f0747..e1c3c34 100644 --- a/supported_settings.toml +++ b/supported_settings.toml @@ -26,8 +26,6 @@ exe_override = "" # Changes the size the `UProperty` class is assumed to have. uproperty_size = -1 -# Changes the size the `UStruct` class is assumed to have. -ustruct_size = -1 # Changes the alignment used when calling the unreal memory allocation functions. alloc_alignment = -1 From 25c4254caef8b4782e3f57a979af88b623a647c2 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Thu, 6 Mar 2025 20:32:07 +1300 Subject: [PATCH 57/81] convert remaining non-properties in theory this means bl1 is completely usable, have yet to test though --- src/unrealsdk/game/abstract_hook.h | 7 ++++++ src/unrealsdk/game/bl1/bl1.h | 2 ++ src/unrealsdk/game/bl1/offsets.cpp | 5 +++++ src/unrealsdk/game/bl1/offsets.h | 3 +++ src/unrealsdk/game/bl2/bl2.h | 2 ++ src/unrealsdk/game/bl2/offsets.cpp | 5 +++++ src/unrealsdk/game/bl2/offsets.h | 24 ++++++++++++++++++++ src/unrealsdk/game/bl3/bl3.h | 2 ++ src/unrealsdk/game/bl3/offsets.cpp | 17 ++++++++++++++ src/unrealsdk/game/bl3/offsets.h | 17 +++++++++++++- src/unrealsdk/game/tps/offsets.h | 2 ++ src/unrealsdk/unreal/classes/uconst.cpp | 10 +++++++++ src/unrealsdk/unreal/classes/uconst.h | 20 ++++++++++++++++- src/unrealsdk/unreal/classes/uenum.cpp | 30 ++++++------------------- src/unrealsdk/unreal/classes/uenum.h | 10 ++++----- src/unrealsdk/unreal/offset_list.h | 4 +++- src/unrealsdk/unrealsdk.h | 14 ++++++++++++ src/unrealsdk/unrealsdk_fw.inl | 20 ++++++++++++++++- src/unrealsdk/unrealsdk_main.cpp | 14 ++++++++++++ src/unrealsdk/unrealsdk_wrappers.cpp | 15 +++++++++++++ 20 files changed, 191 insertions(+), 32 deletions(-) create mode 100644 src/unrealsdk/unreal/classes/uconst.cpp diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index 04d4d4f..6aa59d6 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -4,6 +4,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/tarray.h" #ifndef UNREALSDK_IMPORTING @@ -12,6 +13,7 @@ namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; +class UEnum; class UFunction; class UObject; struct FFrame; @@ -20,6 +22,9 @@ struct FSoftObjectPtr; struct FText; struct TemporaryFString; +template +struct TPair; + } // namespace unrealsdk::unreal namespace unrealsdk::unreal::offsets { @@ -88,6 +93,8 @@ struct AbstractHook { virtual void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const = 0; [[nodiscard]] virtual const unreal::offsets::OffsetList& get_offsets(void) const = 0; + [[nodiscard]] virtual unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum) const = 0; }; #pragma endregion diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index f4bd22a..9a8dfdd 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -127,6 +127,8 @@ class BL1Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; + [[nodiscard]] unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl1/offsets.cpp b/src/unrealsdk/game/bl1/offsets.cpp index 6028b46..c4613e9 100644 --- a/src/unrealsdk/game/bl1/offsets.cpp +++ b/src/unrealsdk/game/bl1/offsets.cpp @@ -21,5 +21,10 @@ constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); return bl1::OFFSETS; } +unreal::TArray> BL1Hook::uenum_get_names( + const unreal::UEnum* uenum) const { + return reinterpret_cast(uenum)->get_names(); +} + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index a84262e..a536275 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -3,6 +3,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" @@ -97,6 +98,8 @@ class UClass : public UStruct { using UScriptStruct = unreal::offsets::generic::UScriptStruct; using UFunction = bl2::generic::UFunction; +using UConst = unreal::offsets::generic::UConst; +using UEnum = bl2::generic::UEnum; // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index c086eec..b6e373d 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -130,6 +130,8 @@ class BL2Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; + [[nodiscard]] unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index fbc1abd..f54b768 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -21,5 +21,10 @@ constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); return bl2::OFFSETS; } +unreal::TArray> BL2Hook::uenum_get_names( + const unreal::UEnum* uenum) const { + return reinterpret_cast(uenum)->get_names(); +} + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index d294a6d..e427cae 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -2,11 +2,14 @@ #define UNREALSDK_GAME_BL2_OFFSETS_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/tarray.h" +#include "unrealsdk/unreal/structs/tarray_funcs.h" +#include "unrealsdk/unreal/structs/tpair.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -86,6 +89,25 @@ class UFunction : public T { void* Func; }; +template +class UEnum : public T { + private: + unreal::TArray Names; + + public: + [[nodiscard]] unreal::TArray> get_names(void) const { + unreal::TArray> output{}; + output.resize(this->Names.size()); + + for (size_t i = 0; i < this->Names.size(); i++) { + output.data[i].key = this->Names.at(i); + output.data[i].value = i; + } + + return output; + } +}; + } // namespace generic using UObject = bl2::generic::UObject; @@ -152,6 +174,8 @@ class UClass : public UStruct { using UScriptStruct = unreal::offsets::generic::UScriptStruct; using UFunction = bl2::generic::UFunction; +using UConst = unreal::offsets::generic::UConst; +using UEnum = bl2::generic::UEnum; // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index cb60709..058ba98 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -119,6 +119,8 @@ class BL3Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; + [[nodiscard]] unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index 87f59cc..34b0d25 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -3,6 +3,7 @@ #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/structs/tarray_funcs.h" #if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) @@ -15,11 +16,27 @@ namespace { constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); } + +unreal::TArray> UEnum::get_names(void) const { + unreal::TArray> output{}; + output.resize(this->Names.size()); + + // We know this is flat so we can straight memcpy + memcpy(output.data, this->Names.data, this->Names.count * sizeof(*this->Names.data)); + + return output; +} + } // namespace bl3 [[nodiscard]] const unreal::offsets::OffsetList& BL3Hook::get_offsets(void) const { return bl3::OFFSETS; } +unreal::TArray> BL3Hook::uenum_get_names( + const unreal::UEnum* uenum) const { + return reinterpret_cast(uenum)->get_names(); +} + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index eb6f7e1..f5c4cf7 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -2,11 +2,14 @@ #define UNREALSDK_GAME_BL3_OFFSETS_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/fstring.h" #include "unrealsdk/unreal/structs/tarray.h" +#include "unrealsdk/unreal/structs/tpair.h" #if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) @@ -98,7 +101,7 @@ class UClass : public UStruct { uint8_t UnknownData01[0xA0]; public: - TArray Interfaces; + unreal::TArray Interfaces; }; using UScriptStruct = unreal::offsets::generic::UScriptStruct; @@ -119,6 +122,18 @@ class UFunction : public UStruct { void* Func; }; +using UConst = unreal::offsets::generic::UConst; + +class UEnum : public UField { + private: + unreal::UnmanagedFString CppType; + unreal::TArray> Names; + int64_t CppForm; + + public: + [[nodiscard]] unreal::TArray> get_names(void) const; +}; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index df2c7c8..d1245e7 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -79,6 +79,8 @@ class UClass : public UStruct { using UScriptStruct = unreal::offsets::generic::UScriptStruct; using UFunction = bl2::generic::UFunction; +using UConst = bl2::UConst; +using UEnum = bl2::UEnum; // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, diff --git a/src/unrealsdk/unreal/classes/uconst.cpp b/src/unrealsdk/unreal/classes/uconst.cpp new file mode 100644 index 0000000..692d55e --- /dev/null +++ b/src/unrealsdk/unreal/classes/uconst.cpp @@ -0,0 +1,10 @@ +#include "unrealsdk/unreal/classes/uconst.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" + +namespace unrealsdk::unreal { + +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UConst, UNREALSDK_UCONST_FIELDS); + +} diff --git a/src/unrealsdk/unreal/classes/uconst.h b/src/unrealsdk/unreal/classes/uconst.h index 863bddc..bd78ae0 100644 --- a/src/unrealsdk/unreal/classes/uconst.h +++ b/src/unrealsdk/unreal/classes/uconst.h @@ -7,6 +7,17 @@ namespace unrealsdk::unreal { +namespace offsets::generic { + +template +class UConst : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UnmanagedFString Value; +}; + +} // namespace offsets::generic + class UConst : public UField { public: UConst() = delete; @@ -16,8 +27,15 @@ class UConst : public UField { UConst& operator=(UConst&&) = delete; ~UConst() = delete; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UCONST_FIELDS(X) X(UnmanagedFString, Value) + // NOLINTNEXTLINE(readability-identifier-naming) - UnmanagedFString Value; + UNREALSDK_DEFINE_FIELDS_HEADER(UConst, UNREALSDK_UCONST_FIELDS); + + private: + // NOLINTNEXTLINE(readability-identifier-naming) + UnmanagedFString Value_internal; }; template <> diff --git a/src/unrealsdk/unreal/classes/uenum.cpp b/src/unrealsdk/unreal/classes/uenum.cpp index 3c2479a..65fc11f 100644 --- a/src/unrealsdk/unreal/classes/uenum.cpp +++ b/src/unrealsdk/unreal/classes/uenum.cpp @@ -1,36 +1,20 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uenum.h" -#include "unrealsdk/unreal/classes/properties/copyable_property.h" -#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" -#include "unrealsdk/unreal/classes/ufield.h" -#include "unrealsdk/unreal/classes/ufunction.h" -#include "unrealsdk/unreal/classes/uobject.h" -#include "unrealsdk/unreal/classes/uobject_funcs.h" -#include "unrealsdk/unreal/structs/fname.h" -#include "unrealsdk/unreal/wrappers/bound_function.h" +#include "unrealsdk/unreal/structs/tarray.h" +#include "unrealsdk/unreal/structs/tpair.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -#ifdef UE4 std::unordered_map UEnum::get_names(void) const { - std::unordered_map output; - for (size_t i = 0; i < this->Names.size(); i++) { - auto pair = this->Names.at(i); - output.emplace(pair.key, pair.value); - } - return output; -} + auto names = unrealsdk::internal::uenum_get_names(this); -#else - -std::unordered_map UEnum::get_names(void) const { std::unordered_map output; - for (size_t i = 0; i < this->Names.size(); i++) { - output.emplace(this->Names.at(i), i); + for (size_t i = 0; i < names.size(); i++) { + auto pair = names.at(i); + output.emplace(pair.key, pair.value); } return output; } -#endif - } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/uenum.h b/src/unrealsdk/unreal/classes/uenum.h index 3796554..9c0210e 100644 --- a/src/unrealsdk/unreal/classes/uenum.h +++ b/src/unrealsdk/unreal/classes/uenum.h @@ -1,5 +1,5 @@ -#ifndef UNREALSDK_UNREAL_CLASSES_UENUM_H -#define UNREALSDK_UNREAL_CLASSES_UENUM_H +#ifndef PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_UENUM_H +#define PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_UENUM_H #include "unrealsdk/pch.h" #include "unrealsdk/unreal/class_traits.h" @@ -44,9 +44,9 @@ class UEnum : public UField { public: /** - * @brief Get the enum values which have assigned names. + * @brief Get the values of an enum which have assigned names. * - * @return A map of integer values to their associated name. + * @return A map of names to their associated integer values. */ [[nodiscard]] std::unordered_map get_names(void) const; }; @@ -66,4 +66,4 @@ struct ClassTraits { } // namespace unrealsdk::unreal -#endif /* UNREALSDK_UNREAL_CLASSES_UENUM_H */ +#endif /* PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_UENUM_H */ diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 7bc73c2..f40edc7 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -3,6 +3,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uobject.h" @@ -26,7 +27,8 @@ namespace unrealsdk::unreal::offsets { X(UProperty) \ X(UClass) \ X(UScriptStruct) \ - X(UFunction) + X(UFunction) \ + X(UConst) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index 380bdcd..074ea14 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -4,6 +4,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/tarray.h" namespace unrealsdk::game { @@ -16,6 +17,7 @@ namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; +class UEnum; class UFunction; class UObject; struct FFrame; @@ -24,6 +26,9 @@ struct FSoftObjectPtr; struct FText; struct TemporaryFString; +template +struct TPair; + } // namespace unrealsdk::unreal namespace unrealsdk::unreal::offsets { @@ -226,6 +231,15 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o */ [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void); +/** + * @brief Get the values of an enum which have assigned names. + * + * @param uenum The unreal enum to get values from. + * @return An array of name-value pairs. + */ +[[nodiscard]] unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum); + } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index 3de1589..482f632 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -9,21 +9,26 @@ namespace. It is included directly in the other two files. #include "unrealsdk/pch.h" -#include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/structs/tarray.h" namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; +class UEnum; class UFunction; struct FFrame; struct FLazyObjectPtr; +struct FName; struct FSoftObjectPtr; struct FText; struct TemporaryFString; +template +struct TPair; + } // namespace unrealsdk::unreal using namespace unrealsdk::unreal; @@ -67,6 +72,19 @@ UNREALSDK_CAPI(void, fsoftobjectptr_assign, FSoftObjectPtr* ptr, const unreal::U UNREALSDK_CAPI(void, flazyobjectptr_assign, FLazyObjectPtr* ptr, const unreal::UObject* obj); UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets); +// Since we're not allowed to return a C++ type in an extern C, create a dummy C type to transfer +// all our values into +struct UEnumGetNamesCRet { + void* data; + int32_t count; + int32_t max; +}; +static_assert(sizeof(UEnumGetNamesCRet) + == sizeof(unreal::TArray>)); +static_assert(alignof(UEnumGetNamesCRet) + == alignof(unreal::TArray>)); +UNREALSDK_CAPI([[nodiscard]] UEnumGetNamesCRet, uenum_get_names, const unreal::UEnum* uenum); + } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index ddfe88a..c38ba57 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -200,6 +200,20 @@ UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets) { return &hook_instance->get_offsets(); } +UNREALSDK_CAPI([[nodiscard]] UEnumGetNamesCRet, uenum_get_names, const unreal::UEnum* uenum) { + auto names = hook_instance->uenum_get_names(uenum); + + UEnumGetNamesCRet ret{nullptr, 0, 0}; + std::swap(ret.count, names.count); + std::swap(ret.max, names.max); + + // different types so can't std::swap + ret.data = names.data; + names.data = nullptr; + + return ret; +} + } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index 1d2c080..d98e72c 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -108,6 +108,21 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o return *UNREALSDK_MANGLE(get_offsets)(); } +[[nodiscard]] unreal::TArray> uenum_get_names( + const unreal::UEnum* uenum) { + auto ret = UNREALSDK_MANGLE(uenum_get_names)(uenum); + + unreal::TArray> names{nullptr, 0, 0}; + std::swap(names.count, ret.count); + std::swap(names.max, ret.max); + + // different types so can't std::swap + names.data = reinterpret_cast*>(ret.data); + ret.data = nullptr; + + return names; +} + } // namespace internal } // namespace unrealsdk From 3adca0876018c61e24218baad70300f082bd5650 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Thu, 6 Mar 2025 20:44:58 +1300 Subject: [PATCH 58/81] linting cleanup --- src/unrealsdk/unreal/classes/uscriptstruct.h | 7 +++++++ src/unrealsdk/unrealsdk_main.cpp | 2 +- src/unrealsdk/unrealsdk_wrappers.cpp | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index 28f9595..af31703 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -9,6 +9,10 @@ namespace unrealsdk::unreal { #if defined(_MSC_VER) && defined(ARCH_X86) #pragma pack(push, 0x4) #endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif namespace offsets::generic { @@ -46,6 +50,9 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ScriptStruct"; }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif #if defined(_MSC_VER) && defined(ARCH_X86) #pragma pack(pop) #endif diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index c38ba57..a29975c 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -203,7 +203,7 @@ UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets) { UNREALSDK_CAPI([[nodiscard]] UEnumGetNamesCRet, uenum_get_names, const unreal::UEnum* uenum) { auto names = hook_instance->uenum_get_names(uenum); - UEnumGetNamesCRet ret{nullptr, 0, 0}; + UEnumGetNamesCRet ret{.data = nullptr, .count = 0, .max = 0}; std::swap(ret.count, names.count); std::swap(ret.max, names.max); diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index d98e72c..cf71a2a 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -112,7 +112,8 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o const unreal::UEnum* uenum) { auto ret = UNREALSDK_MANGLE(uenum_get_names)(uenum); - unreal::TArray> names{nullptr, 0, 0}; + unreal::TArray> names{ + .data = nullptr, .count = 0, .max = 0}; std::swap(names.count, ret.count); std::swap(names.max, ret.max); From 0e0e04cb4ca0a980666852a6f4bfc8954759cf20 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 11:18:48 +1200 Subject: [PATCH 59/81] Merge branch 'ry/bl1sdk' into v2b --- src/unrealsdk/game/bl1/offsets.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index a536275..361f506 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -6,6 +6,7 @@ #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unreal/structs/fname.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -31,7 +32,28 @@ namespace unrealsdk::game::bl1 { class UStruct; class UClass; -using UObject = bl2::generic::UObject; +class UObject { + private: + uintptr_t* vftable; + + public: + int32_t InternalIndex; + uint64_t ObjectFlags; + + private: + void* HashNext; + void* HashOuterNext; + void* StateFrame; + UObject* _Linker; + void* _LinkerIndex; + int32_t NetIndex; + + public: + UObject* Outer; + unreal::FName Name; + UClass* Class; + UObject* ObjectArchetype; +}; class UField : public UObject { public: From 0975e15e5c8c6caf00eceaf4529d3be6ea41a724 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 11:42:33 +1200 Subject: [PATCH 60/81] revert uenum changes after some time away, decided on a different approach uenum was relatively simple to merge, some of the follow types will be worse instead, we'll keep using macros to change implementations when they're siginificantly different --- src/unrealsdk/game/abstract_hook.h | 7 ------- src/unrealsdk/game/bl1/bl1.h | 2 -- src/unrealsdk/game/bl1/offsets.cpp | 5 ----- src/unrealsdk/game/bl1/offsets.h | 2 +- src/unrealsdk/game/bl2/bl2.h | 2 -- src/unrealsdk/game/bl2/offsets.cpp | 5 ----- src/unrealsdk/game/bl2/offsets.h | 20 ++++--------------- src/unrealsdk/game/bl3/bl3.h | 2 -- src/unrealsdk/game/bl3/offsets.cpp | 16 --------------- src/unrealsdk/game/bl3/offsets.h | 4 +++- src/unrealsdk/game/tps/offsets.h | 2 +- src/unrealsdk/unreal/classes/uenum.cpp | 27 +++++++++++++++++++------- src/unrealsdk/unreal/classes/uenum.h | 4 ++-- src/unrealsdk/unrealsdk.h | 14 ------------- src/unrealsdk/unrealsdk_fw.inl | 19 ------------------ src/unrealsdk/unrealsdk_main.cpp | 14 ------------- src/unrealsdk/unrealsdk_wrappers.cpp | 16 --------------- 17 files changed, 31 insertions(+), 130 deletions(-) diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index 6aa59d6..04d4d4f 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -4,7 +4,6 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/structs/fname.h" -#include "unrealsdk/unreal/structs/tarray.h" #ifndef UNREALSDK_IMPORTING @@ -13,7 +12,6 @@ namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; -class UEnum; class UFunction; class UObject; struct FFrame; @@ -22,9 +20,6 @@ struct FSoftObjectPtr; struct FText; struct TemporaryFString; -template -struct TPair; - } // namespace unrealsdk::unreal namespace unrealsdk::unreal::offsets { @@ -93,8 +88,6 @@ struct AbstractHook { virtual void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const = 0; [[nodiscard]] virtual const unreal::offsets::OffsetList& get_offsets(void) const = 0; - [[nodiscard]] virtual unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum) const = 0; }; #pragma endregion diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 9a8dfdd..f4bd22a 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -127,8 +127,6 @@ class BL1Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; - [[nodiscard]] unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl1/offsets.cpp b/src/unrealsdk/game/bl1/offsets.cpp index c4613e9..6028b46 100644 --- a/src/unrealsdk/game/bl1/offsets.cpp +++ b/src/unrealsdk/game/bl1/offsets.cpp @@ -21,10 +21,5 @@ constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); return bl1::OFFSETS; } -unreal::TArray> BL1Hook::uenum_get_names( - const unreal::UEnum* uenum) const { - return reinterpret_cast(uenum)->get_names(); -} - } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 361f506..07f0c8f 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -8,7 +8,7 @@ #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if defined(UE3) && defined(ARCH_X86) namespace unrealsdk::unreal { diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index b6e373d..c086eec 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -130,8 +130,6 @@ class BL2Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; - [[nodiscard]] unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index f54b768..fbc1abd 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -21,10 +21,5 @@ constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); return bl2::OFFSETS; } -unreal::TArray> BL2Hook::uenum_get_names( - const unreal::UEnum* uenum) const { - return reinterpret_cast(uenum)->get_names(); -} - } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index e427cae..d62d0b9 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -3,15 +3,14 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uconst.h" +#include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/tarray.h" -#include "unrealsdk/unreal/structs/tarray_funcs.h" -#include "unrealsdk/unreal/structs/tpair.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if defined(UE3) && defined(ARCH_X86) namespace unrealsdk::unreal { @@ -91,21 +90,10 @@ class UFunction : public T { template class UEnum : public T { + friend class unreal::UEnum; + private: unreal::TArray Names; - - public: - [[nodiscard]] unreal::TArray> get_names(void) const { - unreal::TArray> output{}; - output.resize(this->Names.size()); - - for (size_t i = 0; i < this->Names.size(); i++) { - output.data[i].key = this->Names.at(i); - output.data[i].value = i; - } - - return output; - } }; } // namespace generic diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index 058ba98..cb60709 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -119,8 +119,6 @@ class BL3Hook : public AbstractHook { void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* obj) const override; [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void) const override; - [[nodiscard]] unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum) const override; }; template <> diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index 34b0d25..54e4d8e 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -3,7 +3,6 @@ #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" -#include "unrealsdk/unreal/structs/tarray_funcs.h" #if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) @@ -17,26 +16,11 @@ constexpr auto OFFSETS = OFFSET_LIST_FROM_NAMESPACE(); } -unreal::TArray> UEnum::get_names(void) const { - unreal::TArray> output{}; - output.resize(this->Names.size()); - - // We know this is flat so we can straight memcpy - memcpy(output.data, this->Names.data, this->Names.count * sizeof(*this->Names.data)); - - return output; -} - } // namespace bl3 [[nodiscard]] const unreal::offsets::OffsetList& BL3Hook::get_offsets(void) const { return bl3::OFFSETS; } -unreal::TArray> BL3Hook::uenum_get_names( - const unreal::UEnum* uenum) const { - return reinterpret_cast(uenum)->get_names(); -} - } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index f5c4cf7..fa88632 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -11,7 +11,7 @@ #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/unreal/structs/tpair.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if defined(UE4) && defined(ARCH_X64) namespace unrealsdk::unreal { @@ -125,6 +125,8 @@ class UFunction : public UStruct { using UConst = unreal::offsets::generic::UConst; class UEnum : public UField { + friend class unreal::UEnum; + private: unreal::UnmanagedFString CppType; unreal::TArray> Names; diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index d1245e7..bbbcce4 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -5,7 +5,7 @@ #include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/unreal/offsets.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if defined(UE3) && defined(ARCH_X86) namespace unrealsdk::game::tps { diff --git a/src/unrealsdk/unreal/classes/uenum.cpp b/src/unrealsdk/unreal/classes/uenum.cpp index 65fc11f..26f2fb5 100644 --- a/src/unrealsdk/unreal/classes/uenum.cpp +++ b/src/unrealsdk/unreal/classes/uenum.cpp @@ -1,20 +1,33 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uenum.h" -#include "unrealsdk/unreal/structs/tarray.h" -#include "unrealsdk/unreal/structs/tpair.h" -#include "unrealsdk/unrealsdk.h" +#include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/structs/fname.h" namespace unrealsdk::unreal { +#ifdef UE4 std::unordered_map UEnum::get_names(void) const { - auto names = unrealsdk::internal::uenum_get_names(this); - + auto bl3_enum = reinterpret_cast(this); std::unordered_map output; - for (size_t i = 0; i < names.size(); i++) { - auto pair = names.at(i); + for (size_t i = 0; i < bl3_enum->Names.size(); i++) { + auto pair = bl3_enum->Names.at(i); output.emplace(pair.key, pair.value); } return output; } +#else + +std::unordered_map UEnum::get_names(void) const { + auto bl2_enum = reinterpret_cast(this); + + std::unordered_map output; + for (size_t i = 0; i < bl2_enum->Names.size(); i++) { + output.emplace(bl2_enum->Names.at(i), i); + } + return output; +} + +#endif + } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/uenum.h b/src/unrealsdk/unreal/classes/uenum.h index 9c0210e..a553593 100644 --- a/src/unrealsdk/unreal/classes/uenum.h +++ b/src/unrealsdk/unreal/classes/uenum.h @@ -1,5 +1,5 @@ -#ifndef PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_UENUM_H -#define PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_UENUM_H +#ifndef UNREALSDK_UNREAL_CLASSES_UENUM_H +#define UNREALSDK_UNREAL_CLASSES_UENUM_H #include "unrealsdk/pch.h" #include "unrealsdk/unreal/class_traits.h" diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index 13eb134..23d0205 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -4,7 +4,6 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/structs/fname.h" -#include "unrealsdk/unreal/structs/tarray.h" namespace unrealsdk::game { @@ -17,7 +16,6 @@ namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; -class UEnum; class UFunction; class UObject; struct FFrame; @@ -26,9 +24,6 @@ struct FSoftObjectPtr; struct FText; struct TemporaryFString; -template -struct TPair; - } // namespace unrealsdk::unreal namespace unrealsdk::unreal::offsets { @@ -233,15 +228,6 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o */ [[nodiscard]] const unreal::offsets::OffsetList& get_offsets(void); -/** - * @brief Get the values of an enum which have assigned names. - * - * @param uenum The unreal enum to get values from. - * @return An array of name-value pairs. - */ -[[nodiscard]] unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum); - } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index 482f632..da04215 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -10,25 +10,19 @@ namespace. It is included directly in the other two files. #include "unrealsdk/pch.h" #include "unrealsdk/unreal/offset_list.h" -#include "unrealsdk/unreal/structs/tarray.h" namespace unrealsdk::unreal { class GNames; class GObjects; class UClass; -class UEnum; class UFunction; struct FFrame; struct FLazyObjectPtr; -struct FName; struct FSoftObjectPtr; struct FText; struct TemporaryFString; -template -struct TPair; - } // namespace unrealsdk::unreal using namespace unrealsdk::unreal; @@ -72,19 +66,6 @@ UNREALSDK_CAPI(void, fsoftobjectptr_assign, FSoftObjectPtr* ptr, const unreal::U UNREALSDK_CAPI(void, flazyobjectptr_assign, FLazyObjectPtr* ptr, const unreal::UObject* obj); UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets); -// Since we're not allowed to return a C++ type in an extern C, create a dummy C type to transfer -// all our values into -struct UEnumGetNamesCRet { - void* data; - int32_t count; - int32_t max; -}; -static_assert(sizeof(UEnumGetNamesCRet) - == sizeof(unreal::TArray>)); -static_assert(alignof(UEnumGetNamesCRet) - == alignof(unreal::TArray>)); -UNREALSDK_CAPI([[nodiscard]] UEnumGetNamesCRet, uenum_get_names, const unreal::UEnum* uenum); - } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index a29975c..ddfe88a 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -200,20 +200,6 @@ UNREALSDK_CAPI([[nodiscard]] const offsets::OffsetList*, get_offsets) { return &hook_instance->get_offsets(); } -UNREALSDK_CAPI([[nodiscard]] UEnumGetNamesCRet, uenum_get_names, const unreal::UEnum* uenum) { - auto names = hook_instance->uenum_get_names(uenum); - - UEnumGetNamesCRet ret{.data = nullptr, .count = 0, .max = 0}; - std::swap(ret.count, names.count); - std::swap(ret.max, names.max); - - // different types so can't std::swap - ret.data = names.data; - names.data = nullptr; - - return ret; -} - } // namespace internal } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index cf71a2a..1d2c080 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -108,22 +108,6 @@ void flazyobjectptr_assign(unreal::FLazyObjectPtr* ptr, const unreal::UObject* o return *UNREALSDK_MANGLE(get_offsets)(); } -[[nodiscard]] unreal::TArray> uenum_get_names( - const unreal::UEnum* uenum) { - auto ret = UNREALSDK_MANGLE(uenum_get_names)(uenum); - - unreal::TArray> names{ - .data = nullptr, .count = 0, .max = 0}; - std::swap(names.count, ret.count); - std::swap(names.max, ret.max); - - // different types so can't std::swap - names.data = reinterpret_cast*>(ret.data); - ret.data = nullptr; - - return names; -} - } // namespace internal } // namespace unrealsdk From e560aa14be2c8ec9615b4d146075477634a658e4 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 11:52:33 +1200 Subject: [PATCH 61/81] add helper comments to field macro figure this will make it more clear for anyone just reading the header files --- src/unrealsdk/unreal/classes/uclass.h | 1 + src/unrealsdk/unreal/classes/uconst.h | 1 + src/unrealsdk/unreal/classes/ufield.h | 1 + src/unrealsdk/unreal/classes/ufunction.h | 1 + src/unrealsdk/unreal/classes/uobject.h | 1 + src/unrealsdk/unreal/classes/uproperty.h | 1 + src/unrealsdk/unreal/classes/uscriptstruct.h | 1 + src/unrealsdk/unreal/classes/ustruct.h | 1 + 8 files changed, 8 insertions(+) diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index 3a26966..25f1f4d 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -28,6 +28,7 @@ class UClass : public UStruct { UClass& operator=(UClass&&) = delete; ~UClass() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UCLASS_FIELDS(X) \ X(UObject*, ClassDefaultObject) \ diff --git a/src/unrealsdk/unreal/classes/uconst.h b/src/unrealsdk/unreal/classes/uconst.h index bd78ae0..a52a7f8 100644 --- a/src/unrealsdk/unreal/classes/uconst.h +++ b/src/unrealsdk/unreal/classes/uconst.h @@ -27,6 +27,7 @@ class UConst : public UField { UConst& operator=(UConst&&) = delete; ~UConst() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UCONST_FIELDS(X) X(UnmanagedFString, Value) diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index de5918a..efe66d3 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -35,6 +35,7 @@ class UField : public UObject { UField& operator=(UField&&) = delete; ~UField() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UFIELD_FIELDS(X) X(UField*, Next) diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index dbed0d8..d4afaf5 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -27,6 +27,7 @@ class UFunction : public UStruct { UFunction& operator=(UFunction&&) = delete; ~UFunction() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UFUNCTION_FIELDS(X) \ X(uint32_t, FunctionFlags) \ diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index 377827d..0ab60ae 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -38,6 +38,7 @@ class UObject { using object_flags_type = uint64_t; #endif + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UOBJECT_FIELDS(X) \ X(object_flags_type, ObjectFlags) \ diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index 412735b..6e231fe 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -60,6 +60,7 @@ class UProperty : public UField { using property_flags_type = uint32_t; #endif + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UPROPERTY_FIELDS(X) \ X(int32_t, ArrayDim) \ diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index af31703..42fdaa9 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -34,6 +34,7 @@ class UScriptStruct : public UStruct { UScriptStruct& operator=(UScriptStruct&&) = delete; ~UScriptStruct() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_USCRIPTSTRUCT_FIELDS(X) X(uint32_t, StructFlags) diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index c7d1621..dfba873 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -30,6 +30,7 @@ class UStruct : public UField { UStruct& operator=(UStruct&&) = delete; ~UStruct() = delete; + // These fields become member functions, returning a reference into the object. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_USTRUCT_FIELDS(X) \ X(UStruct*, SuperField) \ From cf74e9c0892ae31256af09a284048aa7293da476 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 13:31:55 +1200 Subject: [PATCH 62/81] merge unreal version and architechure defines into single build "flavour" --- .github/workflows/build.yml | 26 +-- CMakeLists.txt | 11 +- CMakePresets.json | 160 +++++++----------- README.md | 28 +-- src/unrealsdk/game/bl1/antidebug.cpp | 2 +- src/unrealsdk/game/bl1/bl1.cpp | 2 +- src/unrealsdk/game/bl1/bl1.h | 2 +- src/unrealsdk/game/bl1/console.cpp | 2 +- src/unrealsdk/game/bl1/globals.cpp | 2 +- src/unrealsdk/game/bl1/hexedits.cpp | 2 +- src/unrealsdk/game/bl1/hooks.cpp | 2 +- src/unrealsdk/game/bl1/memory.cpp | 2 +- src/unrealsdk/game/bl1/object.cpp | 2 +- src/unrealsdk/game/bl1/offsets.cpp | 2 +- src/unrealsdk/game/bl1/offsets.h | 6 +- .../game/bl1/presistentobjectptr.cpp | 2 +- src/unrealsdk/game/bl2/antidebug.cpp | 2 +- src/unrealsdk/game/bl2/bl2.cpp | 2 +- src/unrealsdk/game/bl2/bl2.h | 2 +- src/unrealsdk/game/bl2/console.cpp | 2 +- src/unrealsdk/game/bl2/globals.cpp | 2 +- src/unrealsdk/game/bl2/hexedits.cpp | 2 +- src/unrealsdk/game/bl2/hooks.cpp | 2 +- src/unrealsdk/game/bl2/memory.cpp | 2 +- src/unrealsdk/game/bl2/object.cpp | 2 +- src/unrealsdk/game/bl2/offsets.cpp | 2 +- src/unrealsdk/game/bl2/offsets.h | 6 +- .../game/bl2/persistentobjectptr.cpp | 2 +- src/unrealsdk/game/bl3/bl3.cpp | 2 +- src/unrealsdk/game/bl3/bl3.h | 2 +- src/unrealsdk/game/bl3/console.cpp | 2 +- src/unrealsdk/game/bl3/globals.cpp | 2 +- src/unrealsdk/game/bl3/hooks.cpp | 2 +- src/unrealsdk/game/bl3/memory.cpp | 2 +- src/unrealsdk/game/bl3/object.cpp | 4 +- src/unrealsdk/game/bl3/offsets.cpp | 2 +- src/unrealsdk/game/bl3/offsets.h | 2 +- .../game/bl3/persistentobjectptr.cpp | 2 +- src/unrealsdk/game/selector.cpp | 16 +- src/unrealsdk/game/tps/hexedits.cpp | 2 +- src/unrealsdk/game/tps/offsets.cpp | 2 +- src/unrealsdk/game/tps/offsets.h | 6 +- src/unrealsdk/game/tps/tps.cpp | 2 +- src/unrealsdk/game/tps/tps.h | 2 +- src/unrealsdk/memory.cpp | 11 +- src/unrealsdk/pch.h | 24 +-- .../classes/properties/attribute_property.h | 4 +- .../classes/properties/copyable_property.h | 4 +- .../persistent_object_ptr_property.h | 4 +- .../classes/properties/uarrayproperty.h | 4 +- .../unreal/classes/properties/uboolproperty.h | 10 +- .../unreal/classes/properties/ubyteproperty.h | 4 +- .../classes/properties/uclassproperty.h | 4 +- .../classes/properties/ucomponentproperty.h | 4 +- .../classes/properties/udelegateproperty.h | 4 +- .../unreal/classes/properties/uenumproperty.h | 4 +- .../classes/properties/uinterfaceproperty.h | 4 +- .../properties/umulticastdelegateproperty.h | 4 +- .../classes/properties/uobjectproperty.h | 4 +- .../unreal/classes/properties/ustrproperty.h | 4 +- .../classes/properties/ustructproperty.h | 4 +- .../unreal/classes/properties/utextproperty.h | 4 +- .../classes/properties/uweakobjectproperty.h | 4 +- src/unrealsdk/unreal/classes/uclass.h | 10 +- src/unrealsdk/unreal/classes/uenum.cpp | 7 +- src/unrealsdk/unreal/classes/uenum.h | 10 +- src/unrealsdk/unreal/classes/ufield.h | 4 +- src/unrealsdk/unreal/classes/ufunction.h | 11 +- src/unrealsdk/unreal/classes/uobject.cpp | 14 +- src/unrealsdk/unreal/classes/uobject.h | 16 +- src/unrealsdk/unreal/classes/uproperty.cpp | 2 +- src/unrealsdk/unreal/classes/uproperty.h | 27 +-- src/unrealsdk/unreal/classes/uscriptstruct.h | 4 +- src/unrealsdk/unreal/classes/ustruct.cpp | 6 +- src/unrealsdk/unreal/classes/ustruct.h | 11 +- src/unrealsdk/unreal/offset_list.h | 4 +- src/unrealsdk/unreal/structs/fframe.h | 16 +- .../unreal/structs/fimplementedinterface.cpp | 6 +- .../unreal/structs/fimplementedinterface.h | 10 +- src/unrealsdk/unreal/structs/fname.h | 4 +- .../unreal/structs/fpropertychangeevent.h | 4 +- .../unreal/structs/fscriptdelegate.cpp | 6 +- .../unreal/structs/fscriptdelegate.h | 10 +- src/unrealsdk/unreal/structs/ftext.cpp | 6 +- src/unrealsdk/unreal/structs/ftext.h | 4 +- src/unrealsdk/unreal/structs/fweakobjectptr.h | 4 +- src/unrealsdk/unreal/structs/gnames.cpp | 2 +- src/unrealsdk/unreal/structs/gnames.h | 12 +- src/unrealsdk/unreal/structs/gobjects.cpp | 2 +- src/unrealsdk/unreal/structs/gobjects.h | 6 +- src/unrealsdk/unreal/structs/tarray.h | 4 +- src/unrealsdk/unreal/structs/tpair.h | 4 +- .../unreal/structs/tpersistentobjectptr.h | 4 +- src/unrealsdk/unreal/structs/tsharedpointer.h | 4 +- .../unreal/wrappers/bound_function.cpp | 9 +- src/unrealsdk/unreal/wrappers/gnames.cpp | 6 +- src/unrealsdk/unreal/wrappers/gnames.h | 9 +- src/unrealsdk/unreal/wrappers/gobjects.cpp | 12 +- src/unrealsdk/unreal/wrappers/gobjects.h | 13 +- src/unrealsdk/unreal/wrappers/weak_pointer.h | 2 +- 100 files changed, 380 insertions(+), 347 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 874aeba..f546805 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: fail-fast: false matrix: preset: [ - "clang-ue3-x86-release", - "clang-ue4-x64-release", - "msvc-ue3-x86-release", - "msvc-ue4-x64-release", + "clang-willow-release", + "clang-oak-release", + "msvc-willow-release", + "msvc-oak-release", ] steps: @@ -29,7 +29,7 @@ jobs: if: startswith(matrix.preset, 'msvc') uses: TheMrMilchmann/setup-msvc-dev@v3 with: - arch: ${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'x64')] }} + arch: ${{ fromJSON('["x86", "x64"]')[contains(matrix.preset, 'oak')] }} - name: Setup CMake and Ninja uses: lukka/get-cmake@latest @@ -52,17 +52,17 @@ jobs: fail-fast: false matrix: toolchain: - - preset: clang-cross-ue3-x86-release + - preset: clang-cross-willow-release container: clang-cross - - preset: clang-cross-ue4-x64-release + - preset: clang-cross-oak-release container: clang-cross - - preset: llvm-mingw-ue3-x86-release + - preset: llvm-mingw-willow-release container: llvm-mingw - - preset: llvm-mingw-ue4-x64-release + - preset: llvm-mingw-oak-release container: llvm-mingw - - preset: mingw-ue3-x86-release + - preset: mingw-willow-release container: mingw - - preset: mingw-ue4-x64-release + - preset: mingw-oak-release container: mingw steps: @@ -102,8 +102,8 @@ jobs: fail-fast: false matrix: preset: [ - "clang-ue3-x86-release", - "clang-ue4-x64-release", + "clang-willow-release", + "clang-oak-release", ] steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index 324c0e4..a65d199 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,12 @@ project(unrealsdk VERSION 1.8.0) -set(UNREALSDK_UE_VERSION "UE4" CACHE STRING "The unreal engine version to build the SDK for. One of 'UE3' or 'UE4'.") -set(UNREALSDK_ARCH "x64" CACHE STRING "The architecture to build the sdk for. One of 'x86' or 'x64'.") +set(UNREALSDK_FLAVOUR "" CACHE STRING "Which \"flavour\" of the SDK to build.") +set_property(CACHE UNREALSDK_FLAVOUR PROPERTY STRINGS "WILLOW" "OAK") +if (NOT UNREALSDK_FLAVOUR MATCHES "(WILLOW|OAK)") + message(FATAL_ERROR "Got invalid sdk flavour '${UNREALSDK_FLAVOUR}'") +endif() + set(UNREALSDK_SHARED False CACHE BOOL "If set, compiles as a shared library instead of as an object.") add_library(_unrealsdk_interface INTERFACE) @@ -60,8 +64,7 @@ target_include_directories(_unrealsdk_interface INTERFACE "src" ${CONFIGURE_FILE target_link_libraries(_unrealsdk_interface INTERFACE minhook tomlplusplus::tomlplusplus) target_compile_definitions(_unrealsdk_interface INTERFACE - "${UNREALSDK_UE_VERSION}" - "ARCH_$" + "UNREALSDK_FLAVOUR=UNREALSDK_FLAVOUR_${UNREALSDK_FLAVOUR}" "$<$:UNREALSDK_SHARED>" ) diff --git a/CMakePresets.json b/CMakePresets.json index 8e1c658..29391df 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -110,41 +110,29 @@ "toolchainFile": "common_cmake/msvc.cmake" }, { - "name": "_x86", + "name": "_willow", "hidden": true, "architecture": { "value": "Win32", "strategy": "external" }, "cacheVariables": { + "UNREALSDK_FLAVOUR": "WILLOW", "UNREALSDK_ARCH": "x86" } }, { - "name": "_x64", + "name": "_oak", "hidden": true, "architecture": { "value": "x64", "strategy": "external" }, "cacheVariables": { + "UNREALSDK_FLAVOUR": "OAK", "UNREALSDK_ARCH": "x64" } }, - { - "name": "_ue3", - "hidden": true, - "cacheVariables": { - "UNREALSDK_UE_VERSION": "UE3" - } - }, - { - "name": "_ue4", - "hidden": true, - "cacheVariables": { - "UNREALSDK_UE_VERSION": "UE4" - } - }, { "name": "_debug", "hidden": true, @@ -160,222 +148,202 @@ } }, { - "name": "clang-ue3-x86-debug", - "displayName": "UE3 x86 Debug (Clang)", + "name": "clang-willow-debug", + "displayName": "Willow Debug (Clang)", "inherits": [ "_base", "_clang_x86", - "_ue3", - "_x86", + "_willow", "_debug" ] }, { - "name": "clang-ue3-x86-release", - "displayName": "UE3 x86 Release (Clang)", + "name": "clang-willow-release", + "displayName": "Willow Release (Clang)", "inherits": [ "_base", "_clang_x86", - "_ue3", - "_x86", + "_willow", "_release" ] }, { - "name": "clang-ue4-x64-debug", - "displayName": "UE4 x64 Debug (Clang)", + "name": "clang-oak-debug", + "displayName": "Oak Debug (Clang)", "inherits": [ "_base", "_clang_x64", - "_ue4", - "_x64", + "_oak", "_debug" ] }, { - "name": "clang-ue4-x64-release", - "displayName": "UE4 x64 Release (Clang)", + "name": "clang-oak-release", + "displayName": "Oak Release (Clang)", "inherits": [ "_base", "_clang_x64", - "_ue4", - "_x64", + "_oak", "_release" ] }, { - "name": "clang-cross-ue3-x86-debug", - "displayName": "UE3 x86 Debug (Clang Cross Compiler)", + "name": "clang-cross-willow-debug", + "displayName": "Willow Debug (Clang Cross Compiler)", "inherits": [ "_base", "_clang_cross_x86", - "_ue3", - "_x86", + "_willow", "_debug" ] }, { - "name": "clang-cross-ue3-x86-release", - "displayName": "UE3 x86 Release (Clang Cross Compiler)", + "name": "clang-cross-willow-release", + "displayName": "Willow Release (Clang Cross Compiler)", "inherits": [ "_base", "_clang_cross_x86", - "_ue3", - "_x86", + "_willow", "_release" ] }, { - "name": "clang-cross-ue4-x64-debug", - "displayName": "UE4 x64 Debug (Clang Cross Compiler)", + "name": "clang-cross-oak-debug", + "displayName": "Oak Debug (Clang Cross Compiler)", "inherits": [ "_base", "_clang_cross_x64", - "_ue4", - "_x64", + "_oak", "_debug" ] }, { - "name": "clang-cross-ue4-x64-release", - "displayName": "UE4 x64 Release (Clang Cross Compiler)", + "name": "clang-cross-oak-release", + "displayName": "Oak Release (Clang Cross Compiler)", "inherits": [ "_base", "_clang_cross_x64", - "_ue4", - "_x64", + "_oak", "_release" ] }, { - "name": "llvm-mingw-ue3-x86-debug", - "displayName": "UE3 x86 Debug (LLVM MinGW)", + "name": "llvm-mingw-willow-debug", + "displayName": "Willow Debug (LLVM MinGW)", "inherits": [ "_base", "_llvm_mingw_x86", - "_ue3", - "_x86", + "_willow", "_debug" ] }, { - "name": "llvm-mingw-ue3-x86-release", - "displayName": "UE3 x86 Release (LLVM MinGW)", + "name": "llvm-mingw-willow-release", + "displayName": "Willow Release (LLVM MinGW)", "inherits": [ "_base", "_llvm_mingw_x86", - "_ue3", - "_x86", + "_willow", "_release" ] }, { - "name": "llvm-mingw-ue4-x64-debug", - "displayName": "UE4 x64 Debug (LLVM MinGW)", + "name": "llvm-mingw-oak-debug", + "displayName": "Oak Debug (LLVM MinGW)", "inherits": [ "_base", "_llvm_mingw_x64", - "_ue4", - "_x64", + "_oak", "_debug" ] }, { - "name": "llvm-mingw-ue4-x64-release", - "displayName": "UE4 x64 Release (LLVM MinGW)", + "name": "llvm-mingw-oak-release", + "displayName": "Oak Release (LLVM MinGW)", "inherits": [ "_base", "_llvm_mingw_x64", - "_ue4", - "_x64", + "_oak", "_release" ] }, { - "name": "mingw-ue3-x86-debug", - "displayName": "UE3 x86 Debug (MinGW)", + "name": "mingw-willow-debug", + "displayName": "Willow Debug (MinGW)", "inherits": [ "_base", "_mingw_x86", - "_ue3", - "_x86", + "_willow", "_debug" ] }, { - "name": "mingw-ue3-x86-release", - "displayName": "UE3 x86 Release (MinGW)", + "name": "mingw-willow-release", + "displayName": "Willow Release (MinGW)", "inherits": [ "_base", "_mingw_x86", - "_ue3", - "_x86", + "_willow", "_release" ] }, { - "name": "mingw-ue4-x64-debug", - "displayName": "UE4 x64 Debug (MinGW)", + "name": "mingw-oak-debug", + "displayName": "Oak Debug (MinGW)", "inherits": [ "_base", "_mingw_x64", - "_ue4", - "_x64", + "_oak", "_debug" ] }, { - "name": "mingw-ue4-x64-release", - "displayName": "UE4 x64 Release (MinGW)", + "name": "mingw-oak-release", + "displayName": "Oak Release (MinGW)", "inherits": [ "_base", "_mingw_x64", - "_ue4", - "_x64", + "_oak", "_release" ] }, { - "name": "msvc-ue3-x86-debug", - "displayName": "UE3 x86 Debug (MSVC)", + "name": "msvc-willow-debug", + "displayName": "Willow Debug (MSVC)", "inherits": [ "_base", "_msvc", - "_ue3", - "_x86", + "_willow", "_debug" ] }, { - "name": "msvc-ue3-x86-release", - "displayName": "UE3 x86 Release (MSVC)", + "name": "msvc-willow-release", + "displayName": "Willow Release (MSVC)", "inherits": [ "_base", "_msvc", - "_ue3", - "_x86", + "_willow", "_release" ] }, { - "name": "msvc-ue4-x64-debug", - "displayName": "UE4 x64 Debug (MSVC)", + "name": "msvc-oak-debug", + "displayName": "Oak Debug (MSVC)", "inherits": [ "_base", "_msvc", - "_ue4", - "_x64", + "_oak", "_debug" ] }, { - "name": "msvc-ue4-x64-release", - "displayName": "UE4 x64 Release (MSVC)", + "name": "msvc-oak-release", + "displayName": "Oak Release (MSVC)", "inherits": [ "_base", "_msvc", - "_ue4", - "_x64", + "_oak", "_release" ] } diff --git a/README.md b/README.md index 93344f6..7c233a2 100644 --- a/README.md +++ b/README.md @@ -59,20 +59,28 @@ The recommended way to link against the sdk is as a submodule. git clone --recursive https://github.com/bl-sdk/unrealsdk.git ``` ```cmake +set(UNREALSDK_FLAVOUR WILLOW) +# set(UNREALSDK_SHARED True) add_submodule(path/to/unrealsdk) target_link_libraries(MyProject PRIVATE unrealsdk) ``` -You can configure the sdk by setting a few variables before including it: -- `UNREALSDK_UE_VERSION` - The unreal engine version to build the SDK for, one of `UE3` or `UE4`. - These versions are different enough that supporting them from a single binary is difficult. -- `UNREALSDK_ARCH` - The architecture to build the sdk for. One of `x86` or `x64`. Will be double - checked at compile time. -- `UNREALSDK_SHARED` - If set, compiles as a shared library instead of as an object. - -If you want to be able to run multiple projects using the sdk in the same game process, you *must* -compile it as a shared library, there's a decent amount of internal state preventing initializing it -twice. +You must define the build "flavour" before including the sdk. Each flavour corresponds to a set of +games running on a similar (but not neccesarily the exact same) unreal engine versions. Essentially, +if two games are similar enough to use the same mod manager, and support mostly the same mods, they +should work under the same build flavour. Some level of runtime adaptation is suppported (e.g. core +types may slightly change size or layout), but accounting for large engine internal differences +(e.g. the different GNames data structures) at runtime is a non-goal. + +The currently supported flavours are: +- `WILLOW`: Borderlands 1, 2, TPS, and AoDK standalone. Named for Gearbox's codename. 32-bit UE3. +- `OAK`: Borderlands 3 and Wonderlands. Named for Gearbox's codename. 64-bit UE 4.21ish - there's + some backports making exact versioning awkward. + +Additionally, you can optionally define the `UNREALSDK_SHARED` variable, to compile as a shared +library instead of as an object one. If you want to be able to run multiple projects using the sdk +in the same game process, you *must* compile it as a shared library, there's a decent amount of +internal state preventing initializing it twice. If you're linking against a static library, the easiest way to initialize it is: ```cpp diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp index bc0c401..4cfa1ec 100644 --- a/src/unrealsdk/game/bl1/antidebug.cpp +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -7,7 +7,7 @@ #include "unrealsdk/game/bl1/bl1.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 514710c..22ad845 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -6,7 +6,7 @@ #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index f4bd22a..7f43f0b 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -6,7 +6,7 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 261372b..97389f3 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -12,7 +12,7 @@ #include "unrealsdk/unreal/classes/uobject_funcs.h" #include "unrealsdk/unreal/find_class.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/globals.cpp b/src/unrealsdk/game/bl1/globals.cpp index 05f46f9..8dca1cd 100644 --- a/src/unrealsdk/game/bl1/globals.cpp +++ b/src/unrealsdk/game/bl1/globals.cpp @@ -6,7 +6,7 @@ #include "unrealsdk/unreal/wrappers/gnames.h" #include "unrealsdk/unreal/wrappers/gobjects.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp index 412942c..dc806e9 100644 --- a/src/unrealsdk/game/bl1/hexedits.cpp +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/memory.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index ff05f90..8f2fbfe 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -9,7 +9,7 @@ #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/unreal/wrappers/wrapped_struct.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/memory.cpp b/src/unrealsdk/game/bl1/memory.cpp index 55b8f54..c63eeba 100644 --- a/src/unrealsdk/game/bl1/memory.cpp +++ b/src/unrealsdk/game/bl1/memory.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/alignment.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp index dfc9c5d..84ff160 100644 --- a/src/unrealsdk/game/bl1/object.cpp +++ b/src/unrealsdk/game/bl1/object.cpp @@ -5,7 +5,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/structs/fstring.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl1/offsets.cpp b/src/unrealsdk/game/bl1/offsets.cpp index 6028b46..96282b3 100644 --- a/src/unrealsdk/game/bl1/offsets.cpp +++ b/src/unrealsdk/game/bl1/offsets.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal::offsets; diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 07f0c8f..ff0b95c 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -8,7 +8,7 @@ #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" -#if defined(UE3) && defined(ARCH_X86) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW namespace unrealsdk::unreal { @@ -18,7 +18,7 @@ struct FImplementedInterface; namespace unrealsdk::game::bl1 { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif #if defined(__clang__) @@ -129,7 +129,7 @@ using UEnum = bl2::generic::UEnum; #if defined(__clang__) #pragma clang diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/game/bl1/presistentobjectptr.cpp b/src/unrealsdk/game/bl1/presistentobjectptr.cpp index 19a67fe..ed3021b 100644 --- a/src/unrealsdk/game/bl1/presistentobjectptr.cpp +++ b/src/unrealsdk/game/bl1/presistentobjectptr.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/bl1/bl1.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl2/antidebug.cpp b/src/unrealsdk/game/bl2/antidebug.cpp index 4bbe1bf..563ccf3 100644 --- a/src/unrealsdk/game/bl2/antidebug.cpp +++ b/src/unrealsdk/game/bl2/antidebug.cpp @@ -2,7 +2,7 @@ #include "unrealsdk/game/bl2/bl2.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl2/bl2.cpp b/src/unrealsdk/game/bl2/bl2.cpp index 38a5afb..6ea61fc 100644 --- a/src/unrealsdk/game/bl2/bl2.cpp +++ b/src/unrealsdk/game/bl2/bl2.cpp @@ -9,7 +9,7 @@ #include "unrealsdk/unreal/structs/ftext.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index c086eec..43eb892 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -6,7 +6,7 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index 41f6f7e..afc8df6 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -21,7 +21,7 @@ #include "unrealsdk/unreal/wrappers/wrapped_struct.h" #include "unrealsdk/unrealsdk.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl2/globals.cpp b/src/unrealsdk/game/bl2/globals.cpp index 076f863..dc911cd 100644 --- a/src/unrealsdk/game/bl2/globals.cpp +++ b/src/unrealsdk/game/bl2/globals.cpp @@ -5,7 +5,7 @@ #include "unrealsdk/unreal/wrappers/gnames.h" #include "unrealsdk/unreal/wrappers/gobjects.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl2/hexedits.cpp b/src/unrealsdk/game/bl2/hexedits.cpp index 947970f..eb4c6b6 100644 --- a/src/unrealsdk/game/bl2/hexedits.cpp +++ b/src/unrealsdk/game/bl2/hexedits.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/memory.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl2/hooks.cpp b/src/unrealsdk/game/bl2/hooks.cpp index a99f01f..63ba284 100644 --- a/src/unrealsdk/game/bl2/hooks.cpp +++ b/src/unrealsdk/game/bl2/hooks.cpp @@ -16,7 +16,7 @@ #include "unrealsdk/unreal/wrappers/wrapped_struct.h" #include "unrealsdk/unrealsdk.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl2/memory.cpp b/src/unrealsdk/game/bl2/memory.cpp index 14c2540..e15b7c9 100644 --- a/src/unrealsdk/game/bl2/memory.cpp +++ b/src/unrealsdk/game/bl2/memory.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/alignment.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl2/object.cpp b/src/unrealsdk/game/bl2/object.cpp index 1215219..e52de5a 100644 --- a/src/unrealsdk/game/bl2/object.cpp +++ b/src/unrealsdk/game/bl2/object.cpp @@ -11,7 +11,7 @@ #include "unrealsdk/unreal/structs/fstring.h" #include "unrealsdk/unreal/wrappers/bound_function.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl2/offsets.cpp b/src/unrealsdk/game/bl2/offsets.cpp index fbc1abd..bedc88c 100644 --- a/src/unrealsdk/game/bl2/offsets.cpp +++ b/src/unrealsdk/game/bl2/offsets.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal::offsets; diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index d62d0b9..6f8b1a0 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -10,7 +10,7 @@ #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/tarray.h" -#if defined(UE3) && defined(ARCH_X86) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW namespace unrealsdk::unreal { @@ -20,7 +20,7 @@ struct FImplementedInterface; namespace unrealsdk::game::bl2 { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif #if defined(__clang__) @@ -171,7 +171,7 @@ using UEnum = bl2::generic::UEnum; #if defined(__clang__) #pragma clang diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/game/bl2/persistentobjectptr.cpp b/src/unrealsdk/game/bl2/persistentobjectptr.cpp index e453e24..6140ce5 100644 --- a/src/unrealsdk/game/bl2/persistentobjectptr.cpp +++ b/src/unrealsdk/game/bl2/persistentobjectptr.cpp @@ -2,7 +2,7 @@ #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/version_error.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl3/bl3.cpp b/src/unrealsdk/game/bl3/bl3.cpp index a1cf683..c1de42d 100644 --- a/src/unrealsdk/game/bl3/bl3.cpp +++ b/src/unrealsdk/game/bl3/bl3.cpp @@ -7,7 +7,7 @@ #include "unrealsdk/unreal/structs/fstring.h" #include "unrealsdk/unreal/structs/ftext.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index cb60709..04ca85c 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -6,7 +6,7 @@ #include "unrealsdk/game/abstract_hook.h" #include "unrealsdk/game/selector.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) namespace unrealsdk::game { diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index 29bf4ca..661e6e2 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -22,7 +22,7 @@ #include "unrealsdk/unreal/wrappers/wrapped_struct.h" #include "unrealsdk/unrealsdk.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl3/globals.cpp b/src/unrealsdk/game/bl3/globals.cpp index 4491a73..47cc8f9 100644 --- a/src/unrealsdk/game/bl3/globals.cpp +++ b/src/unrealsdk/game/bl3/globals.cpp @@ -5,7 +5,7 @@ #include "unrealsdk/unreal/wrappers/gnames.h" #include "unrealsdk/unreal/wrappers/gobjects.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl3/hooks.cpp b/src/unrealsdk/game/bl3/hooks.cpp index 8a71943..130e5e0 100644 --- a/src/unrealsdk/game/bl3/hooks.cpp +++ b/src/unrealsdk/game/bl3/hooks.cpp @@ -12,7 +12,7 @@ #include "unrealsdk/unreal/wrappers/unreal_pointer_funcs.h" #include "unrealsdk/unreal/wrappers/wrapped_struct.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/bl3/memory.cpp b/src/unrealsdk/game/bl3/memory.cpp index 6b7f1a0..c92c119 100644 --- a/src/unrealsdk/game/bl3/memory.cpp +++ b/src/unrealsdk/game/bl3/memory.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/memory.h" #include "unrealsdk/unreal/alignment.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index e7b4790..218d02a 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -6,7 +6,7 @@ #include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/fstring.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; using namespace unrealsdk::unreal; @@ -17,7 +17,7 @@ namespace unrealsdk::game { namespace { -using construct_obj_func = UObject* (*)(UClass * cls, +using construct_obj_func = UObject* (*)(UClass* cls, UObject* obj, FName name, uint32_t flags, diff --git a/src/unrealsdk/game/bl3/offsets.cpp b/src/unrealsdk/game/bl3/offsets.cpp index 54e4d8e..af0b0e0 100644 --- a/src/unrealsdk/game/bl3/offsets.cpp +++ b/src/unrealsdk/game/bl3/offsets.cpp @@ -4,7 +4,7 @@ #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/offset_list.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal::offsets; diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index fa88632..a5435e8 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -11,7 +11,7 @@ #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/unreal/structs/tpair.h" -#if defined(UE4) && defined(ARCH_X64) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK namespace unrealsdk::unreal { diff --git a/src/unrealsdk/game/bl3/persistentobjectptr.cpp b/src/unrealsdk/game/bl3/persistentobjectptr.cpp index 0f029be..ec7146d 100644 --- a/src/unrealsdk/game/bl3/persistentobjectptr.cpp +++ b/src/unrealsdk/game/bl3/persistentobjectptr.cpp @@ -10,7 +10,7 @@ #include "unrealsdk/unreal/wrappers/gobjects.h" #include "unrealsdk/unrealsdk.h" -#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/selector.cpp b/src/unrealsdk/game/selector.cpp index 1fa8c3c..a063d22 100644 --- a/src/unrealsdk/game/selector.cpp +++ b/src/unrealsdk/game/selector.cpp @@ -18,21 +18,13 @@ namespace { // Tuple of all hook types to consider. // The first matching hook will be used, order matters. -#ifdef ARCH_X64 -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW +using all_known_games = std::tuple; +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using all_known_games = std::tuple; #else -#error No known games for UE3 x64 +#error Unknown SDK flavour #endif -#else -#ifdef UE4 -#error No known games for UE4 x86 -#else - -using all_known_games = std::tuple; - -#endif // else !UE4 -#endif // else !ARCH_X64 /** * @brief Recursive helper function to find the right game hook. diff --git a/src/unrealsdk/game/tps/hexedits.cpp b/src/unrealsdk/game/tps/hexedits.cpp index c8f658f..9ccde24 100644 --- a/src/unrealsdk/game/tps/hexedits.cpp +++ b/src/unrealsdk/game/tps/hexedits.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/tps/tps.h" #include "unrealsdk/memory.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::memory; diff --git a/src/unrealsdk/game/tps/offsets.cpp b/src/unrealsdk/game/tps/offsets.cpp index 35b2e81..742abad 100644 --- a/src/unrealsdk/game/tps/offsets.cpp +++ b/src/unrealsdk/game/tps/offsets.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/tps/tps.h" #include "unrealsdk/unreal/offset_list.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal::offsets; diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index bbbcce4..66647b5 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -5,11 +5,11 @@ #include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/unreal/offsets.h" -#if defined(UE3) && defined(ARCH_X86) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW namespace unrealsdk::game::tps { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif #if defined(__clang__) @@ -88,7 +88,7 @@ using UEnum = bl2::UEnum; #if defined(__clang__) #pragma clang diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/game/tps/tps.cpp b/src/unrealsdk/game/tps/tps.cpp index 4b823ef..69531dd 100644 --- a/src/unrealsdk/game/tps/tps.cpp +++ b/src/unrealsdk/game/tps/tps.cpp @@ -3,7 +3,7 @@ #include "unrealsdk/game/tps/tps.h" #include "unrealsdk/unreal/structs/fname.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) using namespace unrealsdk::unreal; diff --git a/src/unrealsdk/game/tps/tps.h b/src/unrealsdk/game/tps/tps.h index 1a304ae..c597155 100644 --- a/src/unrealsdk/game/tps/tps.h +++ b/src/unrealsdk/game/tps/tps.h @@ -3,7 +3,7 @@ #include "unrealsdk/pch.h" -#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) #include "unrealsdk/game/bl2/bl2.h" #include "unrealsdk/game/selector.h" diff --git a/src/unrealsdk/memory.cpp b/src/unrealsdk/memory.cpp index fa6b3a2..45f031b 100644 --- a/src/unrealsdk/memory.cpp +++ b/src/unrealsdk/memory.cpp @@ -126,11 +126,12 @@ uintptr_t read_offset(uintptr_t address) { LOG(ERROR, "Attempted to read a null offset!"); return 0; } -#ifdef ARCH_X64 - return address + *reinterpret_cast(address) + 4; -#else - return *reinterpret_cast(address); -#endif + + if constexpr (sizeof(uintptr_t) == sizeof(uint64_t)) { + return address + *reinterpret_cast(address) + 4; + } else { + return *reinterpret_cast(address); + } } void unlock_range(uintptr_t start, size_t size) { diff --git a/src/unrealsdk/pch.h b/src/unrealsdk/pch.h index 0664ec6..43259f7 100644 --- a/src/unrealsdk/pch.h +++ b/src/unrealsdk/pch.h @@ -5,6 +5,13 @@ // This file is purely macros, it doesn't rely on anything else #include "unrealsdk/exports.h" +// The flavour macros are used in ifdefs everywhere. +// `UNREALSDK_FLAVOUR` should be defined to be equal to one of them. +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define UNREALSDK_FLAVOUR_WILLOW 1 +#define UNREALSDK_FLAVOUR_OAK 2 +// NOLINTEND(cppcoreguidelines-macro-usage) + #define WIN32_LEAN_AND_MEAN #define WIN32_NO_STATUS #define NOGDI @@ -82,21 +89,16 @@ static_assert(std::numeric_limits::is_iec559 && std::numeric_limits::NAME = L"Float template <> inline const wchar_t* const ClassTraits::NAME = L"IntAttributeProperty"; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/copyable_property.h b/src/unrealsdk/unreal/classes/properties/copyable_property.h index 50a5898..acda317 100644 --- a/src/unrealsdk/unreal/classes/properties/copyable_property.h +++ b/src/unrealsdk/unreal/classes/properties/copyable_property.h @@ -15,7 +15,7 @@ This file describes all properties which can be read/written by a simple copy, a namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -83,7 +83,7 @@ inline const wchar_t* const ClassTraits::NAME = L"DoublePropert template <> inline const wchar_t* const ClassTraits::NAME = L"NameProperty"; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h index 21d8188..b122874 100644 --- a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h +++ b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h @@ -14,7 +14,7 @@ struct FLazyObjectPath; struct FSoftObjectPath; class UObject; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -118,7 +118,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"SoftClassProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h index fd1fcac..680ff0f 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h @@ -11,7 +11,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -50,7 +50,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ArrayProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uboolproperty.h b/src/unrealsdk/unreal/classes/properties/uboolproperty.h index cad31ac..67ed3a9 100644 --- a/src/unrealsdk/unreal/classes/properties/uboolproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uboolproperty.h @@ -10,7 +10,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -31,13 +31,15 @@ class UBoolProperty : public UProperty { private: // NOLINTBEGIN(readability-identifier-naming) -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK uint8_t FieldSize; uint8_t ByteOffset; uint8_t ByteMask; uint8_t FieldMask; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW uint32_t FieldMask; +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-identifier-naming) @@ -67,7 +69,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h index 37038b8..44efa7c 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h @@ -12,7 +12,7 @@ namespace unrealsdk::unreal { class UEnum; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -49,7 +49,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ByteProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.h b/src/unrealsdk/unreal/classes/properties/uclassproperty.h index 509cfa8..5ac0b01 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.h @@ -12,7 +12,7 @@ namespace unrealsdk::unreal { class UClass; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -50,7 +50,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ClassProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/ucomponentproperty.h b/src/unrealsdk/unreal/classes/properties/ucomponentproperty.h index bd9a0f5..16ff0f5 100644 --- a/src/unrealsdk/unreal/classes/properties/ucomponentproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ucomponentproperty.h @@ -9,7 +9,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -31,7 +31,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ComponentProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h index 0beb613..d645569 100644 --- a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h @@ -11,7 +11,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -54,7 +54,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"DelegateProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uenumproperty.h b/src/unrealsdk/unreal/classes/properties/uenumproperty.h index 8f573df..30e4acb 100644 --- a/src/unrealsdk/unreal/classes/properties/uenumproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uenumproperty.h @@ -12,7 +12,7 @@ namespace unrealsdk::unreal { class UEnum; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -61,7 +61,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"EnumProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h index 16fff68..e52abaf 100644 --- a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h @@ -13,7 +13,7 @@ namespace unrealsdk::unreal { class UClass; class UObject; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -53,7 +53,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"InterfaceProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h index 0ed7f03..3f811f7 100644 --- a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h @@ -12,7 +12,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -57,7 +57,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"MulticastDelegateProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h index 5208c2f..62df7d1 100644 --- a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h @@ -13,7 +13,7 @@ namespace unrealsdk::unreal { class UClass; class UObject; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -53,7 +53,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"ObjectProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/ustrproperty.h b/src/unrealsdk/unreal/classes/properties/ustrproperty.h index e42eace..2bd6630 100644 --- a/src/unrealsdk/unreal/classes/properties/ustrproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ustrproperty.h @@ -10,7 +10,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -38,7 +38,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"StrProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/ustructproperty.h b/src/unrealsdk/unreal/classes/properties/ustructproperty.h index 9cad83f..8ff1dfa 100644 --- a/src/unrealsdk/unreal/classes/properties/ustructproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ustructproperty.h @@ -13,7 +13,7 @@ namespace unrealsdk::unreal { class UScriptStruct; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -54,7 +54,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"StructProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/utextproperty.h b/src/unrealsdk/unreal/classes/properties/utextproperty.h index a0ebaab..38947e3 100644 --- a/src/unrealsdk/unreal/classes/properties/utextproperty.h +++ b/src/unrealsdk/unreal/classes/properties/utextproperty.h @@ -10,7 +10,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -38,7 +38,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"TextProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.h b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.h index 149b973..a38fe99 100644 --- a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.h @@ -12,7 +12,7 @@ namespace unrealsdk::unreal { class UObject; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -41,7 +41,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"WeakObjectProperty"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index 25f1f4d..f356b2a 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -10,7 +10,7 @@ namespace unrealsdk::unreal { struct FImplementedInterface; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -40,12 +40,12 @@ class UClass : public UStruct { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) private: -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK uint8_t UnknownData00[0x70]; UObject* ClassDefaultObject_internal; uint8_t UnknownData01[0xA0]; TArray Interfaces_internal; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW // Misc Fields I found within this block in BL2, but which we don't care about enough for me to // find in UE4, or to want to increase the compile times by including @@ -59,6 +59,8 @@ class UClass : public UStruct { uint8_t UnknownData01[0x48]; TArray Interfaces_internal; +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-magic-numbers, readability-identifier-naming) public: @@ -83,7 +85,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/uenum.cpp b/src/unrealsdk/unreal/classes/uenum.cpp index 26f2fb5..1b305b9 100644 --- a/src/unrealsdk/unreal/classes/uenum.cpp +++ b/src/unrealsdk/unreal/classes/uenum.cpp @@ -5,7 +5,8 @@ namespace unrealsdk::unreal { -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK + std::unordered_map UEnum::get_names(void) const { auto bl3_enum = reinterpret_cast(this); std::unordered_map output; @@ -16,7 +17,7 @@ std::unordered_map UEnum::get_names(void) const { return output; } -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW std::unordered_map UEnum::get_names(void) const { auto bl2_enum = reinterpret_cast(this); @@ -28,6 +29,8 @@ std::unordered_map UEnum::get_names(void) const { return output; } +#else +#error Unknown SDK flavour #endif } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/uenum.h b/src/unrealsdk/unreal/classes/uenum.h index a553593..81012e1 100644 --- a/src/unrealsdk/unreal/classes/uenum.h +++ b/src/unrealsdk/unreal/classes/uenum.h @@ -11,7 +11,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -32,12 +32,14 @@ class UEnum : public UField { private: // NOLINTBEGIN(readability-identifier-naming) -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK UnmanagedFString CppType; TArray> Names; int64_t CppForm; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW TArray Names; +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-identifier-naming) @@ -60,7 +62,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index efe66d3..c15b18b 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -6,7 +6,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -55,7 +55,7 @@ struct ClassTraits { #if defined(__clang__) #pragma clang diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index d4afaf5..93cc4dc 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -7,7 +7,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -41,7 +41,7 @@ class UFunction : public UStruct { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) private: -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK uint32_t FunctionFlags_internal; uint8_t NumParams_internal; uint16_t ParamsSize_internal; @@ -52,7 +52,7 @@ class UFunction : public UStruct { UFunction* EventGraphFunction; int32_t EventGraphCallOffset; void* Func; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW uint32_t FunctionFlags_internal; uint16_t iNative; uint16_t RepOffset; @@ -63,7 +63,8 @@ class UFunction : public UStruct { uint16_t ReturnValueOffset_internal; uint8_t UnknownData00[0x6]; void* Func; - +#else +#error Unknown SDK flavour #endif public: /** @@ -85,7 +86,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/uobject.cpp b/src/unrealsdk/unreal/classes/uobject.cpp index 2c79b46..14bff53 100644 --- a/src/unrealsdk/unreal/classes/uobject.cpp +++ b/src/unrealsdk/unreal/classes/uobject.cpp @@ -93,11 +93,14 @@ void UObject::post_edit_change_property(const FName& name) const { void UObject::post_edit_change_property(UProperty* prop) const { FPropertyChangedEvent event{prop}; -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW constexpr auto default_idx = 19; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK constexpr auto default_idx = 78; +#else +#error Unknown SDK flavour #endif + static auto idx = config::get_int("unrealsdk.uobject_post_edit_change_property_vf_index") .value_or(default_idx); @@ -110,11 +113,14 @@ void UObject::post_edit_change_chain_property(UProperty* prop, FEditPropertyChain edit_chain{chain}; FPropertyChangedChainEvent event{prop, &edit_chain}; -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW constexpr auto default_idx = 18; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK constexpr auto default_idx = 77; +#else +#error Unknown SDK flavour #endif + static auto idx = config::get_int("unrealsdk.uobject_post_edit_change_chain_property_vf_index") .value_or(default_idx); // NOLINT(readability-magic-numbers) diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index 0ab60ae..b48289f 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -10,7 +10,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -32,10 +32,12 @@ class UObject { // NOLINTBEGIN(readability-identifier-naming) -#if UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using object_flags_type = uint32_t; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW using object_flags_type = uint64_t; +#else +#error Unknown SDK flavour #endif // These fields become member functions, returning a reference into the object. @@ -49,7 +51,7 @@ class UObject { UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECT_FIELDS); -#if UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK private: int32_t ObjectFlags_member; int32_t InternalIndex_member; @@ -58,7 +60,7 @@ class UObject { UObject* Outer_member; public: -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW private: // TODO: remove @@ -76,6 +78,8 @@ class UObject { UObject* ObjectArchetype; public: +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-identifier-naming) @@ -192,7 +196,7 @@ struct ClassTraits { static inline const wchar_t* const NAME = L"Object"; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/uproperty.cpp b/src/unrealsdk/unreal/classes/uproperty.cpp index 3c59ef5..64e115e 100644 --- a/src/unrealsdk/unreal/classes/uproperty.cpp +++ b/src/unrealsdk/unreal/classes/uproperty.cpp @@ -13,7 +13,7 @@ namespace unrealsdk::unreal { UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UProperty, UNREALSDK_UPROPERTY_FIELDS); -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW size_t UProperty::class_size(void) { static size_t size = 0; diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index 6e231fe..acbbbda 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -9,7 +9,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -38,7 +38,7 @@ This means: class UProperty : public UField { public: -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW static constexpr auto PROP_FLAG_OPTIONAL = 0x10; #endif static constexpr auto PROP_FLAG_PARAM = 0x80; @@ -54,10 +54,12 @@ class UProperty : public UField { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) -#if UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using property_flags_type = uint64_t; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW using property_flags_type = uint32_t; +#else +#error Unknown SDK flavour #endif // These fields become member functions, returning a reference into the object. @@ -71,7 +73,7 @@ class UProperty : public UField { UNREALSDK_DEFINE_FIELDS_HEADER(UProperty, UNREALSDK_UPROPERTY_FIELDS); -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK private: int32_t ArrayDim_member; int32_t ElementSize_member; @@ -94,8 +96,7 @@ class UProperty : public UField { UProperty* DestructorLinkNext; /** In memory only: Linked list of properties requiring post constructor initialization.**/ UProperty* PostConstructLinkNext; // 0x0030(0x0040) MISSED OFFSET -#else - +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW private: int32_t ArrayDim_member; int32_t ElementSize_member; @@ -114,8 +115,10 @@ class UProperty : public UField { * @return The size of this class. */ [[nodiscard]] static size_t class_size(void); - +#else +#error Unknown SDK flavour #endif + public: /** * @brief Reads a field on a UProperty subclass, taking into account it's variable length. @@ -131,12 +134,14 @@ class UProperty : public UField { typename FieldType, typename = std::enable_if_t>> FieldType read_field(FieldType PropertyType::* field) const { -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK return reinterpret_cast(this)->*field; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW return *reinterpret_cast( reinterpret_cast(&(reinterpret_cast(this)->*field)) - sizeof(UProperty) + UProperty::class_size()); +#else +#error Unknown SDK flavour #endif } @@ -152,7 +157,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index 42fdaa9..be816d5 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -6,7 +6,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif #if defined(__clang__) @@ -54,7 +54,7 @@ struct ClassTraits { #if defined(__clang__) #pragma clang diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index f5f923b..63a6eab 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -133,10 +133,12 @@ utils::IteratorProxy UStruct::superfields(void) con #pragma endregion size_t UStruct::get_struct_size(void) const { -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK return (this->PropertySize + this->MinAlignment - 1) & ~(this->MinAlignment - 1); -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW return this->PropertySize; +#else +#error Unknown SDK flavour #endif } diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index dfba873..eb4b883 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -9,7 +9,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -42,7 +42,7 @@ class UStruct : public UField { private: // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK /* Struct this inherits from, may be null */ UStruct* SuperField_member; /* Pointer to start of linked list of child fields */ @@ -68,7 +68,7 @@ class UStruct : public UField { /* Array of object references embedded in script code. Mirrored for easy access by realtime * garbage collection code */ TArray ScriptObjectReferences; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW uint8_t UnknownData00[0x8]; UStruct* SuperField_member; @@ -82,7 +82,8 @@ class UStruct : public UField { uint8_t UnknownData02[0x10]; TArray ScriptObjectReferences; - +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-magic-numbers, readability-identifier-naming) @@ -230,7 +231,7 @@ struct ClassTraits { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index f40edc7..54d8114 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -15,7 +15,7 @@ namespace unrealsdk::unreal::offsets { // Since this type is shared between dlls, also force consistent padding between compilers, even // though this isn't an unreal type -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -67,7 +67,7 @@ struct OffsetList { UNREALSDK__DYNAMIC_OFFSET_TYPES(UNREALSDK_OFFSETS__NESTED_FROM_NAMESPACE) \ } -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fframe.h b/src/unrealsdk/unreal/structs/fframe.h index a46ff38..8546757 100644 --- a/src/unrealsdk/unreal/structs/fframe.h +++ b/src/unrealsdk/unreal/structs/fframe.h @@ -3,7 +3,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -29,11 +29,15 @@ struct FOutParamRec { struct FOutputDevice { void* VfTable; -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW private: uint32_t bAllowSuppression; public: +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK + // Intentionally empty +#else +#error Unknown SDK flavour #endif uint32_t bSuppressEventTag; @@ -48,12 +52,16 @@ struct FFrame : public FOutputDevice { uint8_t* Code; void* Locals; -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK private: UProperty* LastProperty; void* LastPropertyAddress; public: +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + // Intentionally empty +#else +#error Unknown SDK flavour #endif FFrame* PreviousFrame; @@ -75,7 +83,7 @@ struct FFrame : public FOutputDevice { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fimplementedinterface.cpp b/src/unrealsdk/unreal/structs/fimplementedinterface.cpp index d22adff..2407c2a 100644 --- a/src/unrealsdk/unreal/structs/fimplementedinterface.cpp +++ b/src/unrealsdk/unreal/structs/fimplementedinterface.cpp @@ -6,10 +6,12 @@ namespace unrealsdk::unreal { size_t FImplementedInterface::get_pointer_offset() const { -#if UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK return this->isNative ? 0 : this->PointerOffset; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW return this->VFTableProperty == nullptr ? 0 : this->VFTableProperty->Offset_Internal(); +#else +#error Unknown SDK flavour #endif } diff --git a/src/unrealsdk/unreal/structs/fimplementedinterface.h b/src/unrealsdk/unreal/structs/fimplementedinterface.h index 703b86d..2825525 100644 --- a/src/unrealsdk/unreal/structs/fimplementedinterface.h +++ b/src/unrealsdk/unreal/structs/fimplementedinterface.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -18,11 +18,13 @@ struct FImplementedInterface { UClass* Class; private: -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK int32_t PointerOffset; bool isNative; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW UStructProperty* VFTableProperty; // May be null (if native?) +#else +#error Unknown SDK flavour #endif // NOLINTEND(readability-identifier-naming) @@ -36,7 +38,7 @@ struct FImplementedInterface { [[nodiscard]] size_t get_pointer_offset(void) const; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fname.h b/src/unrealsdk/unreal/structs/fname.h index 619c9d3..a826175 100644 --- a/src/unrealsdk/unreal/structs/fname.h +++ b/src/unrealsdk/unreal/structs/fname.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -53,7 +53,7 @@ struct FName { operator std::wstring() const; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fpropertychangeevent.h b/src/unrealsdk/unreal/structs/fpropertychangeevent.h index a6911a0..fbd1858 100644 --- a/src/unrealsdk/unreal/structs/fpropertychangeevent.h +++ b/src/unrealsdk/unreal/structs/fpropertychangeevent.h @@ -7,7 +7,7 @@ namespace unrealsdk::unreal { class UProperty; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -63,7 +63,7 @@ struct FPropertyChangedChainEvent : public FPropertyChangedEvent { FEditPropertyChain* PropertyChain{}; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp index 46b8924..9cb3b71 100644 --- a/src/unrealsdk/unreal/structs/fscriptdelegate.cpp +++ b/src/unrealsdk/unreal/structs/fscriptdelegate.cpp @@ -9,7 +9,7 @@ namespace unrealsdk::unreal { -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW UObject* FScriptDelegate::get_object(void) const { return this->object; @@ -19,7 +19,7 @@ void FScriptDelegate::set_object(UObject* obj) { this->object = obj; } -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK UObject* FScriptDelegate::get_object(void) const { return unrealsdk::gobjects().get_weak_object(&this->object); @@ -28,6 +28,8 @@ UObject* FScriptDelegate::get_object(void) const { void FScriptDelegate::set_object(UObject* obj) { unrealsdk::gobjects().set_weak_object(&this->object, obj); } +#else +#error Unknown SDK flavour #endif [[nodiscard]] std::optional FScriptDelegate::as_function(void) const { diff --git a/src/unrealsdk/unreal/structs/fscriptdelegate.h b/src/unrealsdk/unreal/structs/fscriptdelegate.h index 44ffbfc..63f404d 100644 --- a/src/unrealsdk/unreal/structs/fscriptdelegate.h +++ b/src/unrealsdk/unreal/structs/fscriptdelegate.h @@ -8,7 +8,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -17,10 +17,12 @@ class UFunction; struct FScriptDelegate { private: -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW UObject* object = nullptr; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK FWeakObjectPtr object; +#else +#error Unknown SDK flavour #endif public: @@ -66,7 +68,7 @@ struct FScriptDelegate { const UFunction* signature); }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/ftext.cpp b/src/unrealsdk/unreal/structs/ftext.cpp index c48b1f1..04b8cc9 100644 --- a/src/unrealsdk/unreal/structs/ftext.cpp +++ b/src/unrealsdk/unreal/structs/ftext.cpp @@ -9,7 +9,7 @@ namespace unrealsdk::unreal { -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK FText::FText(std::string_view str) : FText(utils::widen(str)) {} FText::FText(std::wstring_view str) : data(), flags() { @@ -53,7 +53,7 @@ FText::~FText() { } } -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW FText::FText(std::string_view /* str */) : data(), flags() { (void)this; @@ -95,6 +95,8 @@ FText::~FText() { (void)this; } +#else +#error Unknown SDK flavour #endif } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/structs/ftext.h b/src/unrealsdk/unreal/structs/ftext.h index 95d6e2c..6e1f19f 100644 --- a/src/unrealsdk/unreal/structs/ftext.h +++ b/src/unrealsdk/unreal/structs/ftext.h @@ -6,7 +6,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -66,7 +66,7 @@ struct FText { FText& operator=(FText&&) = delete; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/fweakobjectptr.h b/src/unrealsdk/unreal/structs/fweakobjectptr.h index a85d441..d607a03 100644 --- a/src/unrealsdk/unreal/structs/fweakobjectptr.h +++ b/src/unrealsdk/unreal/structs/fweakobjectptr.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -21,7 +21,7 @@ struct FWeakObjectPtr { int32_t object_serial_number = 0; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/gnames.cpp b/src/unrealsdk/unreal/structs/gnames.cpp index 807380b..a5c36be 100644 --- a/src/unrealsdk/unreal/structs/gnames.cpp +++ b/src/unrealsdk/unreal/structs/gnames.cpp @@ -8,7 +8,7 @@ bool FNameEntry::is_wide(void) const { return (this->Index & NAME_WIDE_MASK) != 0; } -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK FNameEntry* TStaticIndirectArrayThreadSafeRead_FNameEntry::at(size_t idx) const { if (std::cmp_greater_equal(idx, this->Count)) { diff --git a/src/unrealsdk/unreal/structs/gnames.h b/src/unrealsdk/unreal/structs/gnames.h index 36aac27..1c09074 100644 --- a/src/unrealsdk/unreal/structs/gnames.h +++ b/src/unrealsdk/unreal/structs/gnames.h @@ -3,7 +3,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -19,7 +19,7 @@ struct FNameEntry { // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK int32_t Index; private: @@ -27,7 +27,7 @@ struct FNameEntry { public: FNameEntry* HashNext; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW private: uint8_t UnknownData00[0x08]; @@ -38,6 +38,8 @@ struct FNameEntry { uint8_t UnknownData01[0x04]; public: +#else +#error Unknown SDK flavour #endif union { @@ -55,7 +57,7 @@ struct FNameEntry { [[nodiscard]] bool is_wide(void) const; }; -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK // NOLINTNEXTLINE(readability-identifier-naming) struct TStaticIndirectArrayThreadSafeRead_FNameEntry { @@ -87,7 +89,7 @@ struct TStaticIndirectArrayThreadSafeRead_FNameEntry { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/gobjects.cpp b/src/unrealsdk/unreal/structs/gobjects.cpp index 28ccc52..f63f5bd 100644 --- a/src/unrealsdk/unreal/structs/gobjects.cpp +++ b/src/unrealsdk/unreal/structs/gobjects.cpp @@ -4,7 +4,7 @@ namespace unrealsdk::unreal { -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK FUObjectItem* FChunkedFixedUObjectArray::at(size_t idx) const { if (std::cmp_greater_equal(idx, this->Count)) { diff --git a/src/unrealsdk/unreal/structs/gobjects.h b/src/unrealsdk/unreal/structs/gobjects.h index 7d9b37e..a815713 100644 --- a/src/unrealsdk/unreal/structs/gobjects.h +++ b/src/unrealsdk/unreal/structs/gobjects.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { class UObject; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -14,7 +14,7 @@ class UObject; #pragma clang diagnostic ignored "-Wunused-private-field" #endif -#if defined(UE4) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK // There are a few fields we need to modify to deal with weak pointers. To be thread safe we need // them to be atomic fields - as unreal itself does. The fields are all aligned as normal, we expect @@ -95,7 +95,7 @@ struct FUObjectArray { #pragma GCC diagnostic pop #endif -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/tarray.h b/src/unrealsdk/unreal/structs/tarray.h index a6e5738..ba65e37 100644 --- a/src/unrealsdk/unreal/structs/tarray.h +++ b/src/unrealsdk/unreal/structs/tarray.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -190,7 +190,7 @@ struct TArray { #pragma endregion }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/tpair.h b/src/unrealsdk/unreal/structs/tpair.h index df77885..5874de4 100644 --- a/src/unrealsdk/unreal/structs/tpair.h +++ b/src/unrealsdk/unreal/structs/tpair.h @@ -3,7 +3,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -13,7 +13,7 @@ struct TPair { ValueType value; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/tpersistentobjectptr.h b/src/unrealsdk/unreal/structs/tpersistentobjectptr.h index c720f9f..9ba85bf 100644 --- a/src/unrealsdk/unreal/structs/tpersistentobjectptr.h +++ b/src/unrealsdk/unreal/structs/tpersistentobjectptr.h @@ -14,7 +14,7 @@ class USoftObjectProperty; class WrappedStruct; class WrappedArray; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -100,7 +100,7 @@ struct FLazyObjectPath { struct FSoftObjectPtr : public TPersistentObjectPtr {}; struct FLazyObjectPtr : public TPersistentObjectPtr {}; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/structs/tsharedpointer.h b/src/unrealsdk/unreal/structs/tsharedpointer.h index 45e2301..7f5ac50 100644 --- a/src/unrealsdk/unreal/structs/tsharedpointer.h +++ b/src/unrealsdk/unreal/structs/tsharedpointer.h @@ -5,7 +5,7 @@ namespace unrealsdk::unreal { -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif @@ -40,7 +40,7 @@ struct TSharedPointer { TReferenceController* controller; }; -#if defined(_MSC_VER) && defined(ARCH_X86) +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/wrappers/bound_function.cpp b/src/unrealsdk/unreal/wrappers/bound_function.cpp index 7d2b17a..61c6bfa 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.cpp +++ b/src/unrealsdk/unreal/wrappers/bound_function.cpp @@ -28,11 +28,12 @@ void validate_no_more_params(UProperty* prop) { if ((prop->PropertyFlags() & UProperty::PROP_FLAG_RETURN) != 0) { continue; } -#ifdef UE3 - if ((prop->PropertyFlags() & UProperty::PROP_FLAG_OPTIONAL) != 0) { - continue; + if constexpr (requires { UProperty::PROP_FLAG_OPTIONAL; }) { + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_OPTIONAL) != 0) { + continue; + } } -#endif + throw std::runtime_error("Too few parameters to function call!"); } } diff --git a/src/unrealsdk/unreal/wrappers/gnames.cpp b/src/unrealsdk/unreal/wrappers/gnames.cpp index 7f9fa89..8968179 100644 --- a/src/unrealsdk/unreal/wrappers/gnames.cpp +++ b/src/unrealsdk/unreal/wrappers/gnames.cpp @@ -8,7 +8,7 @@ namespace unrealsdk::unreal { GNames::GNames(void) : internal(nullptr) {} GNames::GNames(internal_type internal) : internal(internal) {} -#if defined(UE4) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK size_t GNames::size(void) const { return this->internal->Count; @@ -21,7 +21,7 @@ FNameEntry* GNames::at(size_t idx) const { return this->internal->at(idx); } -#elif defined(UE3) +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW size_t GNames::size(void) const { return this->internal->size(); @@ -32,7 +32,7 @@ FNameEntry* GNames::at(size_t idx) const { } #else -#error Unknown UE version +#error Unknown SDK flavour #endif } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/wrappers/gnames.h b/src/unrealsdk/unreal/wrappers/gnames.h index de8a80d..f4c2cb9 100644 --- a/src/unrealsdk/unreal/wrappers/gnames.h +++ b/src/unrealsdk/unreal/wrappers/gnames.h @@ -5,7 +5,7 @@ #include "unrealsdk/unreal/structs/gnames.h" -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #include "unrealsdk/unreal/structs/tarray.h" #endif @@ -13,11 +13,14 @@ namespace unrealsdk::unreal { class GNames { public: -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using internal_type = TStaticIndirectArrayThreadSafeRead_FNameEntry*; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW using internal_type = TArray*; +#else +#error Unknown SDK flavour #endif + private: internal_type internal; diff --git a/src/unrealsdk/unreal/wrappers/gobjects.cpp b/src/unrealsdk/unreal/wrappers/gobjects.cpp index 029093f..f5122b1 100644 --- a/src/unrealsdk/unreal/wrappers/gobjects.cpp +++ b/src/unrealsdk/unreal/wrappers/gobjects.cpp @@ -4,11 +4,13 @@ #include "unrealsdk/unreal/structs/fweakobjectptr.h" #include "unrealsdk/unreal/wrappers/gobjects.h" -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK #include "unrealsdk/unreal/structs/gobjects.h" -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/version_error.h" +#else +#error Unknown SDK flavour #endif namespace unrealsdk::unreal { @@ -70,7 +72,7 @@ GObjects::Iterator GObjects::end(void) { GObjects::GObjects(void) : internal(nullptr) {} GObjects::GObjects(internal_type internal) : internal(internal) {} -#if defined(UE4) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK size_t GObjects::size(void) const { return this->internal->ObjObjects.Count; @@ -125,7 +127,7 @@ void GObjects::set_weak_object(FWeakObjectPtr* ptr, const UObject* obj) const { } } -#elif defined(UE3) +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW size_t GObjects::size(void) const { return this->internal->size(); @@ -147,7 +149,7 @@ void GObjects::set_weak_object(FWeakObjectPtr* /* ptr */, const UObject* /* obj } #else -#error Unknown UE version +#error Unknown SDK flavour #endif } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/wrappers/gobjects.h b/src/unrealsdk/unreal/wrappers/gobjects.h index cbf543d..f62565a 100644 --- a/src/unrealsdk/unreal/wrappers/gobjects.h +++ b/src/unrealsdk/unreal/wrappers/gobjects.h @@ -3,10 +3,12 @@ #include "unrealsdk/pch.h" -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK #include "unrealsdk/unreal/structs/gobjects.h" -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #include "unrealsdk/unreal/structs/tarray.h" +#else +#error Unknown SDK flavour #endif namespace unrealsdk::unreal { @@ -16,11 +18,14 @@ class UObject; class GObjects { public: -#ifdef UE4 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using internal_type = FUObjectArray*; -#else +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW using internal_type = TArray*; +#else +#error Unknown SDK flavour #endif + private: internal_type internal; diff --git a/src/unrealsdk/unreal/wrappers/weak_pointer.h b/src/unrealsdk/unreal/wrappers/weak_pointer.h index 6cdf7a4..f492f1c 100644 --- a/src/unrealsdk/unreal/wrappers/weak_pointer.h +++ b/src/unrealsdk/unreal/wrappers/weak_pointer.h @@ -3,7 +3,7 @@ #include "unrealsdk/pch.h" -#ifdef UE3 +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #define UNREALSDK_EMULATED_WEAK_POINTER #endif From d63aab373df95d63f266976d14597370db2211dc Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 14:00:14 +1200 Subject: [PATCH 63/81] linting fixups --- CMakePresets.json | 6 ++---- README.md | 4 ++-- src/unrealsdk/game/bl1/antidebug.cpp | 19 +++++-------------- src/unrealsdk/game/bl3/object.cpp | 2 +- src/unrealsdk/game/bl3/offsets.h | 1 + src/unrealsdk/unreal/classes/uenum.cpp | 1 + .../unreal/wrappers/bound_function.cpp | 8 ++++---- 7 files changed, 16 insertions(+), 25 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 29391df..46b3f32 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -117,8 +117,7 @@ "strategy": "external" }, "cacheVariables": { - "UNREALSDK_FLAVOUR": "WILLOW", - "UNREALSDK_ARCH": "x86" + "UNREALSDK_FLAVOUR": "WILLOW" } }, { @@ -129,8 +128,7 @@ "strategy": "external" }, "cacheVariables": { - "UNREALSDK_FLAVOUR": "OAK", - "UNREALSDK_ARCH": "x64" + "UNREALSDK_FLAVOUR": "OAK" } }, { diff --git a/README.md b/README.md index 7c233a2..4076309 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ target_link_libraries(MyProject PRIVATE unrealsdk) ``` You must define the build "flavour" before including the sdk. Each flavour corresponds to a set of -games running on a similar (but not neccesarily the exact same) unreal engine versions. Essentially, +games running on a similar (but not necessarily the exact same) unreal engine versions. Essentially, if two games are similar enough to use the same mod manager, and support mostly the same mods, they -should work under the same build flavour. Some level of runtime adaptation is suppported (e.g. core +should work under the same build flavour. Some level of runtime adaptation is supported (e.g. core types may slightly change size or layout), but accounting for large engine internal differences (e.g. the different GNames data structures) at runtime is a non-goal. diff --git a/src/unrealsdk/game/bl1/antidebug.cpp b/src/unrealsdk/game/bl1/antidebug.cpp index 4cfa1ec..c8db461 100644 --- a/src/unrealsdk/game/bl1/antidebug.cpp +++ b/src/unrealsdk/game/bl1/antidebug.cpp @@ -27,18 +27,9 @@ typedef NTSTATUS(WINAPI* NtQueryInformationProcess_func)(HANDLE ProcessHandle, ULONG ProcessInformationLength, PULONG ReturnLength); -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" -#endif - -constexpr auto ThreadHideFromDebugger = static_cast(17); -constexpr auto ProcessDebugObjectHandle = static_cast(30); - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - +// These are undocumented values, not in the header, treat as size_t to avoid enum conversion errors +constexpr size_t ThreadHideFromDebugger = 17; +constexpr size_t ProcessDebugObjectHandle = 30; // NOLINTEND(readability-identifier-naming) // NOLINTBEGIN(readability-identifier-naming) @@ -48,7 +39,7 @@ NTSTATUS NTAPI NtSetInformationThread_hook(HANDLE ThreadHandle, PVOID ThreadInformation, ULONG ThreadInformationLength) { // NOLINTEND(readability-identifier-naming) - if (ThreadInformationClass == ThreadHideFromDebugger) { + if (static_cast(ThreadInformationClass) == ThreadHideFromDebugger) { return STATUS_SUCCESS; } @@ -66,7 +57,7 @@ NTSTATUS WINAPI NtQueryInformationProcess_hook(HANDLE ProcessHandle, ULONG ProcessInformationLength, PULONG ReturnLength) { // NOLINTEND(readability-identifier-naming) - if (ProcessInformationClass == ProcessDebugObjectHandle) { + if (static_cast(ProcessInformationClass) == ProcessDebugObjectHandle) { return STATUS_PORT_NOT_SET; } diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index 218d02a..99c8888 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -17,7 +17,7 @@ namespace unrealsdk::game { namespace { -using construct_obj_func = UObject* (*)(UClass* cls, +using construct_obj_func = UObject* (*)(UClass * cls, UObject* obj, FName name, uint32_t flags, diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index a5435e8..7fb37ac 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -3,6 +3,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uconst.h" +#include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" diff --git a/src/unrealsdk/unreal/classes/uenum.cpp b/src/unrealsdk/unreal/classes/uenum.cpp index 1b305b9..4e3c165 100644 --- a/src/unrealsdk/unreal/classes/uenum.cpp +++ b/src/unrealsdk/unreal/classes/uenum.cpp @@ -1,6 +1,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/game/bl3/offsets.h" #include "unrealsdk/unreal/structs/fname.h" namespace unrealsdk::unreal { diff --git a/src/unrealsdk/unreal/wrappers/bound_function.cpp b/src/unrealsdk/unreal/wrappers/bound_function.cpp index 61c6bfa..7ede84a 100644 --- a/src/unrealsdk/unreal/wrappers/bound_function.cpp +++ b/src/unrealsdk/unreal/wrappers/bound_function.cpp @@ -28,11 +28,11 @@ void validate_no_more_params(UProperty* prop) { if ((prop->PropertyFlags() & UProperty::PROP_FLAG_RETURN) != 0) { continue; } - if constexpr (requires { UProperty::PROP_FLAG_OPTIONAL; }) { - if ((prop->PropertyFlags() & UProperty::PROP_FLAG_OPTIONAL) != 0) { - continue; - } +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + if ((prop->PropertyFlags() & UProperty::PROP_FLAG_OPTIONAL) != 0) { + continue; } +#endif throw std::runtime_error("Too few parameters to function call!"); } From 18f69eb6d43b5cbe225a7eb48344de9cc2965615 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 19:19:06 +1200 Subject: [PATCH 64/81] remove postbuild script this was nice when first developing the repo, but these days it's better to load up one of the mod manager repos and create a new preset with custom install dir --- .cruft.json | 4 ++-- .gitignore | 4 ---- CMakeLists.txt | 21 --------------------- README.md | 6 ++---- postbuild.template | 20 -------------------- 5 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 postbuild.template diff --git a/.cruft.json b/.cruft.json index ab66239..3da4c80 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "git@github.com:bl-sdk/common_dotfiles.git", - "commit": "cee5c9dbf5b95f57bb636e5138171aa6a4964cf1", + "commit": "597ec422d3b5692927f325b9b5c2ae42288e8cc5", "checkout": null, "context": { "cookiecutter": { @@ -16,7 +16,7 @@ "include_cpp": true, "include_py": false, "_template": "git@github.com:bl-sdk/common_dotfiles.git", - "_commit": "cee5c9dbf5b95f57bb636e5138171aa6a4964cf1" + "_commit": "597ec422d3b5692927f325b9b5c2ae42288e8cc5" } }, "directory": null diff --git a/.gitignore b/.gitignore index eb698b0..50e5591 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,6 @@ .gdbinit out -# User scripts with templates -postbuild -postbuild.bat - # CMake (from https://github.com/github/gitignore/blob/main/CMake.gitignore) CMakeLists.txt.user CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index a65d199..9a63b5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,25 +109,4 @@ else() # Unconditionally add the exporting flag # Add it privately, so it doesn't appear in anything linking against this target_compile_definitions(unrealsdk PRIVATE "UNREALSDK_EXPORTING") - - # Postbuild - if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - set(POSTBUILD_SCRIPT "postbuild") - if(CMAKE_HOST_WIN32) - set(POSTBUILD_SCRIPT "${POSTBUILD_SCRIPT}.bat") - endif() - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${POSTBUILD_SCRIPT}") - add_custom_command( - TARGET unrealsdk - POST_BUILD - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/${POSTBUILD_SCRIPT}" - ARGS - "$>" - "${UNREALSDK_UE_VERSION}" - "${UNREALSDK_ARCH}" - "$,DEBUG,RELEASE>" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) - endif() - endif() endif() diff --git a/README.md b/README.md index 4076309..f550f1e 100644 --- a/README.md +++ b/README.md @@ -174,15 +174,13 @@ To build: git clone --recursive https://github.com/bl-sdk/unrealsdk.git ``` -2. (OPTIONAL) Copy `postbuild.template`, and edit it to copy files to your game install directories. - -3. Choose a preset, and run CMake. Most IDEs will be able to do this for you, +2. Choose a preset, and run CMake. Most IDEs will be able to do this for you, ``` cmake . --preset msvc-ue4-x64-debug cmake --build out/build/msvc-ue4-x64-debug ``` -4. (OPTIONAL) If you're debugging a game on Steam, add a `steam_appid.txt` in the same folder as the +3. (OPTIONAL) If you're debugging a game on Steam, add a `steam_appid.txt` in the same folder as the executable, containing the game's Steam App Id. Normally, games compiled with Steamworks will call diff --git a/postbuild.template b/postbuild.template deleted file mode 100644 index 81c1efc..0000000 --- a/postbuild.template +++ /dev/null @@ -1,20 +0,0 @@ -REM This script is run after a build completes, intended for advanced customization of install dir. -REM On Windows, it must be renamed to `postbuild.bat`, and thus should be a batch file. On other -REM platforms, it should just be called `postbuild`, and can be any executable program. -REM If the script exists, it will be called with 4 arguments: The path to the built target and the -REM build's UE version, architecture, and type (`DEBUG` or `RELEASE`). - -REM Example (batch) implementation: - -@echo off - -set "target=%~1" -set "ue_version=%~2" -set "architecture=%~3" -set "build_type=%~4" - -if /i "%ue_version%" == "UE4" ( - if "%architecture%" == "x64" ( - copy /Y "%target%" "C:\Program Files (x86)\Steam\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Plugins\unrealsdk.dll" - ) -) From 45b1fae1814ad06cfb025d2c5c2ccb63e41db8fb Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 20:16:30 +1200 Subject: [PATCH 65/81] make basic copyable properties generic --- src/unrealsdk/game/bl1/offsets.h | 18 ++++ src/unrealsdk/game/bl2/offsets.h | 18 ++++ src/unrealsdk/game/bl3/offsets.h | 21 ++++- src/unrealsdk/game/tps/offsets.h | 16 ++++ .../classes/properties/attribute_property.cpp | 15 +++ .../classes/properties/attribute_property.h | 93 +++++++++++-------- .../classes/properties/copyable_property.h | 2 +- .../classes/properties/ubyteproperty.cpp | 8 +- .../unreal/classes/properties/ubyteproperty.h | 32 ++++--- src/unrealsdk/unreal/offset_list.h | 8 +- 10 files changed, 172 insertions(+), 59 deletions(-) create mode 100644 src/unrealsdk/unreal/classes/properties/attribute_property.cpp diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index ff0b95c..5bacf31 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -3,6 +3,8 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" @@ -123,6 +125,22 @@ using UFunction = bl2::generic::UFunction; using UConst = unreal::offsets::generic::UConst; using UEnum = bl2::generic::UEnum; +using UInt8Property = UProperty; +using UInt16Property = UProperty; +using UIntProperty = UProperty; +using UInt64Property = UProperty; +using UByteProperty = unreal::offsets::generic::UByteProperty; +using UUInt16Property = UProperty; +using UUInt32Property = UProperty; +using UUInt64Property = UProperty; +using UFloatProperty = UProperty; +using UDoubleProperty = UProperty; +using UNameProperty = UProperty; + +using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index 6f8b1a0..c2ef55f 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -2,6 +2,8 @@ #define UNREALSDK_GAME_BL2_OFFSETS_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -165,6 +167,22 @@ using UFunction = bl2::generic::UFunction; using UConst = unreal::offsets::generic::UConst; using UEnum = bl2::generic::UEnum; +using UInt8Property = UProperty; +using UInt16Property = UProperty; +using UIntProperty = UProperty; +using UInt64Property = UProperty; +using UByteProperty = unreal::offsets::generic::UByteProperty; +using UUInt16Property = UProperty; +using UUInt32Property = UProperty; +using UUInt64Property = UProperty; +using UFloatProperty = UProperty; +using UDoubleProperty = UProperty; +using UNameProperty = UProperty; + +using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 7fb37ac..4daf797 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -2,6 +2,8 @@ #define UNREALSDK_GAME_BL3_OFFSETS_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -132,11 +134,24 @@ class UEnum : public UField { unreal::UnmanagedFString CppType; unreal::TArray> Names; int64_t CppForm; - - public: - [[nodiscard]] unreal::TArray> get_names(void) const; }; +using UInt8Property = UProperty; +using UInt16Property = UProperty; +using UIntProperty = UProperty; +using UInt64Property = UProperty; +using UByteProperty = unreal::offsets::generic::UByteProperty; +using UUInt16Property = UProperty; +using UUInt32Property = UProperty; +using UUInt64Property = UProperty; +using UFloatProperty = UProperty; +using UDoubleProperty = UProperty; +using UNameProperty = UProperty; + +using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index 66647b5..b65a9bc 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -82,6 +82,22 @@ using UFunction = bl2::generic::UFunction; using UConst = bl2::UConst; using UEnum = bl2::UEnum; +using UInt8Property = UProperty; +using UInt16Property = UProperty; +using UIntProperty = UProperty; +using UInt64Property = UProperty; +using UByteProperty = unreal::offsets::generic::UByteProperty; +using UUInt16Property = UProperty; +using UUInt32Property = UProperty; +using UUInt64Property = UProperty; +using UFloatProperty = UProperty; +using UDoubleProperty = UProperty; +using UNameProperty = UProperty; + +using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/unreal/classes/properties/attribute_property.cpp b/src/unrealsdk/unreal/classes/properties/attribute_property.cpp new file mode 100644 index 0000000..b209b32 --- /dev/null +++ b/src/unrealsdk/unreal/classes/properties/attribute_property.cpp @@ -0,0 +1,15 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" + +namespace unrealsdk::unreal { + +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UByteAttributeProperty, + UNREALSDK_UBYTEATTRIBUTEPROPERTY_FIELDS); +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UFloatAttributeProperty, + UNREALSDK_UFLOATATTRIBUTEPROPERTY_FIELDS); +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UIntAttributeProperty, UNREALSDK_UINTATTRIBUTEPROPERTY_FIELDS); + +} // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/properties/attribute_property.h b/src/unrealsdk/unreal/classes/properties/attribute_property.h index 4a6b163..0e8e86f 100644 --- a/src/unrealsdk/unreal/classes/properties/attribute_property.h +++ b/src/unrealsdk/unreal/classes/properties/attribute_property.h @@ -10,56 +10,67 @@ namespace unrealsdk::unreal { -class UArrayProperty; -class UProperty; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif -template >> -class AttributeProperty : public T { - public: - AttributeProperty() = delete; - AttributeProperty(const AttributeProperty&) = delete; - AttributeProperty(AttributeProperty&&) = delete; - AttributeProperty& operator=(const AttributeProperty&) = delete; - AttributeProperty& operator=(AttributeProperty&&) = delete; - ~AttributeProperty() = delete; +class UArrayProperty; +class UProperty; - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) +namespace offsets::generic { + +template +class GenericAttributeProperty : public T { + public: + // NOLINTBEGIN(readability-identifier-naming) - private: UArrayProperty* ModifierStackProperty; - AttributeProperty* OtherAttributeProperty; + GenericAttributeProperty* OtherAttributeProperty; - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) - public: - /** - * @brief Gets the property used for this attribute's modifier stack. - * - * @return The modifier stack property. - */ - [[nodiscard]] UArrayProperty* get_modifier_stack_prop(void) const { - return this->read_field(&AttributeProperty::ModifierStackProperty); - } - - /** - * @brief Gets the other attribute property used for this attribute. - * - * @return The other attribute property. - */ - [[nodiscard]] AttributeProperty* get_other_attribute_property(void) const { - return this->read_field(&AttributeProperty::OtherAttributeProperty); - } + // NOLINTEND(readability-identifier-naming) }; -template -struct PropTraits> : public PropTraits {}; - -using UByteAttributeProperty = AttributeProperty; -using UFloatAttributeProperty = AttributeProperty; -using UIntAttributeProperty = AttributeProperty; +} // namespace offsets::generic + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEFINE_ATTRIBUTE_PROPERTY(class_name, base_class, fields) \ + class class_name : public base_class { \ + public: \ + class_name() = delete; \ + class_name(const class_name&) = delete; \ + class_name(class_name&&) = delete; \ + class_name& operator=(const class_name&) = delete; \ + class_name& operator=(class_name&&) = delete; \ + ~class_name() = delete; \ + /* NOLINTNEXTLINE(readability-identifier-naming) */ \ + UNREALSDK_DEFINE_FIELDS_HEADER(class_name, fields); \ + }; \ + template <> \ + struct PropTraits : public PropTraits {}; + +// These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UBYTEATTRIBUTEPROPERTY_FIELDS(X) \ + X(UArrayProperty*, ModifierStackProperty) \ + X(UByteAttributeProperty*, OtherAttributeProperty) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UFLOATATTRIBUTEPROPERTY_FIELDS(X) \ + X(UArrayProperty*, ModifierStackProperty) \ + X(UFloatAttributeProperty*, OtherAttributeProperty) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UINTATTRIBUTEPROPERTY_FIELDS(X) \ + X(UArrayProperty*, ModifierStackProperty) \ + X(UIntAttributeProperty*, OtherAttributeProperty) + +DEFINE_ATTRIBUTE_PROPERTY(UByteAttributeProperty, + UByteProperty, + UNREALSDK_UBYTEATTRIBUTEPROPERTY_FIELDS); +DEFINE_ATTRIBUTE_PROPERTY(UFloatAttributeProperty, + UFloatProperty, + UNREALSDK_UFLOATATTRIBUTEPROPERTY_FIELDS); +DEFINE_ATTRIBUTE_PROPERTY(UIntAttributeProperty, + UIntProperty, + UNREALSDK_UINTATTRIBUTEPROPERTY_FIELDS); template <> inline const wchar_t* const ClassTraits::NAME = L"ByteAttributeProperty"; @@ -68,6 +79,8 @@ inline const wchar_t* const ClassTraits::NAME = L"Float template <> inline const wchar_t* const ClassTraits::NAME = L"IntAttributeProperty"; +#undef DEFINE_ATTRIBUTE_PROPERTY + #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(pop) #endif diff --git a/src/unrealsdk/unreal/classes/properties/copyable_property.h b/src/unrealsdk/unreal/classes/properties/copyable_property.h index acda317..697e733 100644 --- a/src/unrealsdk/unreal/classes/properties/copyable_property.h +++ b/src/unrealsdk/unreal/classes/properties/copyable_property.h @@ -49,7 +49,7 @@ using UInt16Property = CopyableProperty; using UIntProperty = CopyableProperty; using UInt64Property = CopyableProperty; -// UByteProperty has an extra field in UE3, handled in a different file +// UByteProperty has an extra field we care about, it's handled in a different file using UUInt16Property = CopyableProperty; using UUInt32Property = CopyableProperty; using UUInt64Property = CopyableProperty; diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp b/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp index eabae20..9593376 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp @@ -1,10 +1,12 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" +#include "unrealsdk/unrealsdk.h" + namespace unrealsdk::unreal { -UEnum* UByteProperty::get_enum(void) const { - return this->read_field(&UByteProperty::Enum); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UByteProperty, UNREALSDK_UBYTEPROPERTY_FIELDS); } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h index 44efa7c..90180b3 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h @@ -10,12 +10,23 @@ namespace unrealsdk::unreal { -class UEnum; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UEnum; + +namespace offsets::generic { + +template +class UByteProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UEnum* Enum; +}; + +} // namespace offsets::generic + class UByteProperty : public CopyableProperty { public: UByteProperty() = delete; @@ -25,20 +36,19 @@ class UByteProperty : public CopyableProperty { UByteProperty& operator=(UByteProperty&&) = delete; ~UByteProperty() = delete; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UBYTEPROPERTY_FIELDS(X) X(UEnum*, Enum) + + // NOLINTNEXTLINE(readability-identifier-naming) + UNREALSDK_DEFINE_FIELDS_HEADER(UByteProperty, UNREALSDK_UBYTEPROPERTY_FIELDS); + private: // NOLINTBEGIN(readability-identifier-naming) - UEnum* Enum; + UEnum* Enum_member; // NOLINTEND(readability-identifier-naming) - - public: - /** - * @brief Get the enum associated with this property, if any. - * - * @return The enum. - */ - [[nodiscard]] UEnum* get_enum(void) const; }; template <> diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 54d8114..6239fb7 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -2,6 +2,8 @@ #define UNREALSDK_UNREAL_OFFSET_LIST_H #include "unrealsdk/pch.h" +#include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -28,7 +30,11 @@ namespace unrealsdk::unreal::offsets { X(UClass) \ X(UScriptStruct) \ X(UFunction) \ - X(UConst) + X(UConst) \ + X(UByteProperty) \ + X(UByteAttributeProperty) \ + X(UFloatAttributeProperty) \ + X(UIntAttributeProperty) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ From 67a7ca1dc8933910245418f589bab88cb7671ce6 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Mon, 12 May 2025 20:21:55 +1200 Subject: [PATCH 66/81] move naming lint suppressing into macro didn't realize we could do this --- src/unrealsdk/unreal/classes/properties/attribute_property.h | 1 - src/unrealsdk/unreal/classes/properties/ubyteproperty.h | 1 - src/unrealsdk/unreal/classes/uclass.h | 1 - src/unrealsdk/unreal/classes/uconst.h | 1 - src/unrealsdk/unreal/classes/ufield.h | 1 - src/unrealsdk/unreal/classes/ufunction.h | 1 - src/unrealsdk/unreal/classes/uobject.h | 3 +-- src/unrealsdk/unreal/classes/uproperty.h | 3 +-- src/unrealsdk/unreal/classes/uscriptstruct.h | 1 - src/unrealsdk/unreal/classes/ustruct.h | 1 - src/unrealsdk/unreal/offsets.h | 2 ++ 11 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/unrealsdk/unreal/classes/properties/attribute_property.h b/src/unrealsdk/unreal/classes/properties/attribute_property.h index 0e8e86f..29c7f42 100644 --- a/src/unrealsdk/unreal/classes/properties/attribute_property.h +++ b/src/unrealsdk/unreal/classes/properties/attribute_property.h @@ -42,7 +42,6 @@ class GenericAttributeProperty : public T { class_name& operator=(const class_name&) = delete; \ class_name& operator=(class_name&&) = delete; \ ~class_name() = delete; \ - /* NOLINTNEXTLINE(readability-identifier-naming) */ \ UNREALSDK_DEFINE_FIELDS_HEADER(class_name, fields); \ }; \ template <> \ diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h index 90180b3..0ecd6c6 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h @@ -40,7 +40,6 @@ class UByteProperty : public CopyableProperty { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UBYTEPROPERTY_FIELDS(X) X(UEnum*, Enum) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UByteProperty, UNREALSDK_UBYTEPROPERTY_FIELDS); private: diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index f356b2a..dfebd71 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -34,7 +34,6 @@ class UClass : public UStruct { X(UObject*, ClassDefaultObject) \ X(TArray, Interfaces) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UClass, UNREALSDK_UCLASS_FIELDS); // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) diff --git a/src/unrealsdk/unreal/classes/uconst.h b/src/unrealsdk/unreal/classes/uconst.h index a52a7f8..cbc6d97 100644 --- a/src/unrealsdk/unreal/classes/uconst.h +++ b/src/unrealsdk/unreal/classes/uconst.h @@ -31,7 +31,6 @@ class UConst : public UField { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UCONST_FIELDS(X) X(UnmanagedFString, Value) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UConst, UNREALSDK_UCONST_FIELDS); private: diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index c15b18b..1550e79 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -39,7 +39,6 @@ class UField : public UObject { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_UFIELD_FIELDS(X) X(UField*, Next) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UField, UNREALSDK_UFIELD_FIELDS); private: diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index 93cc4dc..24c6206 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -35,7 +35,6 @@ class UFunction : public UStruct { X(uint16_t, ParamsSize) \ X(uint16_t, ReturnValueOffset) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UFunction, UNREALSDK_UFUNCTION_FIELDS); // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index b48289f..84717fa 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -30,8 +30,6 @@ class UObject { uintptr_t* vftable; - // NOLINTBEGIN(readability-identifier-naming) - #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using object_flags_type = uint32_t; #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW @@ -51,6 +49,7 @@ class UObject { UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECT_FIELDS); + // NOLINTBEGIN(readability-identifier-naming) #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK private: int32_t ObjectFlags_member; diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index acbbbda..5a78da0 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -52,8 +52,6 @@ class UProperty : public UField { UProperty& operator=(UProperty&&) = delete; ~UProperty() = delete; - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) - #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK using property_flags_type = uint64_t; #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW @@ -73,6 +71,7 @@ class UProperty : public UField { UNREALSDK_DEFINE_FIELDS_HEADER(UProperty, UNREALSDK_UPROPERTY_FIELDS); + // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK private: int32_t ArrayDim_member; diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index be816d5..2a2893d 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -38,7 +38,6 @@ class UScriptStruct : public UStruct { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UNREALSDK_USCRIPTSTRUCT_FIELDS(X) X(uint32_t, StructFlags) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UScriptStruct, UNREALSDK_USCRIPTSTRUCT_FIELDS); private: diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index eb4b883..6a66ec9 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -37,7 +37,6 @@ class UStruct : public UField { X(UField*, Children) \ X(UProperty*, PropertyLink) - // NOLINTNEXTLINE(readability-identifier-naming) UNREALSDK_DEFINE_FIELDS_HEADER(UStruct, UNREALSDK_USTRUCT_FIELDS); private: diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h index 3fa9f27..b51f827 100644 --- a/src/unrealsdk/unreal/offsets.h +++ b/src/unrealsdk/unreal/offsets.h @@ -50,8 +50,10 @@ using copy_cv = std::conditional_t< #define UNREALSDK_DEFINE_FIELDS_HEADER(ClassName, X_MACRO) \ public: \ struct Offsets; \ + /* NOLINTNEXTLINE(readability-identifier-naming) */ \ X_MACRO(UNREALSDK_OFFSETS__DEFINE_GETTER) \ struct Offsets { \ + /* NOLINTNEXTLINE(readability-identifier-naming) */ \ X_MACRO(UNREALSDK_OFFSETS__DEFINE_OFFSET_MEMBERS) \ template \ static constexpr Offsets from() { \ From 5745e31534525ad2b797075d37eba781786dd411 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 13 May 2025 22:16:06 +1200 Subject: [PATCH 67/81] support the object-like properties --- src/unrealsdk/game/bl1/offsets.h | 9 +++++ src/unrealsdk/game/bl2/console.cpp | 4 +-- src/unrealsdk/game/bl2/offsets.h | 9 +++++ src/unrealsdk/game/bl3/offsets.h | 9 +++++ src/unrealsdk/game/tps/offsets.h | 5 +++ .../persistent_object_ptr_property.cpp | 14 ++++---- .../persistent_object_ptr_property.h | 31 +++++++++++------ .../classes/properties/uclassproperty.cpp | 11 +++--- .../classes/properties/uclassproperty.h | 34 ++++++++++++------- .../classes/properties/uinterfaceproperty.cpp | 11 +++--- .../classes/properties/uinterfaceproperty.h | 30 ++++++++++------ .../classes/properties/uobjectproperty.cpp | 9 ++--- .../classes/properties/uobjectproperty.h | 30 ++++++++++------ .../properties/uweakobjectproperty.cpp | 2 +- src/unrealsdk/unreal/offset_list.h | 10 +++++- 15 files changed, 151 insertions(+), 67 deletions(-) diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 5bacf31..05a68d6 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -4,7 +4,11 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" @@ -141,6 +145,11 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index afc8df6..a15b402 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -113,7 +113,7 @@ bool say_crash_fix_hook(hook_manager::Details& hook) { static const auto spark_interface_prop = engine->Class()->find_prop_and_validate(L"SparkInterface"_fn); static const auto is_spark_enabled_func = - spark_interface_prop->get_interface_class()->find_func_and_validate(L"IsSparkEnabled"_fn); + spark_interface_prop->InterfaceClass()->find_func_and_validate(L"IsSparkEnabled"_fn); // Check if we're online, if so allow normal processing if (BoundFunction{.func = is_spark_enabled_func, @@ -136,7 +136,7 @@ bool say_crash_fix_hook(hook_manager::Details& hook) { static const auto pri_prop = hook.args->type->find_prop_and_validate(L"PRI"_fn); static const auto player_name_prop = - pri_prop->get_property_class()->find_prop_and_validate(L"PlayerName"_fn); + pri_prop->PropertyClass()->find_prop_and_validate(L"PlayerName"_fn); auto player_name = hook.args->get(pri_prop)->get(player_name_prop); diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index c2ef55f..d530494 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -3,7 +3,11 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -183,6 +187,11 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 4daf797..1b7f407 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -3,7 +3,11 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -152,6 +156,11 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index b65a9bc..e5f72bd 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -98,6 +98,11 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp index 49d031a..329824e 100644 --- a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp +++ b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.cpp @@ -2,6 +2,8 @@ #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fstring.h" #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/unreal/structs/tarray_funcs.h" @@ -12,9 +14,7 @@ namespace unrealsdk::unreal { -UClass* USoftClassProperty::get_meta_class(void) const { - return this->read_field(&USoftClassProperty::MetaClass); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(USoftClassProperty, UNREALSDK_USOFTCLASSPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const ULazyObjectProperty* /*prop*/, @@ -44,7 +44,7 @@ void PropTraits::set(const ULazyObjectProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } @@ -57,7 +57,7 @@ void PropTraits::set(const USoftObjectProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } @@ -70,11 +70,11 @@ void PropTraits::set(const USoftClassProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } - auto meta_cls = prop->get_meta_class(); + auto meta_cls = prop->MetaClass(); if (!value->inherits(meta_cls)) { throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name()); diff --git a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h index b122874..9a6d86d 100644 --- a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h +++ b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h @@ -5,18 +5,30 @@ #include "unrealsdk/unreal/class_traits.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" namespace unrealsdk::unreal { +#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW +#pragma pack(push, 0x4) +#endif + struct FLazyObjectPath; struct FSoftObjectPath; class UObject; -#if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW -#pragma pack(push, 0x4) -#endif +namespace offsets::generic { + +template +class USoftClassProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UClass* MetaClass; +}; + +} // namespace offsets::generic class ULazyObjectProperty : public UObjectProperty { public: @@ -49,16 +61,15 @@ class USoftClassProperty : public USoftObjectProperty { USoftClassProperty& operator=(USoftClassProperty&&) = delete; ~USoftClassProperty() = delete; - /** - * @brief Get the meta class of this property, which values must be a subclass of. - * - * @return This property's meta class. - */ - [[nodiscard]] UClass* get_meta_class(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_USOFTCLASSPROPERTY_FIELDS(X) X(UClass*, MetaClass) + + UNREALSDK_DEFINE_FIELDS_HEADER(USoftClassProperty, UNREALSDK_USOFTCLASSPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UClass* MetaClass; + UClass* MetaClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp index 57084cb..ec8bcde 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.cpp @@ -2,13 +2,14 @@ #include "unrealsdk/unreal/classes/properties/uclassproperty.h" #include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -UClass* UClassProperty::get_meta_class(void) const { - return this->read_field(&UClassProperty::MetaClass); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UClassProperty, UNREALSDK_UCLASSPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UClassProperty* /*prop*/, @@ -22,11 +23,11 @@ void PropTraits::set(const UClassProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } - auto meta_cls = prop->get_meta_class(); + auto meta_cls = prop->MetaClass(); if (!value->inherits(meta_cls)) { throw std::runtime_error("Class does not inherit from " + (std::string)meta_cls->Name()); diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.h b/src/unrealsdk/unreal/classes/properties/uclassproperty.h index 5ac0b01..5bba14d 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.h @@ -1,5 +1,5 @@ -#ifndef UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H -#define UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H +#ifndef PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H +#define PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H #include "unrealsdk/pch.h" @@ -10,12 +10,23 @@ namespace unrealsdk::unreal { -class UClass; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UClass; + +namespace offsets::generic { + +template +class UClassProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UClass* MetaClass; +}; + +} // namespace offsets::generic + class UClassProperty : public UObjectProperty { public: UClassProperty() = delete; @@ -25,16 +36,15 @@ class UClassProperty : public UObjectProperty { UClassProperty& operator=(UClassProperty&&) = delete; ~UClassProperty() = delete; - /** - * @brief Get the meta class of this property, which values must be a subclass of. - * - * @return This property's meta class. - */ - [[nodiscard]] UClass* get_meta_class(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UCLASSPROPERTY_FIELDS(X) X(UClass*, MetaClass) + + UNREALSDK_DEFINE_FIELDS_HEADER(UClassProperty, UNREALSDK_UCLASSPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UClass* MetaClass; + UClass* MetaClass_member; }; template <> @@ -56,4 +66,4 @@ struct ClassTraits { } // namespace unrealsdk::unreal -#endif /* UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H */ +#endif /* PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_UCLASSPROPERTY_H */ diff --git a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp index 1ede371..104b67f 100644 --- a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.cpp @@ -3,21 +3,22 @@ #include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fimplementedinterface.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UInterfaceProperty, UNREALSDK_UINTERFACEPROPERTY_FIELDS); + struct FScriptInterface { UObject* obj; // A pointer to a UObject that implements a native interface. void* iface_ptr; // Pointer to the location of the interface object within the UObject // referenced by ObjectPointer. }; -UClass* UInterfaceProperty::get_interface_class(void) const { - return this->read_field(&UInterfaceProperty::InterfaceClass); -} - PropTraits::Value PropTraits::get( const UInterfaceProperty* /*prop*/, uintptr_t addr, @@ -28,7 +29,7 @@ PropTraits::Value PropTraits::get( void PropTraits::set(const UInterfaceProperty* prop, uintptr_t addr, const Value& value) { - auto prop_iface = prop->get_interface_class(); + auto prop_iface = prop->InterfaceClass(); size_t pointer_offset = 0; diff --git a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h index e52abaf..16ffdaf 100644 --- a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h @@ -10,13 +10,24 @@ namespace unrealsdk::unreal { -class UClass; -class UObject; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UClass; +class UObject; + +namespace offsets::generic { + +template +class UInterfaceProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UClass* InterfaceClass; +}; + +} // namespace offsets::generic + class UInterfaceProperty : public UProperty { public: UInterfaceProperty() = delete; @@ -26,16 +37,15 @@ class UInterfaceProperty : public UProperty { UInterfaceProperty& operator=(UInterfaceProperty&&) = delete; ~UInterfaceProperty() = delete; - /** - * @brief Get the interface class of this property, which values must be an implementation of. - * - * @return This property's interface class. - */ - [[nodiscard]] UClass* get_interface_class(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UINTERFACEPROPERTY_FIELDS(X) X(UClass*, InterfaceClass) + + UNREALSDK_DEFINE_FIELDS_HEADER(UInterfaceProperty, UNREALSDK_UINTERFACEPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UClass* InterfaceClass; + UClass* InterfaceClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp b/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp index 68b8d92..67f1ea4 100644 --- a/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uobjectproperty.cpp @@ -2,13 +2,14 @@ #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -UClass* UObjectProperty::get_property_class(void) const { - return this->read_field(&UObjectProperty::PropertyClass); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UObjectProperty, UNREALSDK_UOBJECTPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UObjectProperty* /*prop*/, @@ -22,7 +23,7 @@ void PropTraits::set(const UObjectProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } diff --git a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h index 62df7d1..e6001c5 100644 --- a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h @@ -10,13 +10,24 @@ namespace unrealsdk::unreal { -class UClass; -class UObject; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UClass; +class UObject; + +namespace offsets::generic { + +template +class UObjectProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UClass* PropertyClass; +}; + +} // namespace offsets::generic + class UObjectProperty : public UProperty { public: UObjectProperty() = delete; @@ -26,16 +37,15 @@ class UObjectProperty : public UProperty { UObjectProperty& operator=(UObjectProperty&&) = delete; ~UObjectProperty() = delete; - /** - * @brief Get the class of this property, which values must be an instance of. - * - * @return This property's class. - */ - [[nodiscard]] UClass* get_property_class(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UOBJECTPROPERTY_FIELDS(X) X(UClass*, PropertyClass) + + UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECTPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UClass* PropertyClass; + UClass* PropertyClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp index eb83789..54e5bc1 100644 --- a/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uweakobjectproperty.cpp @@ -22,7 +22,7 @@ void PropTraits::set(const UWeakObjectProperty* prop, const Value& value) { // Ensure the object is of a valid class if (value != nullptr) { - auto prop_cls = prop->get_property_class(); + auto prop_cls = prop->PropertyClass(); if (!value->is_instance(prop_cls)) { throw std::runtime_error("Object is not instance of " + (std::string)prop_cls->Name()); } diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 6239fb7..2e27012 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -3,7 +3,11 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" +#include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -34,7 +38,11 @@ namespace unrealsdk::unreal::offsets { X(UByteProperty) \ X(UByteAttributeProperty) \ X(UFloatAttributeProperty) \ - X(UIntAttributeProperty) + X(UIntAttributeProperty) \ + X(UObjectProperty) \ + X(UClassProperty) \ + X(UInterfaceProperty) \ + X(USoftClassProperty) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ From 38a5191cc7c98a58ab123cc13884ef8e61baf74c Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 14 May 2025 20:55:02 +1200 Subject: [PATCH 68/81] support remaining properties --- src/unrealsdk/game/bl1/offsets.h | 26 ++++++----- src/unrealsdk/game/bl2/offsets.h | 32 ++++++++----- src/unrealsdk/game/bl3/offsets.h | 35 ++++++++++----- src/unrealsdk/game/tps/offsets.h | 22 +++++---- .../classes/properties/uarrayproperty.cpp | 13 +++--- .../classes/properties/uarrayproperty.h | 25 ++++++++--- .../classes/properties/uboolproperty.cpp | 11 ++--- .../unreal/classes/properties/uboolproperty.h | 25 +++++++---- .../classes/properties/udelegateproperty.cpp | 9 ++-- .../classes/properties/udelegateproperty.h | 27 +++++++---- .../classes/properties/uenumproperty.cpp | 15 +++---- .../unreal/classes/properties/uenumproperty.h | 45 +++++++++++-------- .../properties/umulticastdelegateproperty.cpp | 14 +++--- .../properties/umulticastdelegateproperty.h | 28 ++++++++---- .../classes/properties/ustructproperty.cpp | 13 +++--- .../classes/properties/ustructproperty.h | 34 +++++++++----- src/unrealsdk/unreal/offset_list.h | 21 +++++++-- 17 files changed, 243 insertions(+), 152 deletions(-) diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 05a68d6..648e532 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -5,10 +5,15 @@ #include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" +#include "unrealsdk/unreal/classes/properties/uarrayproperty.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/udelegateproperty.h" +#include "unrealsdk/unreal/classes/properties/uenumproperty.h" #include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustructproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" #include "unrealsdk/unreal/offsets.h" @@ -129,24 +134,21 @@ using UFunction = bl2::generic::UFunction; using UConst = unreal::offsets::generic::UConst; using UEnum = bl2::generic::UEnum; -using UInt8Property = UProperty; -using UInt16Property = UProperty; -using UIntProperty = UProperty; -using UInt64Property = UProperty; +using UArrayProperty = unreal::offsets::generic::UArrayProperty; +using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; -using UUInt16Property = UProperty; -using UUInt32Property = UProperty; -using UUInt64Property = UProperty; +using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; +using UEnumProperty = unreal::offsets::generic::UEnumProperty; using UFloatProperty = UProperty; -using UDoubleProperty = UProperty; -using UNameProperty = UProperty; +using UIntProperty = UProperty; +using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UStructProperty = unreal::offsets::generic::UStructProperty; using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; - -using UObjectProperty = unreal::offsets::generic::UObjectProperty; -using UClassProperty = unreal::offsets::generic::UClassProperty; using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index d530494..77b4ef1 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -4,10 +4,15 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" +#include "unrealsdk/unreal/classes/properties/uarrayproperty.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/udelegateproperty.h" +#include "unrealsdk/unreal/classes/properties/uenumproperty.h" #include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustructproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -102,6 +107,12 @@ class UEnum : public T { unreal::TArray Names; }; +template +class UBoolProperty : public T { + public: + uint32_t FieldMask; +}; + } // namespace generic using UObject = bl2::generic::UObject; @@ -171,24 +182,21 @@ using UFunction = bl2::generic::UFunction; using UConst = unreal::offsets::generic::UConst; using UEnum = bl2::generic::UEnum; -using UInt8Property = UProperty; -using UInt16Property = UProperty; -using UIntProperty = UProperty; -using UInt64Property = UProperty; +using UArrayProperty = unreal::offsets::generic::UArrayProperty; +using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; -using UUInt16Property = UProperty; -using UUInt32Property = UProperty; -using UUInt64Property = UProperty; +using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; +using UEnumProperty = unreal::offsets::generic::UEnumProperty; using UFloatProperty = UProperty; -using UDoubleProperty = UProperty; -using UNameProperty = UProperty; +using UIntProperty = UProperty; +using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UStructProperty = unreal::offsets::generic::UStructProperty; using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; - -using UObjectProperty = unreal::offsets::generic::UObjectProperty; -using UClassProperty = unreal::offsets::generic::UClassProperty; using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 1b7f407..7231c3b 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -4,10 +4,15 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" +#include "unrealsdk/unreal/classes/properties/uarrayproperty.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/udelegateproperty.h" +#include "unrealsdk/unreal/classes/properties/uenumproperty.h" #include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustructproperty.h" #include "unrealsdk/unreal/classes/uconst.h" #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" @@ -140,27 +145,33 @@ class UEnum : public UField { int64_t CppForm; }; -using UInt8Property = UProperty; -using UInt16Property = UProperty; -using UIntProperty = UProperty; -using UInt64Property = UProperty; +using UArrayProperty = unreal::offsets::generic::UArrayProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; -using UUInt16Property = UProperty; -using UUInt32Property = UProperty; -using UUInt64Property = UProperty; +using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; +using UEnumProperty = unreal::offsets::generic::UEnumProperty; using UFloatProperty = UProperty; -using UDoubleProperty = UProperty; -using UNameProperty = UProperty; +using UIntProperty = UProperty; +using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UStructProperty = unreal::offsets::generic::UStructProperty; using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; - -using UObjectProperty = unreal::offsets::generic::UObjectProperty; -using UClassProperty = unreal::offsets::generic::UClassProperty; using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; +class UBoolProperty : public UProperty { + private: + uint8_t FieldSize; + uint8_t ByteOffset; + uint8_t ByteMask; + + public: + uint8_t FieldMask; +}; + // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, // readability-magic-numbers) diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index e5f72bd..e7641ca 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -82,26 +82,24 @@ using UFunction = bl2::generic::UFunction; using UConst = bl2::UConst; using UEnum = bl2::UEnum; -using UInt8Property = UProperty; -using UInt16Property = UProperty; -using UIntProperty = UProperty; -using UInt64Property = UProperty; +using UArrayProperty = unreal::offsets::generic::UArrayProperty; +using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; -using UUInt16Property = UProperty; -using UUInt32Property = UProperty; -using UUInt64Property = UProperty; +using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; +using UEnumProperty = unreal::offsets::generic::UEnumProperty; using UFloatProperty = UProperty; -using UDoubleProperty = UProperty; -using UNameProperty = UProperty; +using UIntProperty = UProperty; +using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; +using UObjectProperty = unreal::offsets::generic::UObjectProperty; +using UStructProperty = unreal::offsets::generic::UStructProperty; using UByteAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; +using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; - -using UObjectProperty = unreal::offsets::generic::UObjectProperty; -using UClassProperty = unreal::offsets::generic::UClassProperty; using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; +; // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp index 9c44d23..263cfe4 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.cpp @@ -2,24 +2,25 @@ #include "unrealsdk/unreal/cast.h" #include "unrealsdk/unreal/classes/properties/uarrayproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/unreal/structs/tarray_funcs.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unreal/wrappers/unreal_pointer_funcs.h" #include "unrealsdk/unreal/wrappers/wrapped_array.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -UProperty* UArrayProperty::get_inner(void) const { - return this->read_field(&UArrayProperty::Inner); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UArrayProperty, UNREALSDK_UARRAYPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UArrayProperty* prop, uintptr_t addr, const UnrealPointer& parent) { - auto inner = prop->get_inner(); + auto inner = prop->Inner(); if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); @@ -31,7 +32,7 @@ PropTraits::Value PropTraits::get( void PropTraits::set(const UArrayProperty* prop, uintptr_t addr, const Value& value) { - auto inner = prop->get_inner(); + auto inner = prop->Inner(); if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); @@ -62,7 +63,7 @@ void PropTraits::set(const UArrayProperty* prop, } void PropTraits::destroy(const UArrayProperty* prop, uintptr_t addr) { - auto inner = prop->get_inner(); + auto inner = prop->Inner(); if (prop->ArrayDim() > 1) { throw std::runtime_error( "Array has static array inner property - unsure how to handle, aborting!"); diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h index 680ff0f..1672c05 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h @@ -5,6 +5,7 @@ #include "unrealsdk/unreal/class_traits.h" #include "unrealsdk/unreal/classes/uproperty.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unreal/wrappers/wrapped_array.h" @@ -15,6 +16,17 @@ namespace unrealsdk::unreal { #pragma pack(push, 0x4) #endif +namespace offsets::generic { + +template +class UArrayProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UProperty* Inner; +}; + +} // namespace offsets::generic + class UArrayProperty : public UProperty { public: UArrayProperty() = delete; @@ -24,16 +36,15 @@ class UArrayProperty : public UProperty { UArrayProperty& operator=(UArrayProperty&&) = delete; ~UArrayProperty() = delete; - /** - * @brief Get the inner property, which the array entries are instances of. - * - * @return This property's inner property. - */ - [[nodiscard]] UProperty* get_inner(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UARRAYPROPERTY_FIELDS(X) X(UProperty*, Inner) + + UNREALSDK_DEFINE_FIELDS_HEADER(UArrayProperty, UNREALSDK_UARRAYPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UProperty* Inner; + UProperty* Inner_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uboolproperty.cpp b/src/unrealsdk/unreal/classes/properties/uboolproperty.cpp index d8a4ed1..1ce95a5 100644 --- a/src/unrealsdk/unreal/classes/properties/uboolproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uboolproperty.cpp @@ -1,26 +1,27 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/uboolproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -decltype(UBoolProperty::FieldMask) UBoolProperty::get_field_mask(void) const { - return this->read_field(&UBoolProperty::FieldMask); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UBoolProperty, UNREALSDK_UBOOLPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UBoolProperty* prop, uintptr_t addr, const UnrealPointer& /*parent*/) { - auto mask = prop->get_field_mask(); + auto mask = prop->FieldMask(); auto* bitfield = reinterpret_cast(addr); return (*bitfield & mask) != 0; } void PropTraits::set(const UBoolProperty* prop, uintptr_t addr, const Value& value) { - auto mask = prop->get_field_mask(); + auto mask = prop->FieldMask(); auto* bitfield = reinterpret_cast(addr); if (value) { diff --git a/src/unrealsdk/unreal/classes/properties/uboolproperty.h b/src/unrealsdk/unreal/classes/properties/uboolproperty.h index 67ed3a9..61a642a 100644 --- a/src/unrealsdk/unreal/classes/properties/uboolproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uboolproperty.h @@ -28,6 +28,20 @@ class UBoolProperty : public UProperty { UBoolProperty& operator=(UBoolProperty&&) = delete; ~UBoolProperty() = delete; +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK + using field_mask_type = uint8_t; +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + using field_mask_type = uint32_t; +#else +#error Unknown SDK flavour +#endif + + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UBOOLPROPERTY_FIELDS(X) X(field_mask_type, FieldMask) + + UNREALSDK_DEFINE_FIELDS_HEADER(UBoolProperty, UNREALSDK_UBOOLPROPERTY_FIELDS); + private: // NOLINTBEGIN(readability-identifier-naming) @@ -35,21 +49,14 @@ class UBoolProperty : public UProperty { uint8_t FieldSize; uint8_t ByteOffset; uint8_t ByteMask; - uint8_t FieldMask; + uint8_t FieldMask_member; #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - uint32_t FieldMask; + uint32_t FieldMask_member; #else #error Unknown SDK flavour #endif // NOLINTEND(readability-identifier-naming) - public: - /** - * @brief Get the bool field mask of this property. - * - * @return The field mask. - */ - [[nodiscard]] decltype(FieldMask) get_field_mask(void) const; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/udelegateproperty.cpp b/src/unrealsdk/unreal/classes/properties/udelegateproperty.cpp index 8719387..97513cd 100644 --- a/src/unrealsdk/unreal/classes/properties/udelegateproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/udelegateproperty.cpp @@ -1,14 +1,15 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/udelegateproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/structs/fscriptdelegate.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -UFunction* UDelegateProperty::get_signature(void) const { - return this->read_field(&UDelegateProperty::Signature); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UDelegateProperty, UNREALSDK_UDELEGATEPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UDelegateProperty* /*prop*/, @@ -20,7 +21,7 @@ PropTraits::Value PropTraits::get( void PropTraits::set(const UDelegateProperty* prop, uintptr_t addr, const Value& value) { - FScriptDelegate::validate_signature(value, prop->get_signature()); + FScriptDelegate::validate_signature(value, prop->Signature()); reinterpret_cast(addr)->bind(value); } diff --git a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h index d645569..7893bb2 100644 --- a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h @@ -17,6 +17,17 @@ namespace unrealsdk::unreal { class UFunction; +namespace offsets::generic { + +template +class UDelegateProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UFunction* Signature; +}; + +} // namespace offsets::generic + class UDelegateProperty : public UProperty { public: UDelegateProperty() = delete; @@ -26,17 +37,15 @@ class UDelegateProperty : public UProperty { UDelegateProperty& operator=(UDelegateProperty&&) = delete; ~UDelegateProperty() = delete; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UDELEGATEPROPERTY_FIELDS(X) X(UFunction*, Signature) + + UNREALSDK_DEFINE_FIELDS_HEADER(UDelegateProperty, UNREALSDK_UDELEGATEPROPERTY_FIELDS); + private: // NOLINTNEXTLINE(readability-identifier-naming) - UFunction* Signature; - - public: - /** - * @brief Get the function holding this delegate's signature. - * - * @return The signature function. - */ - [[nodiscard]] UFunction* get_signature(void) const; + UFunction* Signature_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uenumproperty.cpp b/src/unrealsdk/unreal/classes/properties/uenumproperty.cpp index 19d35e3..0c31183 100644 --- a/src/unrealsdk/unreal/classes/properties/uenumproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/uenumproperty.cpp @@ -3,8 +3,11 @@ #include "unrealsdk/unreal/cast.h" #include "unrealsdk/unreal/classes/properties/copyable_property.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { @@ -23,13 +26,7 @@ using valid_underlying_types = std::tupleread_field(&UEnumProperty::UnderlyingProp); -} - -UEnum* UEnumProperty::get_enum(void) const { - return this->read_field(&UEnumProperty::Enum); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UEnumProperty, UNREALSDK_UENUMPROPERTY_FIELDS); PropTraits::Value PropTraits::get(const UEnumProperty* prop, uintptr_t addr, @@ -37,7 +34,7 @@ PropTraits::Value PropTraits::get(const UEnumPrope PropTraits::Value value{}; cast::with_classes>( - prop->get_underlying_prop(), [addr, parent, &value](const T* underlying) { + prop->UnderlyingProp(), [addr, parent, &value](const T* underlying) { static_assert(std::is_integral_v::Value>); using underlying_limits = std::numeric_limits::Value>; @@ -53,7 +50,7 @@ PropTraits::Value PropTraits::get(const UEnumPrope void PropTraits::set(const UEnumProperty* prop, uintptr_t addr, const Value& value) { cast::with_classes>( - prop->get_underlying_prop(), [addr, value](const T* underlying) { + prop->UnderlyingProp(), [addr, value](const T* underlying) { static_assert(std::is_integral_v::Value>); using underlying_limits = std::numeric_limits::Value>; diff --git a/src/unrealsdk/unreal/classes/properties/uenumproperty.h b/src/unrealsdk/unreal/classes/properties/uenumproperty.h index 30e4acb..e241edd 100644 --- a/src/unrealsdk/unreal/classes/properties/uenumproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uenumproperty.h @@ -10,12 +10,27 @@ namespace unrealsdk::unreal { -class UEnum; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UEnum; + +namespace offsets::generic { + +template +class UEnumProperty : public T { + public: + // NOLINTBEGIN(readability-identifier-naming) + + UProperty* UnderlyingProp; + UEnum* Enum; + + // NOLINTEND(readability-identifier-naming) +}; + +} // namespace offsets::generic + class UEnumProperty : public UProperty { public: UEnumProperty() = delete; @@ -25,27 +40,21 @@ class UEnumProperty : public UProperty { UEnumProperty& operator=(UEnumProperty&&) = delete; ~UEnumProperty() = delete; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UENUMPROPERTY_FIELDS(X) \ + X(UProperty*, UnderlyingProp) \ + X(UEnum*, Enum) + + UNREALSDK_DEFINE_FIELDS_HEADER(UEnumProperty, UNREALSDK_UENUMPROPERTY_FIELDS); + private: // NOLINTBEGIN(readability-identifier-naming) - UProperty* UnderlyingProp; - UEnum* Enum; + UProperty* UnderlyingProp_member; + UEnum* Enum_member; // NOLINTEND(readability-identifier-naming) - public: - /** - * @brief Get the underlying numeric property of this enum property. - * - * @return The underlying property. - */ - [[nodiscard]] UProperty* get_underlying_prop(void) const; - - /** - * @brief Get the enum associated with this property. - * - * @return The enum. - */ - [[nodiscard]] UEnum* get_enum(void) const; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp index e2145ae..995896f 100644 --- a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.cpp @@ -1,31 +1,33 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/structs/fscriptdelegate.h" #include "unrealsdk/unreal/structs/tarray.h" #include "unrealsdk/unreal/structs/tarray_funcs.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unreal/wrappers/wrapped_multicast_delegate.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -UFunction* UMulticastDelegateProperty::get_signature(void) const { - return this->read_field(&UMulticastDelegateProperty::Signature); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UMulticastDelegateProperty, + UNREALSDK_UMULTICASTDELEGATEPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UMulticastDelegateProperty* prop, uintptr_t addr, const UnrealPointer& parent) { - return {prop->get_signature(), reinterpret_cast*>(addr), parent}; + return {prop->Signature(), reinterpret_cast*>(addr), parent}; } void PropTraits::set(const UMulticastDelegateProperty* prop, uintptr_t addr, const Value& value) { - if (value.signature != prop->get_signature()) { + if (value.signature != prop->Signature()) { throw std::runtime_error("Multicast delegate signature doesn't match existing signature of " - + (std::string)prop->get_signature()->Name()); + + (std::string)prop->Signature()->Name()); } auto arr = reinterpret_cast*>(addr); diff --git a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h index 3f811f7..44addb3 100644 --- a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h @@ -18,6 +18,17 @@ namespace unrealsdk::unreal { class UFunction; +namespace offsets::generic { + +template +class UMulticastDelegateProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + UFunction* Signature; +}; + +} // namespace offsets::generic + class UMulticastDelegateProperty : public UProperty { public: UMulticastDelegateProperty() = delete; @@ -27,17 +38,16 @@ class UMulticastDelegateProperty : public UProperty { UMulticastDelegateProperty& operator=(UMulticastDelegateProperty&&) = delete; ~UMulticastDelegateProperty() = delete; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UMULTICASTDELEGATEPROPERTY_FIELDS(X) X(UFunction*, Signature) + + UNREALSDK_DEFINE_FIELDS_HEADER(UMulticastDelegateProperty, + UNREALSDK_UMULTICASTDELEGATEPROPERTY_FIELDS); + private: // NOLINTNEXTLINE(readability-identifier-naming) - UFunction* Signature; - - public: - /** - * @brief Get the function holding this delegate's signature. - * - * @return The signature function. - */ - [[nodiscard]] UFunction* get_signature(void) const; + UFunction* Signature_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp b/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp index 99ed774..32776db 100644 --- a/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/ustructproperty.cpp @@ -2,28 +2,29 @@ #include "unrealsdk/unreal/classes/properties/ustructproperty.h" #include "unrealsdk/unreal/classes/uscriptstruct.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/prop_traits.h" #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unreal/wrappers/wrapped_struct.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { -[[nodiscard]] UScriptStruct* UStructProperty::get_inner_struct(void) const { - return this->read_field(&UStructProperty::Struct); -} +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UStructProperty, UNREALSDK_USTRUCTPROPERTY_FIELDS); PropTraits::Value PropTraits::get( const UStructProperty* prop, uintptr_t addr, const UnrealPointer& parent) { - auto this_struct = prop->get_inner_struct(); + auto this_struct = prop->Struct(); return {this_struct, reinterpret_cast(addr), parent}; } void PropTraits::set(const UStructProperty* prop, uintptr_t addr, const Value& value) { - auto this_struct = prop->get_inner_struct(); + auto this_struct = prop->Struct(); if (value.type != this_struct) { throw std::runtime_error("Struct is not an instance of " + (std::string)this_struct->Name()); @@ -32,7 +33,7 @@ void PropTraits::set(const UStructProperty* prop, } void PropTraits::destroy(const UStructProperty* prop, uintptr_t addr) { - destroy_struct(prop->get_inner_struct(), addr); + destroy_struct(prop->Struct(), addr); } } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/properties/ustructproperty.h b/src/unrealsdk/unreal/classes/properties/ustructproperty.h index 8ff1dfa..4f2ba11 100644 --- a/src/unrealsdk/unreal/classes/properties/ustructproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ustructproperty.h @@ -1,5 +1,5 @@ -#ifndef UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H -#define UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H +#ifndef PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H +#define PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H #include "unrealsdk/pch.h" @@ -11,12 +11,23 @@ namespace unrealsdk::unreal { -class UScriptStruct; - #if defined(_MSC_VER) && UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW #pragma pack(push, 0x4) #endif +class UScriptStruct; + +namespace offsets::generic { + +template +class UStructProperty : public T { + public: + // NOLINTNEXTLINE(readability-identifier-naming) + unreal::UScriptStruct* Struct; +}; + +} // namespace offsets::generic + class UStructProperty : public UProperty { public: UStructProperty() = delete; @@ -26,16 +37,15 @@ class UStructProperty : public UProperty { UStructProperty& operator=(UStructProperty&&) = delete; ~UStructProperty() = delete; - /** - * @brief Get the struct type of this property, which values must be an instance of. - * - * @return This property's struct type. - */ - [[nodiscard]] UScriptStruct* get_inner_struct(void) const; + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_USTRUCTPROPERTY_FIELDS(X) X(UScriptStruct*, Struct) + + UNREALSDK_DEFINE_FIELDS_HEADER(UStructProperty, UNREALSDK_USTRUCTPROPERTY_FIELDS); private: // NOLINTNEXTLINE(readability-identifier-naming) - UScriptStruct* Struct; + UScriptStruct* Struct_member; }; template <> @@ -60,4 +70,4 @@ struct ClassTraits { } // namespace unrealsdk::unreal -#endif /* UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H */ +#endif /* PYUNREALSDK_LIBS_UNREALSDK_SRC_UNREALSDK_UNREAL_CLASSES_PROPERTIES_USTRUCTPROPERTY_H */ diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index 2e27012..ca7c286 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -4,12 +4,19 @@ #include "unrealsdk/pch.h" #include "unrealsdk/unreal/classes/properties/attribute_property.h" #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" +#include "unrealsdk/unreal/classes/properties/uarrayproperty.h" +#include "unrealsdk/unreal/classes/properties/uboolproperty.h" #include "unrealsdk/unreal/classes/properties/ubyteproperty.h" #include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/udelegateproperty.h" +#include "unrealsdk/unreal/classes/properties/uenumproperty.h" #include "unrealsdk/unreal/classes/properties/uinterfaceproperty.h" +#include "unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustructproperty.h" #include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uconst.h" +#include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/ufunction.h" #include "unrealsdk/unreal/classes/uobject.h" @@ -35,14 +42,20 @@ namespace unrealsdk::unreal::offsets { X(UScriptStruct) \ X(UFunction) \ X(UConst) \ - X(UByteProperty) \ + X(UArrayProperty) \ + X(UBoolProperty) \ X(UByteAttributeProperty) \ + X(UByteProperty) \ + X(UClassProperty) \ + X(UDelegateProperty) \ + X(UEnumProperty) \ X(UFloatAttributeProperty) \ X(UIntAttributeProperty) \ - X(UObjectProperty) \ - X(UClassProperty) \ X(UInterfaceProperty) \ - X(USoftClassProperty) + X(UMulticastDelegateProperty) \ + X(UObjectProperty) \ + X(USoftClassProperty) \ + X(UStructProperty) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; #define UNREALSDK_OFFSETS__NESTED_FROM_TYPE(name) \ From f84fb141b5b2e81c898063b764e44ad4ab34707f Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 14 May 2025 21:25:44 +1200 Subject: [PATCH 69/81] remove all traces of the old object layout switching system --- src/unrealsdk/game/bl2/offsets.h | 2 +- src/unrealsdk/game/bl3/offsets.h | 4 +- src/unrealsdk/game/tps/offsets.h | 2 +- .../persistent_object_ptr_property.h | 4 - .../classes/properties/uarrayproperty.h | 4 - .../unreal/classes/properties/uboolproperty.h | 16 ---- .../unreal/classes/properties/ubyteproperty.h | 7 -- .../classes/properties/uclassproperty.h | 4 - .../classes/properties/udelegateproperty.h | 4 - .../unreal/classes/properties/uenumproperty.h | 8 -- .../classes/properties/uinterfaceproperty.h | 4 - .../properties/umulticastdelegateproperty.h | 4 - .../classes/properties/uobjectproperty.h | 4 - .../classes/properties/ustructproperty.h | 4 - src/unrealsdk/unreal/classes/uclass.h | 27 ------ src/unrealsdk/unreal/classes/uconst.h | 4 - src/unrealsdk/unreal/classes/uenum.h | 16 ---- src/unrealsdk/unreal/classes/ufield.h | 4 - src/unrealsdk/unreal/classes/ufunction.h | 31 ------- src/unrealsdk/unreal/classes/uobject.h | 34 ------- src/unrealsdk/unreal/classes/uproperty.cpp | 40 -------- src/unrealsdk/unreal/classes/uproperty.h | 93 ------------------- src/unrealsdk/unreal/classes/uscriptstruct.h | 4 - src/unrealsdk/unreal/classes/ustruct.cpp | 6 +- src/unrealsdk/unreal/classes/ustruct.h | 63 +++---------- supported_settings.toml | 6 +- 26 files changed, 23 insertions(+), 376 deletions(-) diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index 77b4ef1..36ecb37 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -142,9 +142,9 @@ class UStruct : public UField { public: UStruct* SuperField; UField* Children; + uint16_t PropertySize; private: - uint16_t PropertySize; uint8_t UnknownData01[0x1A]; public: diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 7231c3b..ea4c2c4 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -83,12 +83,14 @@ class UProperty : public UField { }; class UStruct : public UField { + friend class unreal::UStruct; + public: UStruct* SuperField; UField* Children; + int32_t PropertySize; private: - int32_t PropertySize; int32_t MinAlignment; unreal::TArray Script; diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index e7641ca..fa9a104 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -49,9 +49,9 @@ class UStruct : public UField { public: UStruct* SuperField; UField* Children; + uint16_t PropertySize; private: - uint16_t PropertySize; uint8_t UnknownData01[0x1A]; public: diff --git a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h index 9a6d86d..541be17 100644 --- a/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h +++ b/src/unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h @@ -66,10 +66,6 @@ class USoftClassProperty : public USoftObjectProperty { #define UNREALSDK_USOFTCLASSPROPERTY_FIELDS(X) X(UClass*, MetaClass) UNREALSDK_DEFINE_FIELDS_HEADER(USoftClassProperty, UNREALSDK_USOFTCLASSPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UClass* MetaClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h index 1672c05..44b4fde 100644 --- a/src/unrealsdk/unreal/classes/properties/uarrayproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uarrayproperty.h @@ -41,10 +41,6 @@ class UArrayProperty : public UProperty { #define UNREALSDK_UARRAYPROPERTY_FIELDS(X) X(UProperty*, Inner) UNREALSDK_DEFINE_FIELDS_HEADER(UArrayProperty, UNREALSDK_UARRAYPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UProperty* Inner_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uboolproperty.h b/src/unrealsdk/unreal/classes/properties/uboolproperty.h index 61a642a..8d8a39a 100644 --- a/src/unrealsdk/unreal/classes/properties/uboolproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uboolproperty.h @@ -41,22 +41,6 @@ class UBoolProperty : public UProperty { #define UNREALSDK_UBOOLPROPERTY_FIELDS(X) X(field_mask_type, FieldMask) UNREALSDK_DEFINE_FIELDS_HEADER(UBoolProperty, UNREALSDK_UBOOLPROPERTY_FIELDS); - - private: - // NOLINTBEGIN(readability-identifier-naming) - -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - uint8_t FieldSize; - uint8_t ByteOffset; - uint8_t ByteMask; - uint8_t FieldMask_member; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - uint32_t FieldMask_member; -#else -#error Unknown SDK flavour -#endif - - // NOLINTEND(readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h index 0ecd6c6..b18e923 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.h @@ -41,13 +41,6 @@ class UByteProperty : public CopyableProperty { #define UNREALSDK_UBYTEPROPERTY_FIELDS(X) X(UEnum*, Enum) UNREALSDK_DEFINE_FIELDS_HEADER(UByteProperty, UNREALSDK_UBYTEPROPERTY_FIELDS); - - private: - // NOLINTBEGIN(readability-identifier-naming) - - UEnum* Enum_member; - - // NOLINTEND(readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uclassproperty.h b/src/unrealsdk/unreal/classes/properties/uclassproperty.h index 5bba14d..c1dcc78 100644 --- a/src/unrealsdk/unreal/classes/properties/uclassproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uclassproperty.h @@ -41,10 +41,6 @@ class UClassProperty : public UObjectProperty { #define UNREALSDK_UCLASSPROPERTY_FIELDS(X) X(UClass*, MetaClass) UNREALSDK_DEFINE_FIELDS_HEADER(UClassProperty, UNREALSDK_UCLASSPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UClass* MetaClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h index 7893bb2..eb46806 100644 --- a/src/unrealsdk/unreal/classes/properties/udelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/udelegateproperty.h @@ -42,10 +42,6 @@ class UDelegateProperty : public UProperty { #define UNREALSDK_UDELEGATEPROPERTY_FIELDS(X) X(UFunction*, Signature) UNREALSDK_DEFINE_FIELDS_HEADER(UDelegateProperty, UNREALSDK_UDELEGATEPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UFunction* Signature_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uenumproperty.h b/src/unrealsdk/unreal/classes/properties/uenumproperty.h index e241edd..a96757a 100644 --- a/src/unrealsdk/unreal/classes/properties/uenumproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uenumproperty.h @@ -47,14 +47,6 @@ class UEnumProperty : public UProperty { X(UEnum*, Enum) UNREALSDK_DEFINE_FIELDS_HEADER(UEnumProperty, UNREALSDK_UENUMPROPERTY_FIELDS); - - private: - // NOLINTBEGIN(readability-identifier-naming) - - UProperty* UnderlyingProp_member; - UEnum* Enum_member; - - // NOLINTEND(readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h index 16ffdaf..ece87c3 100644 --- a/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uinterfaceproperty.h @@ -42,10 +42,6 @@ class UInterfaceProperty : public UProperty { #define UNREALSDK_UINTERFACEPROPERTY_FIELDS(X) X(UClass*, InterfaceClass) UNREALSDK_DEFINE_FIELDS_HEADER(UInterfaceProperty, UNREALSDK_UINTERFACEPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UClass* InterfaceClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h index 44addb3..485bd3a 100644 --- a/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h +++ b/src/unrealsdk/unreal/classes/properties/umulticastdelegateproperty.h @@ -44,10 +44,6 @@ class UMulticastDelegateProperty : public UProperty { UNREALSDK_DEFINE_FIELDS_HEADER(UMulticastDelegateProperty, UNREALSDK_UMULTICASTDELEGATEPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UFunction* Signature_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h index e6001c5..3157486 100644 --- a/src/unrealsdk/unreal/classes/properties/uobjectproperty.h +++ b/src/unrealsdk/unreal/classes/properties/uobjectproperty.h @@ -42,10 +42,6 @@ class UObjectProperty : public UProperty { #define UNREALSDK_UOBJECTPROPERTY_FIELDS(X) X(UClass*, PropertyClass) UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECTPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UClass* PropertyClass_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/properties/ustructproperty.h b/src/unrealsdk/unreal/classes/properties/ustructproperty.h index 4f2ba11..ef8cd16 100644 --- a/src/unrealsdk/unreal/classes/properties/ustructproperty.h +++ b/src/unrealsdk/unreal/classes/properties/ustructproperty.h @@ -42,10 +42,6 @@ class UStructProperty : public UProperty { #define UNREALSDK_USTRUCTPROPERTY_FIELDS(X) X(UScriptStruct*, Struct) UNREALSDK_DEFINE_FIELDS_HEADER(UStructProperty, UNREALSDK_USTRUCTPROPERTY_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UScriptStruct* Struct_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/uclass.h b/src/unrealsdk/unreal/classes/uclass.h index dfebd71..194243c 100644 --- a/src/unrealsdk/unreal/classes/uclass.h +++ b/src/unrealsdk/unreal/classes/uclass.h @@ -36,33 +36,6 @@ class UClass : public UStruct { UNREALSDK_DEFINE_FIELDS_HEADER(UClass, UNREALSDK_UCLASS_FIELDS); - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) - - private: -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - uint8_t UnknownData00[0x70]; - UObject* ClassDefaultObject_internal; - uint8_t UnknownData01[0xA0]; - TArray Interfaces_internal; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - // Misc Fields I found within this block in BL2, but which we don't care about enough for me to - // find in UE4, or to want to increase the compile times by including - - // 0xE8: TArray ClassReps; - // 0xF4: TArray NetFields; - // 0x100: TArray HideCategories; - // 0x10C: TArray AutoExpandCategories; - - uint8_t UnknownData00[0xCC]; - UObject* ClassDefaultObject_internal; - uint8_t UnknownData01[0x48]; - TArray Interfaces_internal; - -#else -#error Unknown SDK flavour -#endif - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) - public: /** * @brief Checks if this class implements an interface. * diff --git a/src/unrealsdk/unreal/classes/uconst.h b/src/unrealsdk/unreal/classes/uconst.h index cbc6d97..f64b285 100644 --- a/src/unrealsdk/unreal/classes/uconst.h +++ b/src/unrealsdk/unreal/classes/uconst.h @@ -32,10 +32,6 @@ class UConst : public UField { #define UNREALSDK_UCONST_FIELDS(X) X(UnmanagedFString, Value) UNREALSDK_DEFINE_FIELDS_HEADER(UConst, UNREALSDK_UCONST_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UnmanagedFString Value_internal; }; template <> diff --git a/src/unrealsdk/unreal/classes/uenum.h b/src/unrealsdk/unreal/classes/uenum.h index 81012e1..153040b 100644 --- a/src/unrealsdk/unreal/classes/uenum.h +++ b/src/unrealsdk/unreal/classes/uenum.h @@ -29,22 +29,6 @@ class UEnum : public UField { UEnum& operator=(UEnum&&) = delete; ~UEnum() = delete; - private: - // NOLINTBEGIN(readability-identifier-naming) - -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - UnmanagedFString CppType; - TArray> Names; - int64_t CppForm; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - TArray Names; -#else -#error Unknown SDK flavour -#endif - - // NOLINTEND(readability-identifier-naming) - - public: /** * @brief Get the values of an enum which have assigned names. * diff --git a/src/unrealsdk/unreal/classes/ufield.h b/src/unrealsdk/unreal/classes/ufield.h index 1550e79..9229555 100644 --- a/src/unrealsdk/unreal/classes/ufield.h +++ b/src/unrealsdk/unreal/classes/ufield.h @@ -40,10 +40,6 @@ class UField : public UObject { #define UNREALSDK_UFIELD_FIELDS(X) X(UField*, Next) UNREALSDK_DEFINE_FIELDS_HEADER(UField, UNREALSDK_UFIELD_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - UField* Next_member; }; template <> diff --git a/src/unrealsdk/unreal/classes/ufunction.h b/src/unrealsdk/unreal/classes/ufunction.h index 24c6206..3f7bb7a 100644 --- a/src/unrealsdk/unreal/classes/ufunction.h +++ b/src/unrealsdk/unreal/classes/ufunction.h @@ -37,43 +37,12 @@ class UFunction : public UStruct { UNREALSDK_DEFINE_FIELDS_HEADER(UFunction, UNREALSDK_UFUNCTION_FIELDS); - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) - - private: -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - uint32_t FunctionFlags_internal; - uint8_t NumParams_internal; - uint16_t ParamsSize_internal; - uint16_t ReturnValueOffset_internal; - uint16_t RPCId; - uint16_t RPCResponseId; - UProperty* FirstPropertyToInit; - UFunction* EventGraphFunction; - int32_t EventGraphCallOffset; - void* Func; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - uint32_t FunctionFlags_internal; - uint16_t iNative; - uint16_t RepOffset; - FName FriendlyName; - uint8_t OperPrecedence; - uint8_t NumParams_internal; - uint16_t ParamsSize_internal; - uint16_t ReturnValueOffset_internal; - uint8_t UnknownData00[0x6]; - void* Func; -#else -#error Unknown SDK flavour -#endif - public: /** * @brief Finds the return param for this function (if it exists). * * @return The return param, or `nullptr` if none exists. */ [[nodiscard]] UProperty* find_return_param(void) const; - - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index 84717fa..0c29f64 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -49,40 +49,6 @@ class UObject { UNREALSDK_DEFINE_FIELDS_HEADER(UObject, UNREALSDK_UOBJECT_FIELDS); - // NOLINTBEGIN(readability-identifier-naming) -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - private: - int32_t ObjectFlags_member; - int32_t InternalIndex_member; - UClass* Class_member; - FName Name_member; - UObject* Outer_member; - - public: -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - - private: - // TODO: remove - void* HashNext; - uint64_t ObjectFlags_member; - void* HashOuterNext; - void* StateFrame; - class UObject* _Linker; - void* _LinkerIndex; - int InternalIndex_member; - int NetIndex; - UObject* Outer_member; - FName Name_member; - UClass* Class_member; - UObject* ObjectArchetype; - - public: -#else -#error Unknown SDK flavour -#endif - - // NOLINTEND(readability-identifier-naming) - /** * @brief Calls a virtual function on this object. * diff --git a/src/unrealsdk/unreal/classes/uproperty.cpp b/src/unrealsdk/unreal/classes/uproperty.cpp index 64e115e..938b429 100644 --- a/src/unrealsdk/unreal/classes/uproperty.cpp +++ b/src/unrealsdk/unreal/classes/uproperty.cpp @@ -13,44 +13,4 @@ namespace unrealsdk::unreal { UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UProperty, UNREALSDK_UPROPERTY_FIELDS); -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - -size_t UProperty::class_size(void) { - static size_t size = 0; - if (size != 0) { - return size; - } - - auto config_size = config::get_int("unrealsdk.uproperty_size"); - if (config_size.has_value()) { - size = (size_t)*config_size; - return size; - } - - // Rather than bother with a findobject call, we can recover UProperty from any arbitrary object - // UObject always has properties on it, we don't need to worry about what class we get - auto obj = *unrealsdk::gobjects().begin(); - auto prop = obj->Class()->PropertyLink(); - - const UStruct* cls = nullptr; - for (auto superfield : prop->Class()->superfields()) { - if (superfield->Name() == L"Property"_fn) { - cls = superfield; - break; - } - } - - // If we couldn't find the class, default to our actual size - if (cls == nullptr) { - size = sizeof(UProperty); - LOG(WARNING, "Couldn't find UProperty class size, defaulting to: {:#x}", size); - } else { - size = cls->get_struct_size(); - LOG(MISC, "UProperty class size: {:#x}", size); - } - return size; -} - -#endif - } // namespace unrealsdk::unreal diff --git a/src/unrealsdk/unreal/classes/uproperty.h b/src/unrealsdk/unreal/classes/uproperty.h index 5a78da0..6fb303b 100644 --- a/src/unrealsdk/unreal/classes/uproperty.h +++ b/src/unrealsdk/unreal/classes/uproperty.h @@ -18,24 +18,6 @@ namespace unrealsdk::unreal { #pragma clang diagnostic ignored "-Wunused-private-field" #endif -/* -There is a massive issue with `UProperty`. - -We've observed that it's 0x80 bytes big in BL2, but it's only 0x74 in TPS -These are both 32-bit UE3 games, so we want to support them both from the same dll. - -The extra 0xC bytes are placed right at the end, which makes accesses within `UProperty` itself - simple enough, especially since we don't care about whatever fields they are. -The issue is subclasses. We very much do care about fields on subclasses, which will be placed - after the variable offset. -Luckily, we can read the size off of `Core.Property` at runtime, and do some maths to work out the - actual offsets of the fields we want, using the helper function `read_field`. - -This means: -- All accesses to UProperty subclasses' fields **MUST** be done through `read_field`. -- All accesses to base UProperty fields **MUST** be direct, and **MUST NOT** use `read_field` -*/ - class UProperty : public UField { public: #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW @@ -70,81 +52,6 @@ class UProperty : public UField { X(UProperty*, PropertyLinkNext) UNREALSDK_DEFINE_FIELDS_HEADER(UProperty, UNREALSDK_UPROPERTY_FIELDS); - - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - private: - int32_t ArrayDim_member; - int32_t ElementSize_member; - uint64_t PropertyFlags_member; - - uint16_t RepIndex; - uint8_t BlueprintReplicationCondition; - - int32_t Offset_Internal_member; - - FName RepNotifyFunc; - - /** In memory only: Linked list of properties from most-derived to base **/ - UProperty* PropertyLinkNext_member; - - /** In memory only: Linked list of object reference properties from most-derived to base **/ - UProperty* NextRef; - /** In memory only: Linked list of properties requiring destruction. Note this does not include - * things that will be destroyed by the native destructor **/ - UProperty* DestructorLinkNext; - /** In memory only: Linked list of properties requiring post constructor initialization.**/ - UProperty* PostConstructLinkNext; // 0x0030(0x0040) MISSED OFFSET -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - private: - int32_t ArrayDim_member; - int32_t ElementSize_member; - uint32_t PropertyFlags_member; - - uint8_t UnknownData00[0x14]; - - int32_t Offset_Internal_member; - UProperty* PropertyLinkNext_member; - - uint8_t UnknownData01[0x18]; - - /** - * @brief Gets the size of this class. - * - * @return The size of this class. - */ - [[nodiscard]] static size_t class_size(void); -#else -#error Unknown SDK flavour -#endif - - public: - /** - * @brief Reads a field on a UProperty subclass, taking into account it's variable length. - * - * @tparam PropertyType The subclass of UProperty to read the field off of (should be picked up - * automatically). - * @tparam FieldType The type of the field being read (should be picked up automatically). - * @param prop The property to read the field off of. - * @param field Pointer to member of the field to read. - * @return The field's value. - */ - template >> - FieldType read_field(FieldType PropertyType::* field) const { -#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - return reinterpret_cast(this)->*field; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - return *reinterpret_cast( - reinterpret_cast(&(reinterpret_cast(this)->*field)) - - sizeof(UProperty) + UProperty::class_size()); -#else -#error Unknown SDK flavour -#endif - } - - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) }; template <> diff --git a/src/unrealsdk/unreal/classes/uscriptstruct.h b/src/unrealsdk/unreal/classes/uscriptstruct.h index 2a2893d..0b7ca29 100644 --- a/src/unrealsdk/unreal/classes/uscriptstruct.h +++ b/src/unrealsdk/unreal/classes/uscriptstruct.h @@ -39,10 +39,6 @@ class UScriptStruct : public UStruct { #define UNREALSDK_USCRIPTSTRUCT_FIELDS(X) X(uint32_t, StructFlags) UNREALSDK_DEFINE_FIELDS_HEADER(UScriptStruct, UNREALSDK_USCRIPTSTRUCT_FIELDS); - - private: - // NOLINTNEXTLINE(readability-identifier-naming) - uint32_t StructFlags_internal; }; template <> diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 63a6eab..615a421 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -134,9 +134,11 @@ utils::IteratorProxy UStruct::superfields(void) con size_t UStruct::get_struct_size(void) const { #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - return (this->PropertySize + this->MinAlignment - 1) & ~(this->MinAlignment - 1); + auto bl3_struct = reinterpret_cast(this); + return (bl3_struct->PropertySize + bl3_struct->MinAlignment - 1) + & ~(bl3_struct->MinAlignment - 1); #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - return this->PropertySize; + return this->PropertySize(); #else #error Unknown SDK flavour #endif diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index 6a66ec9..ded5569 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -30,63 +30,24 @@ class UStruct : public UField { UStruct& operator=(UStruct&&) = delete; ~UStruct() = delete; - // These fields become member functions, returning a reference into the object. -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define UNREALSDK_USTRUCT_FIELDS(X) \ - X(UStruct*, SuperField) \ - X(UField*, Children) \ - X(UProperty*, PropertyLink) - - UNREALSDK_DEFINE_FIELDS_HEADER(UStruct, UNREALSDK_USTRUCT_FIELDS); - - private: - // NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - /* Struct this inherits from, may be null */ - UStruct* SuperField_member; - /* Pointer to start of linked list of child fields */ - UField* Children_member; - - /* Total size of all UProperties, the allocated structure may be larger due to alignment */ - int32_t PropertySize; - /* Alignment of structure in memory, structure will be at least this large */ - int32_t MinAlignment; - /* Script bytecode associated with this object */ - TArray Script; - - /* In memory only: Linked list of properties from most-derived to base */ - UProperty* PropertyLink_member; - - /* In memory only: Linked list of object reference properties from most-derived to base */ - UProperty* RefLink; - /* In memory only: Linked list of properties requiring destruction. Note this does not include - * things that will be destroyed by the native destructor */ - UProperty* DestructorLink; - /** In memory only: Linked list of properties requiring post constructor initialization */ - UProperty* PostConstructLink; - /* Array of object references embedded in script code. Mirrored for easy access by realtime - * garbage collection code */ - TArray ScriptObjectReferences; + using property_size_type = uint16_t; #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW - uint8_t UnknownData00[0x8]; - - UStruct* SuperField_member; - UField* Children_member; - - uint16_t PropertySize; - uint8_t UnknownData01[0x1A]; - - UProperty* PropertyLink_member; - - uint8_t UnknownData02[0x10]; - - TArray ScriptObjectReferences; + using property_size_type = int32_t; #else #error Unknown SDK flavour #endif - // NOLINTEND(readability-magic-numbers, readability-identifier-naming) - public: + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_USTRUCT_FIELDS(X) \ + X(UStruct*, SuperField) \ + X(UField*, Children) \ + X(property_size_type, PropertySize) \ + X(UProperty*, PropertyLink) + + UNREALSDK_DEFINE_FIELDS_HEADER(UStruct, UNREALSDK_USTRUCT_FIELDS); + #pragma region Iterators struct FieldIterator { using iterator_category = std::forward_iterator_tag; diff --git a/supported_settings.toml b/supported_settings.toml index e1c3c34..2178122 100644 --- a/supported_settings.toml +++ b/supported_settings.toml @@ -5,8 +5,8 @@ # crashes if used. # You are not expected to provide all settings, you should only set those you need. Some settings -# will change behaviour simply by being defined. For example, if `uproperty_size` is set, the sdk -# will not attempt to auto detect it, and will instead assume the provided value is accurate. +# will change behaviour simply by being defined. For example, if `alloc_alignment` is set, the sdk +# will not attempt to dynamically adjust it, and will always use the provided value. # Not all settings are used under all build configurations - for example UE3 doesn't support FText, # so `ftext_get_display_string_vf_index` is of course unused. @@ -24,8 +24,6 @@ console_log_level = "INFO" # If set, overrides the executable name used for game detection in the shared module. exe_override = "" -# Changes the size the `UProperty` class is assumed to have. -uproperty_size = -1 # Changes the alignment used when calling the unreal memory allocation functions. alloc_alignment = -1 From 9c079c8374ee1adb51e8f71b1c9548bd52ec2e4f Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 14 May 2025 23:32:12 +1200 Subject: [PATCH 70/81] crash fixes missed this one use of the arch define, not having thiscall breaks the stack guard accidentally swapped the two property size types --- src/unrealsdk/unreal/classes/uobject.h | 16 ++++++++-------- src/unrealsdk/unreal/classes/ustruct.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/unrealsdk/unreal/classes/uobject.h b/src/unrealsdk/unreal/classes/uobject.h index 0c29f64..af706b2 100644 --- a/src/unrealsdk/unreal/classes/uobject.h +++ b/src/unrealsdk/unreal/classes/uobject.h @@ -60,21 +60,21 @@ class UObject { */ template R call_virtual_function(size_t index, Args... args) const { -#ifdef ARCH_X86 + if constexpr (sizeof(void*) == sizeof(uint32_t)) { #if defined(__MINGW32__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" // thiscall on non-class #endif - // NOLINTNEXTLINE(modernize-use-using) - need a typedef for the __thiscall - typedef R(__thiscall * func_ptr)(const UObject*, Args...); + // NOLINTNEXTLINE(modernize-use-using) - need a typedef for the __thiscall + typedef R(__thiscall * func_ptr)(const UObject*, Args...); #if defined(__MINGW32__) #pragma GCC diagnostic pop #endif -#else - using func_ptr = R (*)(const UObject*, Args...); -#endif - - return reinterpret_cast(this->vftable[index])(this, args...); + return reinterpret_cast(this->vftable[index])(this, args...); + } else { + using func_ptr = R (*)(const UObject*, Args...); + return reinterpret_cast(this->vftable[index])(this, args...); + } } /** diff --git a/src/unrealsdk/unreal/classes/ustruct.h b/src/unrealsdk/unreal/classes/ustruct.h index ded5569..4c6545d 100644 --- a/src/unrealsdk/unreal/classes/ustruct.h +++ b/src/unrealsdk/unreal/classes/ustruct.h @@ -31,9 +31,9 @@ class UStruct : public UField { ~UStruct() = delete; #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK - using property_size_type = uint16_t; -#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW using property_size_type = int32_t; +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + using property_size_type = uint16_t; #else #error Unknown SDK flavour #endif From 4b36dab1e876927222555dd06810883d1a19caf8 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 14 May 2025 23:32:40 +1200 Subject: [PATCH 71/81] linting fixes --- src/unrealsdk/game/bl3/console.cpp | 2 +- src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp | 1 - src/unrealsdk/unreal/classes/ustruct.cpp | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index 661e6e2..772553b 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -167,7 +167,7 @@ bool inject_console_hook(hook_manager::Details& hook) { console = viewport->get(console_property); if (console == nullptr) { - auto default_console = console_property->get_property_class()->ClassDefaultObject(); + auto default_console = console_property->PropertyClass()->ClassDefaultObject(); console = unrealsdk::construct_object(default_console->Class(), default_console->Outer()); viewport->set(L"ViewportConsole"_fn, console); } diff --git a/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp b/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp index 9593376..716f7ea 100644 --- a/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp +++ b/src/unrealsdk/unreal/classes/properties/ubyteproperty.cpp @@ -4,7 +4,6 @@ #include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unrealsdk.h" - namespace unrealsdk::unreal { UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UByteProperty, UNREALSDK_UBYTEPROPERTY_FIELDS); diff --git a/src/unrealsdk/unreal/classes/ustruct.cpp b/src/unrealsdk/unreal/classes/ustruct.cpp index 615a421..e7d336d 100644 --- a/src/unrealsdk/unreal/classes/ustruct.cpp +++ b/src/unrealsdk/unreal/classes/ustruct.cpp @@ -1,6 +1,7 @@ #include "unrealsdk/pch.h" #include "unrealsdk/config.h" +#include "unrealsdk/game/bl3/offsets.h" #include "unrealsdk/unreal/class_name.h" #include "unrealsdk/unreal/classes/ufield.h" #include "unrealsdk/unreal/classes/ufunction.h" From d10e4e1d2eb19d873e9a8c442f81ed5541ea8d49 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 14 May 2025 23:32:55 +1200 Subject: [PATCH 72/81] expose get exe range mopie requested this, why not --- src/unrealsdk/memory.cpp | 13 ++----------- src/unrealsdk/memory.h | 7 +++++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/unrealsdk/memory.cpp b/src/unrealsdk/memory.cpp index 45f031b..b294877 100644 --- a/src/unrealsdk/memory.cpp +++ b/src/unrealsdk/memory.cpp @@ -4,15 +4,8 @@ namespace unrealsdk::memory { -namespace { - -/** - * @brief Gets the address range covered by the exe's module. - * - * @return A tuple of the exe start address and it's length. - */ -std::tuple get_exe_range(void) { - static std::optional> range = std::nullopt; +std::pair get_exe_range(void) { + static std::optional> range = std::nullopt; if (range) { return *range; } @@ -38,8 +31,6 @@ std::tuple get_exe_range(void) { return *range; } -} // namespace - uintptr_t sigscan(const uint8_t* bytes, const uint8_t* mask, size_t pattern_size) { auto [start, size] = get_exe_range(); return sigscan(bytes, mask, pattern_size, start, size); diff --git a/src/unrealsdk/memory.h b/src/unrealsdk/memory.h index 2dbac55..0eb26bd 100644 --- a/src/unrealsdk/memory.h +++ b/src/unrealsdk/memory.h @@ -241,6 +241,13 @@ struct Pattern { } }; +/** + * @brief Gets the address range covered by the exe's module. + * + * @return A tuple of the exe start address and it's length. + */ +std::pair get_exe_range(void); + } // namespace unrealsdk::memory #endif /* UNREALSDK_MEMORY_H */ From 485bc1555f7091a5be15c3fa3b34ea8ac09bf6cb Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 17 May 2025 14:29:20 +1200 Subject: [PATCH 73/81] fix `UInterfaceProperty` inheriting incrorrectly, add debuging type casting helpers --- src/unrealsdk/game/bl1/offsets.h | 6 +- src/unrealsdk/game/bl2/offsets.h | 6 +- src/unrealsdk/game/bl3/offsets.h | 6 +- src/unrealsdk/game/tps/offsets.h | 13 ++--- src/unrealsdk/unreal/debug_casters.cpp | 77 ++++++++++++++++++++++++++ src/unrealsdk/unreal/offset_list.h | 3 + 6 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 src/unrealsdk/unreal/debug_casters.cpp diff --git a/src/unrealsdk/game/bl1/offsets.h b/src/unrealsdk/game/bl1/offsets.h index 648e532..85f4579 100644 --- a/src/unrealsdk/game/bl1/offsets.h +++ b/src/unrealsdk/game/bl1/offsets.h @@ -139,8 +139,9 @@ using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; using UEnumProperty = unreal::offsets::generic::UEnumProperty; -using UFloatProperty = UProperty; -using UIntProperty = UProperty; +class UFloatProperty : public UProperty {}; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +class UIntProperty : public UProperty {}; using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; using UObjectProperty = unreal::offsets::generic::UObjectProperty; using UStructProperty = unreal::offsets::generic::UStructProperty; @@ -149,7 +150,6 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; -using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; // NOLINTEND(cppcoreguidelines-pro-type-member-init, diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index 36ecb37..02dca1b 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -187,8 +187,9 @@ using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; using UEnumProperty = unreal::offsets::generic::UEnumProperty; -using UFloatProperty = UProperty; -using UIntProperty = UProperty; +class UFloatProperty : public UProperty {}; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +class UIntProperty : public UProperty {}; using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; using UObjectProperty = unreal::offsets::generic::UObjectProperty; using UStructProperty = unreal::offsets::generic::UStructProperty; @@ -197,7 +198,6 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; -using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; // NOLINTEND(cppcoreguidelines-pro-type-member-init, diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index ea4c2c4..8ad024c 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -151,8 +151,9 @@ using UArrayProperty = unreal::offsets::generic::UArrayProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; using UEnumProperty = unreal::offsets::generic::UEnumProperty; -using UFloatProperty = UProperty; -using UIntProperty = UProperty; +class UFloatProperty : public UProperty {}; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +class UIntProperty : public UProperty {}; using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; using UObjectProperty = unreal::offsets::generic::UObjectProperty; using UStructProperty = unreal::offsets::generic::UStructProperty; @@ -161,7 +162,6 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; -using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; class UBoolProperty : public UProperty { diff --git a/src/unrealsdk/game/tps/offsets.h b/src/unrealsdk/game/tps/offsets.h index fa9a104..8e38102 100644 --- a/src/unrealsdk/game/tps/offsets.h +++ b/src/unrealsdk/game/tps/offsets.h @@ -23,7 +23,7 @@ namespace unrealsdk::game::tps { class UClass; using UObject = bl2::generic::UObject; -using UField = bl2::UField; +class UField : public bl2::UField {}; class UProperty : public UField { public: @@ -79,16 +79,17 @@ class UClass : public UStruct { using UScriptStruct = unreal::offsets::generic::UScriptStruct; using UFunction = bl2::generic::UFunction; -using UConst = bl2::UConst; -using UEnum = bl2::UEnum; +class UConst : public bl2::UConst {}; +class UEnum : public bl2::UEnum {}; using UArrayProperty = unreal::offsets::generic::UArrayProperty; using UBoolProperty = bl2::generic::UBoolProperty; using UByteProperty = unreal::offsets::generic::UByteProperty; using UDelegateProperty = unreal::offsets::generic::UDelegateProperty; using UEnumProperty = unreal::offsets::generic::UEnumProperty; -using UFloatProperty = UProperty; -using UIntProperty = UProperty; +class UFloatProperty : public UProperty {}; +using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; +class UIntProperty : public UProperty {}; using UMulticastDelegateProperty = unreal::offsets::generic::UMulticastDelegateProperty; using UObjectProperty = unreal::offsets::generic::UObjectProperty; using UStructProperty = unreal::offsets::generic::UStructProperty; @@ -97,9 +98,7 @@ using UByteAttributeProperty = unreal::offsets::generic::GenericAttributePropert using UClassProperty = unreal::offsets::generic::UClassProperty; using UFloatAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; using UIntAttributeProperty = unreal::offsets::generic::GenericAttributeProperty; -using UInterfaceProperty = unreal::offsets::generic::UInterfaceProperty; using USoftClassProperty = unreal::offsets::generic::USoftClassProperty; -; // NOLINTEND(cppcoreguidelines-pro-type-member-init, // readability-identifier-naming, diff --git a/src/unrealsdk/unreal/debug_casters.cpp b/src/unrealsdk/unreal/debug_casters.cpp new file mode 100644 index 0000000..429b704 --- /dev/null +++ b/src/unrealsdk/unreal/debug_casters.cpp @@ -0,0 +1,77 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl1/offsets.h" +#include "unrealsdk/game/bl2/offsets.h" +#include "unrealsdk/game/bl3/offsets.h" +#include "unrealsdk/game/tps/offsets.h" +#include "unrealsdk/unreal/offset_list.h" + + +#ifndef NDEBUG +/* +For debug builds, we define a bunch of helper functions you may call to transform the "generic" +unreal types into "concrete" ones. These are intended for use only in a debugger. + +These creates a few series of global functions, named after each supported game, and "ue_generic()". +These functions have overloads for every dynamic unreal type, they take a pointer to one and return +it cast to their concret, game specific type (or to the generic one for "ue_generic()"). These +functions all also support being run as a no-op, so you don't need to double check if you have a +generic or concrete type. +*/ + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + +#define DEBUG_CASTER_GAMES(X, ...) \ + X(bl1, __VA_ARGS__) \ + X(bl2, __VA_ARGS__) \ + X(tps, __VA_ARGS__) + +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK + +#define DEBUG_CASTER_GAMES(X, ...) X(bl3, __VA_ARGS__) + +#else +#error Unknown sdk flavour +#endif + +#define DEFINE_DEBUG_CASTERS_FOR_GAME_AND_TYPE(game_name, type_name) \ + unrealsdk::game::game_name::type_name* game_name(unrealsdk::unreal::type_name* obj) { \ + return reinterpret_cast(obj); \ + } \ + const unrealsdk::game::game_name::type_name* game_name( \ + const unrealsdk::unreal::type_name* obj) { \ + return reinterpret_cast(obj); \ + } \ + unrealsdk::game::game_name::type_name* game_name(unrealsdk::game::game_name::type_name* obj) { \ + return obj; \ + } \ + const unrealsdk::game::game_name::type_name* game_name( \ + const unrealsdk::game::game_name::type_name* obj) { \ + return obj; \ + } \ + unrealsdk::unreal::type_name* ue_generic(unrealsdk::game::game_name::type_name* obj) { \ + return reinterpret_cast(obj); \ + } \ + const unrealsdk::unreal::type_name* ue_generic( \ + const unrealsdk::game::game_name::type_name* obj) { \ + return reinterpret_cast(obj); \ + } + +#define DEFINE_DEBUG_GENERIC_NOOP_CASTER(type_name) \ + unrealsdk::unreal::type_name* ue_generic(unrealsdk::unreal::type_name* obj) { \ + return obj; \ + } \ + const unrealsdk::unreal::type_name* ue_generic(const unrealsdk::unreal::type_name* obj) { \ + return obj; \ + } + +#define DEFINE_DEBUG_CASTERS_FOR_TYPE(type_name) \ + DEBUG_CASTER_GAMES(DEFINE_DEBUG_CASTERS_FOR_GAME_AND_TYPE, type_name) + +// NOLINTBEGIN(misc-use-internal-linkage) +UNREALSDK__DYNAMIC_OFFSET_TYPES(DEFINE_DEBUG_CASTERS_FOR_TYPE) +UNREALSDK__DYNAMIC_OFFSET_TYPES(DEFINE_DEBUG_GENERIC_NOOP_CASTER) +// NOLINTEND(misc-use-internal-linkage) + +// NOLINTEND(cppcoreguidelines-macro-usage) +#endif diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index ca7c286..ed6fd17 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -2,6 +2,9 @@ #define UNREALSDK_UNREAL_OFFSET_LIST_H #include "unrealsdk/pch.h" + +// Note: this header needs to pull in almost all unreal classes, and their offset definitions +// Refrain from including it in other headers #include "unrealsdk/unreal/classes/properties/attribute_property.h" #include "unrealsdk/unreal/classes/properties/persistent_object_ptr_property.h" #include "unrealsdk/unreal/classes/properties/uarrayproperty.h" From ed058211f16de5ef7727a3dee22f8f9aaefa523c Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 17 May 2025 15:10:00 +1200 Subject: [PATCH 74/81] replace deducing this with macro-generated overloads, lower to c++20 since this is all using macros anyway, deducing this doesn't save us much using it makes debugging a whole lot more awkward, since it means the dynamic fields are no longer normal functions, you have to explictly give your specialization --- CMakeLists.txt | 2 +- README.md | 2 +- src/unrealsdk/unreal/offsets.h | 36 ++++++++++++++++++++-------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a63b5a..176679e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(UNREALSDK_SHARED False CACHE BOOL "If set, compiles as a shared library inst add_library(_unrealsdk_interface INTERFACE) -target_compile_features(_unrealsdk_interface INTERFACE cxx_std_23) +target_compile_features(_unrealsdk_interface INTERFACE cxx_std_20) set_target_properties(_unrealsdk_interface PROPERTIES COMPILE_WARNING_AS_ERROR True INTERPROCEDURAL_OPTIMIZATION True diff --git a/README.md b/README.md index f550f1e..228859a 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ auto op_string = hook.obj->get(L"BuildOverpowerPromptS So there are a few extra steps to integrate the sdk into your project before you can start using hooks like above. -The SDK requires at least C++23. +The SDK requires at least C++20. The recommended way to link against the sdk is as a submodule. diff --git a/src/unrealsdk/unreal/offsets.h b/src/unrealsdk/unreal/offsets.h index b51f827..f0ba7d3 100644 --- a/src/unrealsdk/unreal/offsets.h +++ b/src/unrealsdk/unreal/offsets.h @@ -7,12 +7,6 @@ namespace unrealsdk::unreal::offsets { using offset_type = uint16_t; -template -using copy_cv = std::conditional_t< - std::is_const_v, - std::add_const_t, std::add_volatile_t, To>>, - /* align */ std::conditional_t, std::add_volatile_t, To>>; - // NOLINTBEGIN(cppcoreguidelines-macro-usage) // Internal macros @@ -23,11 +17,18 @@ using copy_cv = std::conditional_t< < std::numeric_limits::max()); #define UNREALSDK_OFFSETS__OFFSETOF(type, name) \ static_cast(offsetof(T, name)), -#define UNREALSDK_OFFSETS__DEFINE_GETTER(type, name) \ - template \ - [[nodiscard]] unrealsdk::unreal::offsets::copy_cv& name(this T& self) { \ - return *reinterpret_cast*>( \ - reinterpret_cast(&self) + Offsets::get(&Offsets::name)); \ +#define UNREALSDK_OFFSETS__DEFINE_GETTER(type, name) \ + private: \ + using _type__##name = type; \ + \ + public: \ + [[nodiscard]] _type__##name& name(void) { \ + return *reinterpret_cast<_type__##name*>(reinterpret_cast(this) \ + + Offsets::get(&Offsets::name)); \ + } \ + [[nodiscard]] const _type__##name& name(void) const { \ + return *reinterpret_cast(reinterpret_cast(this) \ + + Offsets::get(&Offsets::name)); \ } #if defined(__MINGW32__) || defined(__clang__) @@ -113,10 +114,15 @@ struct Offsets { auto bl2_offsets = Offsets::from(); // The macro also creates a bunch of getters like the following -template -[[nodiscard]] copy_cv& Class(this T& self) { - return *reinterpret_cast*>(reinterpret_cast(&self) - + Offsets::get(&Offsets::Class)); +using _type__Class = UClass*; + +[[nodiscard]] _type__Class& Class(void) { + return *reinterpret_cast<_type__Class*>(reinterpret_cast(&self) + + Offsets::get(&Offsets::Class)); +} +[[nodiscard]] const _type__Class& Class(void) const { + return *reinterpret_cast<_type__Class*>(reinterpret_cast(&self) + + Offsets::get(&Offsets::Class)); } // Since these return a reference, they can be used pretty much the exact same way as a member. From 057eb5242fde6fbd21ac91a9eae87782ddc8380e Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 17 May 2025 22:28:39 +1200 Subject: [PATCH 75/81] update cmake toolchains should no longer need to disable intrinsics --- CMakeLists.txt | 10 ---------- common_cmake | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 176679e..ea96ce1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,16 +50,6 @@ include(common_cmake/git_watcher.cmake) add_subdirectory(libs/minhook) add_subdirectory(libs/tomlplusplus) -# If using the clang windows cross compilation toolchain -if((NOT CMAKE_HOST_WIN32) - AND (CMAKE_SYSTEM_NAME STREQUAL "Windows") - AND (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) - # Disable some intrinsics - MSVC implicitly defines intrinsics, but Clang does not, so we get - # a linking error otherwise - target_compile_definitions(minhook PRIVATE MINHOOK_DISABLE_INTRINSICS) - target_compile_definitions(_unrealsdk_interface INTERFACE TOML_ENABLE_SIMD=0) -endif() - target_include_directories(_unrealsdk_interface INTERFACE "src" ${CONFIGURE_FILES_DIR}) target_link_libraries(_unrealsdk_interface INTERFACE minhook tomlplusplus::tomlplusplus) diff --git a/common_cmake b/common_cmake index 92175e1..0b1c32d 160000 --- a/common_cmake +++ b/common_cmake @@ -1 +1 @@ -Subproject commit 92175e1c74b78bbb6746a589793cf54a9e7c5465 +Subproject commit 0b1c32de1ff98a7851f798d6b092edd1e89e5eba From bc3a5a9857b431e0015ceb6ef258670144f8f152 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 17 May 2025 22:49:22 +1200 Subject: [PATCH 76/81] linting fixes --- CMakePresets.json | 6 ++++-- src/unrealsdk/unreal/debug_casters.cpp | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 46b3f32..718359f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -40,9 +40,10 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, - "environment": { + "cacheVariables": { "MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x86/msvcenv.sh" }, + "generator": "Ninja", "toolchainFile": "common_cmake/clang-cross-x86.cmake" }, { @@ -53,9 +54,10 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, - "environment": { + "cacheVariables": { "MSVC_WINE_ENV_SCRIPT": "/win-sdk/bin/x64/msvcenv.sh" }, + "generator": "Ninja", "toolchainFile": "common_cmake/clang-cross-x64.cmake" }, { diff --git a/src/unrealsdk/unreal/debug_casters.cpp b/src/unrealsdk/unreal/debug_casters.cpp index 429b704..eff9be7 100644 --- a/src/unrealsdk/unreal/debug_casters.cpp +++ b/src/unrealsdk/unreal/debug_casters.cpp @@ -5,7 +5,6 @@ #include "unrealsdk/game/tps/offsets.h" #include "unrealsdk/unreal/offset_list.h" - #ifndef NDEBUG /* For debug builds, we define a bunch of helper functions you may call to transform the "generic" @@ -13,7 +12,7 @@ unreal types into "concrete" ones. These are intended for use only in a debugger These creates a few series of global functions, named after each supported game, and "ue_generic()". These functions have overloads for every dynamic unreal type, they take a pointer to one and return -it cast to their concret, game specific type (or to the generic one for "ue_generic()"). These +it cast to their concrete, game specific type (or to the generic one for "ue_generic()"). These functions all also support being run as a no-op, so you don't need to double check if you have a generic or concrete type. */ From be879cfb5d80fbb760fc60e1bb89251c2aa1ad09 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Wed, 21 May 2025 22:09:37 +1200 Subject: [PATCH 77/81] clean up hook signatures, rework a few --- src/unrealsdk/game/bl1/bl1.cpp | 240 ++++-------------- src/unrealsdk/game/bl1/bl1.h | 5 - src/unrealsdk/game/bl1/console.cpp | 75 +++--- src/unrealsdk/game/bl1/globals.cpp | 31 +-- src/unrealsdk/game/bl1/hexedits.cpp | 122 +++------ src/unrealsdk/game/bl1/hooks.cpp | 52 ++-- src/unrealsdk/game/bl1/memory.cpp | 53 ++-- src/unrealsdk/game/bl1/object.cpp | 110 +++----- ...tobjectptr.cpp => persistentobjectptr.cpp} | 0 9 files changed, 214 insertions(+), 474 deletions(-) rename src/unrealsdk/game/bl1/{presistentobjectptr.cpp => persistentobjectptr.cpp} (100%) diff --git a/src/unrealsdk/game/bl1/bl1.cpp b/src/unrealsdk/game/bl1/bl1.cpp index 22ad845..c5e20dd 100644 --- a/src/unrealsdk/game/bl1/bl1.cpp +++ b/src/unrealsdk/game/bl1/bl1.cpp @@ -13,81 +13,41 @@ using namespace unrealsdk::unreal; namespace unrealsdk::game { -// These could be defined in the class but since they are only used here this will do for now. -namespace { - -std::atomic_bool bl1_has_initialised{false}; - -using Clock = std::chrono::steady_clock; - -bool hook_init_func(void); -} // namespace - void BL1Hook::hook(void) { hook_antidebug(); - if (!hook_init_func()) { - return; - } - hook_process_event(); hook_call_function(); - // Grabbing these asap seems fine + find_gobjects(); + find_gnames(); find_fname_init(); find_fframe_step(); + find_gmalloc(); find_construct_object(); find_get_path_name(); find_static_find_object(); find_load_package(); - // idk if these are required or if they are correct hexedit_set_command(); hexedit_array_limit(); - hexedit_array_limit_message(); - - // This ensures that the unrealscript is initialised when we exit/return - while (!bl1_has_initialised.load(std::memory_order_relaxed)) { - // NOLINTNEXTLINE(readability-magic-numbers) - std::this_thread::sleep_for(std::chrono::milliseconds{50}); - } - - // Delayed until after initialisation to ensure we grab valid data; Should be able to avoid this - // and grab earlier if we grab a pointer to the global not the value held by the global. - find_gobjects(); - find_gnames(); - find_gmalloc(); } void BL1Hook::post_init(void) { - LOG(MISC, "Attaching Hooks!"); inject_console(); } -// ############################################################################// -// | FFRAME STEP | -// ############################################################################// +#pragma region FFrame::Step namespace { -// - NOTE - -// This is inlined so we have to manually re-implement FFrame::Step using GNatives. -const constinit Pattern<36> FFRAME_STEP_SIG{ - "74 ??" // je borderlands.59CEA4 - "8B45 D4" // mov eax,dword ptr ss:[ebp-2C] - "830D ???????? 02" // or dword ptr ds:[1F73BE8],2 - "0FB610" // movzx edx,byte ptr ds:[eax] - "8B1495 {????????}" // mov edx,dword ptr ds:[edx*4+1F97A80] - "57" // push edi - "8D4D BC" // lea ecx,dword ptr ss:[ebp-44] - "40" // inc eax - "51" // push ecx - "8B4D EC" // mov ecx,dword ptr ss:[ebp-14] - "8945 D4" // mov dword ptr ss:[ebp-2C],eax - "FFD2" // call edx +// FFrame::Step is inlined, so instead we manually re-implement it using GNatives. +const constinit Pattern<11> GNATIVES_SIG{ + "8B 14 95 {????????}" // mov edx, [edx*4+01F942C0] + "57" // push edi + "8D 4D ??" // lea ecx, [ebp-44] }; -// RET 0x8; Callee cleans up the stack (8 bytes) // NOLINTNEXTLINE(modernize-use-using) typedef void(__stdcall* fframe_step_func)(FFrame*, void*); fframe_step_func** fframe_step_gnatives; @@ -95,40 +55,44 @@ fframe_step_func** fframe_step_gnatives; } // namespace void BL1Hook::find_fframe_step(void) { - fframe_step_gnatives = FFRAME_STEP_SIG.sigscan_nullable(); - - if (fframe_step_gnatives == nullptr) { - LOG(ERROR, "FFrame::Step(...), GNatives was null."); - return; - } - - LOG(MISC, "FFrame::Step: {:p}", reinterpret_cast(fframe_step_gnatives)); + fframe_step_gnatives = GNATIVES_SIG.sigscan_nullable(); + LOG(MISC, "GNatives: {:p}", reinterpret_cast(fframe_step_gnatives)); } void BL1Hook::fframe_step(unreal::FFrame* frame, unreal::UObject* /*obj*/, void* param) const { ((*fframe_step_gnatives)[*frame->Code++])(frame, param); } -// ############################################################################// -// | FNAME INIT | -// ############################################################################// +#pragma region FName::Init + +namespace { #if defined(__MINGW32__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" // thiscall on non-class #endif -namespace { - -const constinit Pattern<34> FNAME_INIT_SIG{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18EB45B - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "81EC 980C0000" // sub esp,C98 - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C4" // xor eax,esp - "898424 940C0000" // mov dword ptr ss:[esp+C94],eax +const constinit Pattern<72> FNAME_INIT_SIG{ + "6A FF" // push -01 + "68 ????????" // push 018E73CB + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "81 EC ????????" // sub esp, 00000C98 + "A1 ????????" // mov eax, [01F131C0] + "33 C4" // xor eax, esp + "89 84 24 ????????" // mov [esp+00000C94], eax + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax, [01F131C0] + "33 C4" // xor eax, esp + "50" // push eax + "8D 84 24 ????????" // lea eax, [esp+00000CAC] + "64 A3 ????????" // mov fs:[00000000], eax + "8B BC 24 ????????" // mov edi, [esp+00000CBC] + "8B E9" // mov ebp, ecx + "89 6C 24 ??" // mov [esp+1C], ebp }; // NOLINTNEXTLINE(modernize-use-using) @@ -140,6 +104,10 @@ typedef void(__thiscall* fname_init_func)(FName* name, fname_init_func fname_init_ptr = nullptr; +#if defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif + } // namespace void BL1Hook::find_fname_init(void) { @@ -151,139 +119,17 @@ void BL1Hook::fname_init(unreal::FName* name, const wchar_t* str, int32_t number fname_init_ptr(name, str, number, 1, 1); } -#if defined(__MINGW32__) -#pragma GCC diagnostic pop -#endif - -// ############################################################################// -// | CUSTOM HOOKS | -// ############################################################################// - -namespace { - -// - NOTE - -// The init function is only used to delay initialisation of the SDK to ensure that we can proceed -// with injection at the right time. However, the Steams version of this function is different -// the core functionality though is the same. - -const constinit Pattern<64> INIT_FUNC_STEAM_SIG{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.198D998 - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 3C" // sub esp,3C - "53" // push ebx - "55" // push ebp - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F131C0] - "33C4" // xor eax,esp - "50" // push eax - "8D4424 50" // lea eax,dword ptr ss:[esp+50] - "64A3 00000000" // mov dword ptr fs:[0],eax - "8BD9" // mov ebx,ecx - "68 ????????" // push borderlands.1CC3E78 - "E8 ????????" // call borderlands.5C20F0 - "50" // push eax - "E8 ????????" // call borderlands.5C2A80 - "83C4 08" // add esp,8 - "85C0" // test eax,eax - "74 ??" // je borderlands.138F06A -}; - -const constinit Pattern<45> INIT_FUNC_141_UDK_SIG{ - "6A FF" // push FFFFFFFF - "68 ????????" // push - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 3C" // sub esp,3C - "53" // push ebx - "55" // push ebp - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C4" // xor eax,esp - "50" // push eax - "8D4424 50" // lea eax,dword ptr ss:[esp+50] - "64A3 00000000" // mov dword ptr fs:[0],eax - "8BD9" // mov ebx,ecx - "EB ??" // jmp borderlands.13ADAC9 - "4D" // dec ebp - "61" // popad -}; - -// Not sure if this is a __thiscall or an actual __fastcall; ecx is used. -// NOLINTNEXTLINE(modernize-use-using) -typedef void(__fastcall* init_function)(void* ecx, void* edx); - -init_function init_func_ptr = nullptr; - -void __fastcall detour_init_func(void* ecx, void* edx) { - auto start = Clock::now(); - LOG(INFO, "Init function called"); - init_func_ptr(ecx, edx); - LOG(INFO, "Init function took {}s to execute", - std::chrono::duration(Clock::now() - start).count()); - - // When this is true the unrealscript game engine has been created - bl1_has_initialised.store(true, std::memory_order_relaxed); -} - -bool hook_init_func(void) { - // - NOTE - - // I don't think is actually has to be in a loop. - // - constexpr float init_func_timeout_seconds{15.0F}; - - // Note: The time requested is still constrained to the OS; i.e., min on windows is 16ms - // this should just be 'as fast as possible' so 20ms for all systems should suffice. - constexpr std::chrono::milliseconds delay{20}; - - LOG(INFO, "Attempting to hook init function with polling rate: {}ms", delay.count()); - - auto start = Clock::now(); - - while (true) { - std::this_thread::sleep_for(delay); - - // Steam - uintptr_t addr = INIT_FUNC_STEAM_SIG.sigscan_nullable(); - if (addr != 0) { - if (!detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_steam_init_func")) { - continue; - } - return true; - } - - // UDK 141 - addr = INIT_FUNC_141_UDK_SIG.sigscan_nullable(); - if (addr != 0) { - if (!detour(addr, &detour_init_func, &init_func_ptr, "bl1_hook_udk_init_func")) { - continue; - } - return true; - } - - // This should never really be hit adding it just incase though - if (std::chrono::duration(Clock::now() - start).count() - > init_func_timeout_seconds) { - LOG(ERROR, "It has taken too long to hook the init function; Aborting..."); - return false; - } - } -} - -} // namespace +#pragma endregion -// ############################################################################// -// | NOT IMPLEMENTED | -// ############################################################################// +#pragma region FText::AsCultureInvariant void BL1Hook::ftext_as_culture_invariant(unreal::FText* /*text*/, TemporaryFString&& /*str*/) const { throw_version_error("FTexts are not implemented in UE3"); } +#pragma endregion + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl1/bl1.h b/src/unrealsdk/game/bl1/bl1.h index 7f43f0b..3fe259c 100644 --- a/src/unrealsdk/game/bl1/bl1.h +++ b/src/unrealsdk/game/bl1/bl1.h @@ -12,11 +12,6 @@ namespace unrealsdk::game { class BL1Hook : public AbstractHook { protected: - /** - * @brief Hex edits out the `obj dump` array limit message. - */ - virtual void hexedit_array_limit_message(void) const; - /** * @brief Finds `FName::Init`, and sets up such that `fname_init` may be called. */ diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index 97389f3..e0eea26 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -20,16 +20,30 @@ namespace unrealsdk::game { namespace { -BoundFunction console_output_text{}; +// This is an extra hook, which we don't strictly need for the interface, but is really handy. By +// default the game prepends 'say ' to every command as a primitive way to disable console. Bypass +// it so you can actually use it. + +const std::wstring SAY_BYPASS_FUNC = L"Engine.Console:ShippingConsoleCommand"; +const constexpr auto SAY_BYPASS_TYPE = hook_manager::Type::PRE; +const std::wstring SAY_BYPASS_ID = L"unrealsdk_bl1_say_bypass"; + +// We could combine this with the say bypass, but by keeping them separate it'll let users disable +// one if they really want to +const std::wstring CONSOLE_COMMAND_FUNC = L"Engine.Console:ConsoleCommand"; +// This is the actual end point of all console commands, the above function normally calls through +// into this one - but we needed to hook it to be able to manage the console history. If something +/// directly calls `PC.ConsoleCommand("my_cmd")`, we need this hook to be able to catch it. +const std::wstring PC_CONSOLE_COMMAND_FUNC = L"Engine.PlayerController:ConsoleCommand"; + +const constexpr auto CONSOLE_COMMAND_TYPE = hook_manager::Type::PRE; +const std::wstring CONSOLE_COMMAND_ID = L"unrealsdk_bl1_console_command"; -//////////////////////////////////////////////////////////////////////////////// -// | CONSOLE HOOKS | -//////////////////////////////////////////////////////////////////////////////// +const std::wstring INJECT_CONSOLE_FUNC = L"WillowGame.WillowGameViewportClient:PostRender"; +const constexpr auto INJECT_CONSOLE_TYPE = hook_manager::Type::PRE; +const std::wstring INJECT_CONSOLE_ID = L"unrealsdk_bl1_inject_console"; -// - NOTE - -// I don't know if the say_crash_fix_hook is needed; don't know if the implementation/fix -// needs to change either. Just going assume it is not needed until someone complains about crashing -// in multiplayer lol. +BoundFunction console_output_text{}; bool say_bypass_hook(const hook_manager::Details& hook) { static const auto console_command_func = @@ -150,13 +164,7 @@ bool pc_console_command_hook(const hook_manager::Details& hook) { } bool inject_console_hook(const hook_manager::Details& hook) { - // clang-format off - remove_hook( - L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, - L"bl1_inject_console_hook" - ); - // clang-format on + remove_hook(INJECT_CONSOLE_FUNC, INJECT_CONSOLE_TYPE, INJECT_CONSOLE_ID); auto console = hook.obj->get(L"ViewportConsole"_fn); @@ -179,37 +187,14 @@ bool inject_console_hook(const hook_manager::Details& hook) { } // namespace -//////////////////////////////////////////////////////////////////////////////// -// | BL1HOOK ENTRIES | -//////////////////////////////////////////////////////////////////////////////// - void BL1Hook::inject_console(void) { - // clang-format off - add_hook( - L"Engine.Console:ShippingConsoleCommand", - hook_manager::Type::PRE, - L"bl1_say_bypass_hook", - &say_bypass_hook - ); - add_hook( - L"Engine.Console:ConsoleCommand", - hook_manager::Type::PRE, - L"bl1_console_command_hook", - &console_command_hook - ); - add_hook( - L"Engine.PlayerController:ConsoleCommand", - hook_manager::Type::PRE, - L"bl1_pc_console_command_hook", - &pc_console_command_hook - ); - add_hook( - L"WillowGame.WillowGameViewportClient:PostRender", - hook_manager::Type::PRE, - L"bl1_inject_console_hook", - &inject_console_hook - ); - // clang-format on + add_hook(SAY_BYPASS_FUNC, SAY_BYPASS_TYPE, SAY_BYPASS_ID, &say_bypass_hook); + + add_hook(CONSOLE_COMMAND_FUNC, CONSOLE_COMMAND_TYPE, CONSOLE_COMMAND_ID, &console_command_hook); + add_hook(PC_CONSOLE_COMMAND_FUNC, CONSOLE_COMMAND_TYPE, CONSOLE_COMMAND_ID, + &pc_console_command_hook); + + add_hook(INJECT_CONSOLE_FUNC, INJECT_CONSOLE_TYPE, INJECT_CONSOLE_ID, &inject_console_hook); } void BL1Hook::uconsole_output_text(const std::wstring& str) const { diff --git a/src/unrealsdk/game/bl1/globals.cpp b/src/unrealsdk/game/bl1/globals.cpp index 8dca1cd..fd93f9d 100644 --- a/src/unrealsdk/game/bl1/globals.cpp +++ b/src/unrealsdk/game/bl1/globals.cpp @@ -15,17 +15,14 @@ namespace unrealsdk::game { namespace { -// ############################################################################// -// | GOBJECTS | -// ############################################################################// - GObjects gobjects_wrapper{}; -const constinit Pattern<15> GOBJECTS_SIG{ - "8B0D {????????}" // mov ecx,dword ptr ds:[1FB85A8] - "8B04B1" // mov eax,dword ptr ds:[ecx+esi*4] - "8B50 0C" // mov edx,dword ptr ds:[eax+C] - "2158 08" // and dword ptr ds:[eax+8],ebx +const constinit Pattern<18> GOBJECTS_SIG{ + "8B 0D {????????}" // mov ecx, [01FB4DD8] + "8B 04 ??" // mov eax, [ecx+esi*4] + "8B 50 ??" // mov edx, [eax+0C] + "21 58 ??" // and [eax+08], ebx + "89 50 ??" // mov [eax+0C], edx }; } // namespace @@ -41,19 +38,17 @@ const GObjects& BL1Hook::gobjects(void) const { return gobjects_wrapper; }; -// ############################################################################// -// | GNAMES | -// ############################################################################// - namespace { GNames gnames_wrapper{}; -const constinit Pattern<15> GNAMES_SIG{ - "A1 {????????}" // mov eax,dword ptr ds:[1FB8578] - "8B0CB0" // mov ecx,dword ptr ds:[eax+esi*4] - "68 00100000" // push 1000 - "6A 00" // push 0 +const constinit Pattern<21> GNAMES_SIG{ + "A1 {????????}" // mov eax, [01FB4DA8] + "8B 0C ??" // mov ecx, [eax+esi*4] + "68 ????????" // push 00001000 + "6A ??" // push 00 + "E8 ????????" // call 005C21F0 + "5E" // pop esi }; } // namespace diff --git a/src/unrealsdk/game/bl1/hexedits.cpp b/src/unrealsdk/game/bl1/hexedits.cpp index dc806e9..5978bee 100644 --- a/src/unrealsdk/game/bl1/hexedits.cpp +++ b/src/unrealsdk/game/bl1/hexedits.cpp @@ -7,54 +7,40 @@ using namespace unrealsdk::memory; -// - NOTE - -// I genuinely have no idea what these do and if they were required or not lol. They don't seem to -// have any negative impact at runtime so fundamentally they should/might be correct. I was just -// using the BL2 implementation as a checklist and these were trivial to find & patch. -// - namespace unrealsdk::game { -// ############################################################################// -// | SIGNATURES | -// ############################################################################// - namespace { -const constinit Pattern<17> SET_COMMAND_SIG{ - "75 16" // jne borderlands.87E1A7 - "8D4C24 18" // lea ecx,dword ptr ss:[esp+18] - "68 ????????" // push borderlands.1B18900 - "51" // push ecx - "E8 ????????" // call -}; - -const constinit Pattern<20> ARRAY_LIMIT_SIG{ - "6A 64" // push 64 - "50" // push eax - "46" // inc esi - "E8 ????????" // call - "83C4 08" // add esp,8 - "3BF0" // cmp esi,eax - "0F8C 59FFFFFF" // jl borderlands.5E7D33 +const constinit Pattern<32> SET_COMMAND_SIG{ + "85 C0" // test eax, eax + "{?? ??}" // jne 0087EC57 + "8D 4C 24 ??" // lea ecx, [esp+18] + "68 ????????" // push 01B167C0 + "51" // push ecx + "E8 ????????" // call 005C2FD0 + "83 C4 08" // add esp, 08 + "85 C0" // test eax, eax + "74 ??" // je 0087EC80 + "39 9E ????????" // cmp [esi+000003E0], ebx }; -const constinit Pattern<29> ARRAY_LIMIT_MESSAGE{ - "0F8C 7E000000" // jl borderlands.5E7E64 - "8B4C24 38" // mov ecx,dword ptr ss:[esp+38] - "83C7 9D" // add edi,FFFFFF9D - "57" // push edi - "68 ????????" // push borderlands.1A7F42C - "E8 ????????" // call borderlands.51BAA0 - "E9 ????????" // jmp borderlands.5E7E64 +const constinit Pattern<32> ARRAY_LIMIT_SIG{ + "6A 64" // push 64 + "50" // push eax + "46" // inc esi + "{?? ????????}" // call MIN <--- + "83 C4 08" // add esp, 08 + "3B F0" // cmp esi, eax + "0F8C ????????" // jl 005E8F03 + "8B 7F ??" // mov edi, [edi+04] + "83 FF 64" // cmp edi, 64 + "{???? ????????}" // jl DONT_PRINT_MSG <--- }; +const constexpr auto ARRAY_LIMIT_MESSAGE_OFFSET_FROM_MIN = 5 + 3 + 2 + 6 + 3 + 3; +const constexpr auto ARRAY_LIMIT_UNLOCK_SIZE = ARRAY_LIMIT_MESSAGE_OFFSET_FROM_MIN + 2; } // namespace -// ############################################################################// -// | HEX EDITS | -// ############################################################################// - void BL1Hook::hexedit_set_command(void) { auto* set_command_msg = SET_COMMAND_SIG.sigscan_nullable(); @@ -73,57 +59,33 @@ void BL1Hook::hexedit_set_command(void) { } void BL1Hook::hexedit_array_limit(void) { - // - NOTE - - // In BL2 this seems to be inlined however for BL1 its not so we will NOP the CALL and its - // 4 byte address. The caller cleans the stack so need to worry about that. Might also be a - // good idea to move the signature forward to the CALL so we don't need to index weirdly. - // - auto array_limit = ARRAY_LIMIT_SIG.sigscan_nullable(); if (array_limit == nullptr) { LOG(ERROR, "Couldn't find array limit signature"); - } else { - LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); - - // NOLINTBEGIN(readability-magic-numbers) - auto* call_instruction = array_limit + 4; - - // Should *never* be true - if (call_instruction[0] != 0xE8) { - LOG(ERROR, "[ARRAY_LIMIT] ~ Instruction at {:p} + 0x04 is {:02X}", - static_cast(array_limit), *call_instruction); - return; - } - - // Patching out the CALL ???????? to 5 NOP instructions - unlock_range(call_instruction, 5); - call_instruction[0] = 0x90; - call_instruction[1] = 0x90; - call_instruction[2] = 0x90; - call_instruction[3] = 0x90; - call_instruction[4] = 0x90; - // NOLINTEND(readability-magic-numbers) + return; } -} -void BL1Hook::hexedit_array_limit_message(void) const { - auto* array_limit_msg = ARRAY_LIMIT_MESSAGE.sigscan_nullable(); + LOG(MISC, "Array Limit: {:p}", reinterpret_cast(array_limit)); + unlock_range(array_limit, ARRAY_LIMIT_UNLOCK_SIZE); - if (array_limit_msg == nullptr) { - LOG(ERROR, "Failed to find array limit message signature."); - return; - } + // To patch the array limit, we simply NOP out the call to min, so it always uses the full array + // size. Luckily, this means we don't need to do any stack work. + + // NOLINTBEGIN(readability-magic-numbers) + array_limit[0] = 0x90; + array_limit[1] = 0x90; + array_limit[2] = 0x90; + array_limit[3] = 0x90; + array_limit[4] = 0x90; + // NOLINTEND(readability-magic-numbers) - LOG(MISC, "Array Limit Message: {:p}", reinterpret_cast(array_limit_msg)); + // Then for the message, we're patching the JL with a NOP then a JMP w/ 4 byte offset + // Doing it this way means we can reuse the offset from the original JL + auto array_limit_msg = array_limit + ARRAY_LIMIT_MESSAGE_OFFSET_FROM_MIN; // NOLINTBEGIN(readability-magic-numbers) - unlock_range(array_limit_msg, 6); - array_limit_msg[0] = 0xE9; - array_limit_msg[1] = 0x7F; - array_limit_msg[2] = 0x00; - array_limit_msg[3] = 0x00; - array_limit_msg[4] = 0x00; - array_limit_msg[5] = 0x90; + array_limit_msg[0] = 0x90; + array_limit_msg[1] = 0xE9; // NOLINTEND(readability-magic-numbers) } diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 8f2fbfe..5430426 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -16,10 +16,6 @@ using namespace unrealsdk::unreal; namespace unrealsdk::game { -//////////////////////////////////////////////////////////////////////////////// -// | PROCESS EVENT HOOK | -//////////////////////////////////////////////////////////////////////////////// - namespace { // NOLINTNEXTLINE(modernize-use-using) @@ -30,23 +26,17 @@ typedef void(__fastcall* process_event_func)(UObject* obj, void* /*null*/); process_event_func process_event_ptr; -const constinit Pattern<43> PROCESS_EVENT_SIG{ - "55" // push ebp - "8BEC" // mov ebp,esp - "6A FF" // push FFFFFFFF - "68 ????????" // push - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 40" // sub esp,40 - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C5" // xor eax,ebp - "8945 F0" // mov dword ptr ss:[ebp-10],eax - "53" // push ebx - "56" // push esi - "57" // push edi - "50" // push eax - "8D45 F4" // lea eax,dword ptr ss:[ebp-C] - "64A3 00000000" // mov dword ptr fs:[0],eax +const constinit Pattern<30> PROCESS_EVENT_SIG{ + "55" // push ebp + "8B EC" // mov ebp, esp + "6A FF" // push -01 + "68 ????????" // push 018E55E8 + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "83 EC 40" // sub esp, 40 + "A1 ????????" // mov eax, [01F131C0] + "33 C5" // xor eax, ebp + "89 45 ??" // mov [ebp-10], eax }; void __fastcall process_event_hook(UObject* obj, @@ -131,10 +121,6 @@ void BL1Hook::process_event(UObject* object, UFunction* func, void* params) cons locking_process_event_hook(object, nullptr, func, params, nullptr); } -//////////////////////////////////////////////////////////////////////////////// -// | CALL FUNCTION HOOK | -//////////////////////////////////////////////////////////////////////////////// - namespace { // NOLINTNEXTLINE(modernize-use-using) @@ -146,14 +132,14 @@ typedef void(__fastcall* call_function_func)(UObject* obj, call_function_func call_function_ptr; const constinit Pattern<31> CALL_FUNCTION_SIG{ - "55" // push ebp - "8DAC24 FCFBFFFF" // lea ebp,dword ptr ss:[esp-404] - "81EC 04040000" // sub esp,404 - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18E9638 - "64 A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 40" // sub esp,40 + "55" // push ebp + "8D AC 24 ????????" // lea ebp, [esp-00000404] + "81 EC ????????" // sub esp, 00000404 + "6A FF" // push -01 + "68 ????????" // push 018E55B8 + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "83 EC 40" // sub esp, 40 }; void __fastcall call_function_hook(UObject* obj, diff --git a/src/unrealsdk/game/bl1/memory.cpp b/src/unrealsdk/game/bl1/memory.cpp index c63eeba..5eabfad 100644 --- a/src/unrealsdk/game/bl1/memory.cpp +++ b/src/unrealsdk/game/bl1/memory.cpp @@ -33,53 +33,50 @@ struct FMalloc { #pragma GCC diagnostic pop #endif -FMalloc* gmalloc; - -// - NOTE - -// This is *unique* to GMalloc for the 141 UDK however its not completely unique. Its just all -// other usages are also GMalloc. Might be a better idea to use ds:[eax+8] since realloc is a bit -// rarer and used pretty much exclusively by dynamic contiguous containers. -// - -const constinit Pattern<20> GMALLOC_PATTERN{ - "8B0D {????????}" // mov ecx,dword ptr ds:[1F73BB4] - "8B01" // mov eax,dword ptr ds:[ecx] - "8B50 04" // mov edx,dword ptr ds:[eax+4] - "6A 08" // push 8 - "68 E4000000" // push E4 - "FFD2" // call edx +FMalloc** gmalloc_ptr; + +const constinit Pattern<8> GMALLOC_PATTERN{ + "89 0D {????????}" // mov [01F703F4],ecx { (05AA6980) } + "8B 11" // mov edx,[ecx] }; } // namespace -// ############################################################################// -// | BL1Hook Methods | -// ############################################################################// - void BL1Hook::find_gmalloc(void) { - // TODO: I would prefer to create FMalloc if it is null since all usages are wrapped in an - // if null then create block. However, with the delayed hooking via the init function this will - // always be non-null. - - gmalloc = *read_offset(GMALLOC_PATTERN.sigscan("GMalloc")); - if (gmalloc == nullptr) { - // This happens if we try to hook it too early. - throw std::runtime_error("GMalloc is null"); + gmalloc_ptr = read_offset(GMALLOC_PATTERN.sigscan_nullable()); + LOG(MISC, "GMalloc*: {:p}", reinterpret_cast(gmalloc_ptr)); + + if (gmalloc_ptr != nullptr) { + LOG(MISC, "GMalloc: {:p}", reinterpret_cast(*gmalloc_ptr)); } - LOG(MISC, "GMalloc: {:p}", reinterpret_cast(gmalloc)); } void* BL1Hook::u_malloc(size_t len) const { + if (gmalloc_ptr == nullptr) { + throw std::runtime_error("tried allocating memory while gmalloc was still null!"); + } + auto gmalloc = *gmalloc_ptr; + auto ret = gmalloc->vftable->u_malloc(gmalloc, len, get_malloc_alignment(len)); memset(ret, 0, len); return ret; } void* BL1Hook::u_realloc(void* original, size_t len) const { + if (gmalloc_ptr == nullptr) { + throw std::runtime_error("tried allocating memory while gmalloc was still null!"); + } + auto gmalloc = *gmalloc_ptr; + return gmalloc->vftable->u_realloc(gmalloc, original, len, get_malloc_alignment(len)); } void BL1Hook::u_free(void* data) const { + if (gmalloc_ptr == nullptr) { + throw std::runtime_error("tried allocating memory while gmalloc was still null!"); + } + auto gmalloc = *gmalloc_ptr; + gmalloc->vftable->u_free(gmalloc, data); } diff --git a/src/unrealsdk/game/bl1/object.cpp b/src/unrealsdk/game/bl1/object.cpp index 84ff160..2d0d54a 100644 --- a/src/unrealsdk/game/bl1/object.cpp +++ b/src/unrealsdk/game/bl1/object.cpp @@ -12,10 +12,6 @@ using namespace unrealsdk::memory; namespace unrealsdk::game { -// ############################################################################// -// | CONSTRUCT OBJECT | -// ############################################################################// - #pragma region ConstructObject namespace { @@ -31,24 +27,23 @@ typedef UObject*(__cdecl* construct_obj_func)(UClass* cls, uint32_t assume_template_is_archetype); construct_obj_func construct_obj_ptr; -const constinit Pattern<49> CONSTRUCT_OBJECT_PATTERN{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18ED693 - "64A1 00000000" // mov eax,dword ptr fs:[0] +const constinit Pattern<47> CONSTRUCT_OBJECT_PATTERN{ + "6A FF" // push -01 + "68 ????????" // push 018E9573 + "64 A1 ????????" // mov eax, fs:[00000000] "50" // push eax - "83EC 0C" // sub esp,C + "83 EC 0C" // sub esp, 0C "53" // push ebx "55" // push ebp "56" // push esi "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33C4" // xor eax,esp + "A1 ????????" // mov eax, [01F131C0] + "33 C4" // xor eax, esp "50" // push eax - "8D4424 20" // lea eax,dword ptr ss:[esp+20] - "64 A3 00000000" // mov dword ptr fs:[0],eax - "8B6C24 54" // mov ebp,dword ptr ss:[esp+54] - "896C24 14" // mov dword ptr ss:[esp+14],ebp - "85ED" // test ebp,ebp + "8D 44 24 ??" // lea eax, [esp+20] + "64 A3 ????????" // mov fs:[00000000], eax + "8B 6C 24 ??" // mov ebp, [esp+54] + "89 6C 24 ??" // mov [esp+14], ebp }; } // namespace @@ -69,10 +64,6 @@ UObject* BL1Hook::construct_object(UClass* cls, #pragma endregion -// ############################################################################// -// | PATH NAME | -// ############################################################################// - #pragma region PathName namespace { @@ -93,16 +84,13 @@ typedef void(__thiscall* get_path_name_func)(const UObject* self, get_path_name_func get_path_name_ptr; -const constinit Pattern<19> GET_PATH_NAME_PATTERN{ - "8B4424 04" // mov eax,dword ptr ss:[esp+4] - "56" // push esi - "8BF1" // mov esi,ecx - "3BF0" // cmp esi,eax - "74 76" // je borderlands.5CF501 - "85F6" // test esi,esi - "74 72" // je borderlands.5CF501 - "8B4E 28" // mov ecx,dword ptr ds:[esi+28] - "53" // push ebx +const constinit Pattern<13> GET_PATH_NAME_PATTERN{ + "8B 44 24 ??" // mov eax, [esp+04] + "56" // push esi + "8B F1" // mov esi, ecx + "3B F0" // cmp esi, eax + "74 ??" // je 005D09D1 + "85 F6" // test esi, esi }; } // namespace @@ -120,10 +108,6 @@ std::wstring BL1Hook::uobject_path_name(const UObject* obj) const { #pragma endregion -// ############################################################################// -// | FIND OBJECT | -// ############################################################################// - #pragma region FindObject namespace { @@ -135,25 +119,23 @@ typedef UObject*(__cdecl* static_find_object_func)(const UClass* cls, uint32_t exact_class); static_find_object_func static_find_object_ptr; -const constinit Pattern<50> STATIC_FIND_OBJECT_PATTERN{ - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18EC190 - "64A1 00000000" // mov eax,dword ptr fs:[0] - "50" // push eax - "83EC 24" // sub esp,24 - "53" // push ebx - "55" // push ebp - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax,dword ptr ds:[1F16980] - "33 C4" // xor eax,esp - "50" // push eax - "8D4424 38" // lea eax,dword ptr ss:[esp+38] - "64A3 00000000" // mov dword ptr fs:[0],eax - "8B7424 4C" // mov esi,dword ptr ss:[esp+4C] - "8B7C24 50" // mov edi,dword ptr ss:[esp+50] - "8BC6" // mov eax,esi - "40" // inc eax +const constinit Pattern<47> STATIC_FIND_OBJECT_PATTERN{ + "6A FF" // push -01 + "68 ????????" // push 018E7FF0 + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "83 EC 24" // sub esp, 24 + "53" // push ebx + "55" // push ebp + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax, [01F131C0] + "33 C4" // xor eax, esp + "50" // push eax + "8D 44 24 ??" // lea eax, [esp+38] + "64 A3 ????????" // mov fs:[00000000], eax + "8B 74 24 ??" // mov esi, [esp+4C] + "8B 7C 24 ??" // mov edi, [esp+50] }; } // namespace @@ -169,10 +151,6 @@ UObject* BL1Hook::find_object(UClass* cls, const std::wstring& name) const { #pragma endregion -// ############################################################################// -// | LOAD PACKAGE | -// ############################################################################// - #pragma region LoadPackage namespace { @@ -180,19 +158,15 @@ namespace { using load_package_func = UObject* (*)(const UObject* outer, const wchar_t* name, uint32_t flags); load_package_func load_package_ptr; -const constinit Pattern<34> LOAD_PACKAGE_PATTERN{ +const constinit Pattern<21> LOAD_PACKAGE_PATTERN{ "55" // push ebp - "8B EC" // mov ebp,esp - "6A FF" // push FFFFFFFF - "68 ????????" // push borderlands.18ECDB0 - "64 A1 00000000" // mov eax,dword ptr fs:[0] + "8B EC" // mov ebp, esp + "6A FF" // push -01 + "68 ????????" // push 018E8C90 + "64 A1 ????????" // mov eax, fs:[00000000] "50" // push eax - "83EC 28" // sub esp,28 - "53 56 57" // push ebx - "A1 ????????" // push esi - "33C5" // push edi - "50" // mov eax,dword ptr ds:[1F16980] - "8D45 F4" // xor eax,ebp + "83 EC 28" // sub esp, 28 + "53" // push ebx }; } // namespace diff --git a/src/unrealsdk/game/bl1/presistentobjectptr.cpp b/src/unrealsdk/game/bl1/persistentobjectptr.cpp similarity index 100% rename from src/unrealsdk/game/bl1/presistentobjectptr.cpp rename to src/unrealsdk/game/bl1/persistentobjectptr.cpp From 2eb7b171ce502a2b35e54cb0bfbb7aaa2e3ffe9e Mon Sep 17 00:00:00 2001 From: apple1417 Date: Thu, 22 May 2025 22:40:36 +1200 Subject: [PATCH 78/81] change how enum names are handled in hindsight this casting was never really going to work, it was crashing in bl1 since it has a different layout also importing the namespace removal code from pyunrealsdk, since it makes the function more useful and a bit meaningfully different --- src/unrealsdk/game/bl2/offsets.h | 4 +--- src/unrealsdk/game/bl3/offsets.h | 6 ++++-- src/unrealsdk/unreal/classes/uenum.cpp | 28 ++++++++++++++++++-------- src/unrealsdk/unreal/classes/uenum.h | 17 +++++++++++++++- src/unrealsdk/unreal/offset_list.h | 17 ++++++++-------- 5 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/unrealsdk/game/bl2/offsets.h b/src/unrealsdk/game/bl2/offsets.h index 02dca1b..74ef1c7 100644 --- a/src/unrealsdk/game/bl2/offsets.h +++ b/src/unrealsdk/game/bl2/offsets.h @@ -101,9 +101,7 @@ class UFunction : public T { template class UEnum : public T { - friend class unreal::UEnum; - - private: + public: unreal::TArray Names; }; diff --git a/src/unrealsdk/game/bl3/offsets.h b/src/unrealsdk/game/bl3/offsets.h index 8ad024c..eb9d77e 100644 --- a/src/unrealsdk/game/bl3/offsets.h +++ b/src/unrealsdk/game/bl3/offsets.h @@ -139,11 +139,13 @@ class UFunction : public UStruct { using UConst = unreal::offsets::generic::UConst; class UEnum : public UField { - friend class unreal::UEnum; - private: unreal::UnmanagedFString CppType; + + public: unreal::TArray> Names; + + private: int64_t CppForm; }; diff --git a/src/unrealsdk/unreal/classes/uenum.cpp b/src/unrealsdk/unreal/classes/uenum.cpp index 4e3c165..c469df8 100644 --- a/src/unrealsdk/unreal/classes/uenum.cpp +++ b/src/unrealsdk/unreal/classes/uenum.cpp @@ -2,18 +2,30 @@ #include "unrealsdk/unreal/classes/uenum.h" #include "unrealsdk/game/bl2/offsets.h" #include "unrealsdk/game/bl3/offsets.h" +#include "unrealsdk/unreal/offset_list.h" +#include "unrealsdk/unreal/offsets.h" #include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unrealsdk.h" namespace unrealsdk::unreal { +UNREALSDK_DEFINE_FIELDS_SOURCE_FILE(UEnum, UNREALSDK_UENUM_FIELDS); + #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK std::unordered_map UEnum::get_names(void) const { - auto bl3_enum = reinterpret_cast(this); std::unordered_map output; - for (size_t i = 0; i < bl3_enum->Names.size(); i++) { - auto pair = bl3_enum->Names.at(i); - output.emplace(pair.key, pair.value); + auto names = this->Names(); + for (size_t i = 0; i < names.size(); i++) { + auto pair = names.at(i); + + // Oak enums include the enum class name and a namespace separator before the value's name + // If we see it, strip it + const std::wstring str_key{pair.key}; + auto after_colons = str_key.find_first_not_of(L':', str_key.find_first_of(L':')); + + output.emplace(after_colons == std::string::npos ? pair.key : str_key.substr(after_colons), + pair.value); } return output; } @@ -21,11 +33,11 @@ std::unordered_map UEnum::get_names(void) const { #elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW std::unordered_map UEnum::get_names(void) const { - auto bl2_enum = reinterpret_cast(this); - std::unordered_map output; - for (size_t i = 0; i < bl2_enum->Names.size(); i++) { - output.emplace(bl2_enum->Names.at(i), i); + auto names = this->Names(); + for (size_t i = 0; i < names.size(); i++) { + // Willow enums just use the raw name, and are always stored in order + output.emplace(names.at(i), i); } return output; } diff --git a/src/unrealsdk/unreal/classes/uenum.h b/src/unrealsdk/unreal/classes/uenum.h index 153040b..fd6584c 100644 --- a/src/unrealsdk/unreal/classes/uenum.h +++ b/src/unrealsdk/unreal/classes/uenum.h @@ -29,8 +29,23 @@ class UEnum : public UField { UEnum& operator=(UEnum&&) = delete; ~UEnum() = delete; +#if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW + using names_type = TArray; +#elif UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_OAK + using names_type = TArray >; +#else +#error Unknown sdk flavour +#endif + + // These fields become member functions, returning a reference into the object. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNREALSDK_UENUM_FIELDS(X) X(names_type, Names) + + UNREALSDK_DEFINE_FIELDS_HEADER(UEnum, UNREALSDK_UENUM_FIELDS); + /** - * @brief Get the values of an enum which have assigned names. + * @brief Converts the enum names into a more usable name to value map. + * @note Keys are always the raw enum names, rather than `MyEnum::Entry` it's always `Entry`. * * @return A map of names to their associated integer values. */ diff --git a/src/unrealsdk/unreal/offset_list.h b/src/unrealsdk/unreal/offset_list.h index ed6fd17..2cd1724 100644 --- a/src/unrealsdk/unreal/offset_list.h +++ b/src/unrealsdk/unreal/offset_list.h @@ -37,27 +37,28 @@ namespace unrealsdk::unreal::offsets { // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define UNREALSDK__DYNAMIC_OFFSET_TYPES(X) \ - X(UObject) \ - X(UField) \ - X(UStruct) \ - X(UProperty) \ - X(UClass) \ - X(UScriptStruct) \ - X(UFunction) \ - X(UConst) \ X(UArrayProperty) \ X(UBoolProperty) \ X(UByteAttributeProperty) \ X(UByteProperty) \ + X(UClass) \ X(UClassProperty) \ + X(UConst) \ X(UDelegateProperty) \ + X(UEnum) \ X(UEnumProperty) \ + X(UField) \ X(UFloatAttributeProperty) \ + X(UFunction) \ X(UIntAttributeProperty) \ X(UInterfaceProperty) \ X(UMulticastDelegateProperty) \ + X(UObject) \ X(UObjectProperty) \ + X(UProperty) \ + X(UScriptStruct) \ X(USoftClassProperty) \ + X(UStruct) \ X(UStructProperty) #define UNREALSDK_OFFSETS__DEFINE_OFFSET_LIST_MEMBERS(name) unrealsdk::unreal::name::Offsets name; From a611960beb2b0b376d7fc7ead0611895b1bf367e Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 24 May 2025 18:21:28 +1200 Subject: [PATCH 79/81] don't match next line console commands on automated inputs --- src/unrealsdk/commands.cpp | 31 +++++++++++++++++++++++++----- src/unrealsdk/commands.h | 28 ++++++++++++++++++--------- src/unrealsdk/game/bl1/console.cpp | 14 ++++++++------ src/unrealsdk/game/bl2/console.cpp | 12 ++++++------ src/unrealsdk/game/bl3/console.cpp | 8 +++++--- 5 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/unrealsdk/commands.cpp b/src/unrealsdk/commands.cpp index 7d787bd..c0c24b1 100644 --- a/src/unrealsdk/commands.cpp +++ b/src/unrealsdk/commands.cpp @@ -82,17 +82,39 @@ namespace impl { #ifndef UNREALSDK_IMPORTING -std::pair find_matching_command(std::wstring_view line) { +bool is_command_valid(std::wstring_view line, bool direct_user_input) { + if (direct_user_input && commands.find(NEXT_LINE) != commands.end()) { + return true; + } + auto non_space = std::ranges::find_if_not(line, &std::iswspace); + if (non_space == line.end()) { + return false; + } + + auto cmd_end = std::find_if(non_space, line.end(), &std::iswspace); + + std::wstring cmd(cmd_end - non_space, '\0'); + std::transform(non_space, cmd_end, cmd.begin(), &std::towlower); + return commands.contains(cmd); +} + +void run_command(std::wstring_view line) { auto iter = commands.find(NEXT_LINE); if (iter != commands.end()) { auto callback = iter->second; commands.erase(iter); - return {callback, 0}; + + callback->operator()(line.data(), line.size(), 0); + + callback->destroy(); + return; } + // I realize we're redoing a bunch of work from above here, but meh. + // Hopefully LTO gets it auto non_space = std::ranges::find_if_not(line, &std::iswspace); if (non_space == line.end()) { - return {nullptr, 0}; + return; } auto cmd_end = std::find_if(non_space, line.end(), &std::iswspace); @@ -100,8 +122,7 @@ std::pair find_matching_command(std::wstring_view line std::wstring cmd(cmd_end - non_space, '\0'); std::transform(non_space, cmd_end, cmd.begin(), &std::towlower); - return commands.contains(cmd) ? std::pair{commands[cmd], cmd_end - line.begin()} - : std::pair{nullptr, 0}; + commands.at(cmd)->operator()(line.data(), line.size(), cmd_end - line.begin()); } #endif diff --git a/src/unrealsdk/commands.h b/src/unrealsdk/commands.h index 4310cca..5393577 100644 --- a/src/unrealsdk/commands.h +++ b/src/unrealsdk/commands.h @@ -12,16 +12,18 @@ You can use this module to register custom console commands, in a single unified To register a command, you simply provide it's name. Commands are matched by comparing the first block of non-whitespace characters in a line submitted to console against all registered names. -As a special case, if you register the special `NEXT_LINE` command, it will always match the very -next line, in place of anything else which might have been matched otherwise. It will then -immediately be removed (though before the callback is run, so you can re-register it if needed), to -allow normal command processing to continue afterwards. +To create interactive menus, rather than registering separate commands for every input, use the +special `NEXT_LINE` command, which always matches the very next line, with higher priority than +anything else. These commands are only matched once, and are automatically removed before running +the callback. They also try to only match direct user inputs in console, and try ignore commands +sent via automated means - e.g. if the game actually sends an `open` command to load a map. */ /** * @brief A special value used to register a command which will always match the very next line. * @note Only one next line command can be registered at a time. * @note The next line command is automatically removed after it gets matched. + * @note The next line command *should not* be matched by automated commands. */ extern const std::wstring NEXT_LINE; @@ -67,13 +69,21 @@ namespace impl { // These functions are only relevant when implementing a game #ifndef UNREALSDK_IMPORTING /** - * @brief Finds the command which matches the given line. + * @brief Checks if a command line contains a valid command to be run. * - * @param line The line which was submitted. - * @return A pair of the callback to run and the offset to pass to it, or of nullptr and 0 if there - * was no match. + * @param line The line to check. + * @param direct_user_input True if this command was directly input to console by a user. + * @return True if this contains a command we should run. */ -std::pair find_matching_command(std::wstring_view line); +bool is_command_valid(std::wstring_view line, bool direct_user_input); + +/** + * @brief Runs the command associated with the given line. + * @note Assumes is_command_valid previously returned true on the same line. + * + * @param line The line holding the command to be run. + */ +void run_command(std::wstring_view line); #endif } // namespace impl diff --git a/src/unrealsdk/game/bl1/console.cpp b/src/unrealsdk/game/bl1/console.cpp index e0eea26..0b990d4 100644 --- a/src/unrealsdk/game/bl1/console.cpp +++ b/src/unrealsdk/game/bl1/console.cpp @@ -6,11 +6,13 @@ #include "unrealsdk/config.h" #include "unrealsdk/hook_manager.h" #include "unrealsdk/unreal/classes/properties/copyable_property.h" +#include "unrealsdk/unreal/classes/properties/uboolproperty.h" #include "unrealsdk/unreal/classes/properties/uobjectproperty.h" #include "unrealsdk/unreal/classes/properties/ustrproperty.h" #include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/classes/uobject_funcs.h" #include "unrealsdk/unreal/find_class.h" +#include "unrealsdk/unreal/wrappers/bound_function.h" #if UNREALSDK_FLAVOUR == UNREALSDK_FLAVOUR_WILLOW && !defined(UNREALSDK_IMPORTING) @@ -76,8 +78,8 @@ bool console_command_hook(const hook_manager::Details& hook) { auto line = hook.args->get(command_property); - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { + // This hook only runs when input via console, it is direct user input + if (!commands::impl::is_command_valid(line, true)) { return false; } @@ -134,7 +136,7 @@ bool console_command_hook(const hook_manager::Details& hook) { LOG(MIN, L"{}", msg); try { - callback->operator()(line.c_str(), line.size(), cmd_len); + commands::impl::run_command(line); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); } @@ -148,15 +150,15 @@ bool pc_console_command_hook(const hook_manager::Details& hook) { auto line = hook.args->get(command_property); - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { + // Conversely, this hook is explicitly not from console + if (!commands::impl::is_command_valid(line, false)) { return false; } // This hook does not go to console, so there's no extra processing to be done, we can just run // the callback immediately try { - callback->operator()(line.c_str(), line.size(), cmd_len); + commands::impl::run_command(line); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); } diff --git a/src/unrealsdk/game/bl2/console.cpp b/src/unrealsdk/game/bl2/console.cpp index a15b402..aa1fb76 100644 --- a/src/unrealsdk/game/bl2/console.cpp +++ b/src/unrealsdk/game/bl2/console.cpp @@ -174,8 +174,8 @@ bool console_command_hook(hook_manager::Details& hook) { auto line = hook.args->get(command_property); - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { + // This hook only runs when input via console, it is direct user input + if (!commands::impl::is_command_valid(line, true)) { return false; } @@ -232,7 +232,7 @@ bool console_command_hook(hook_manager::Details& hook) { LOG(MIN, L"{}", msg); try { - callback->operator()(line.c_str(), line.size(), cmd_len); + commands::impl::run_command(line); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); } @@ -246,15 +246,15 @@ bool pc_console_command_hook(hook_manager::Details& hook) { auto line = hook.args->get(command_property); - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback == nullptr) { + // Conversely, this hook is explicitly not from console + if (!commands::impl::is_command_valid(line, false)) { return false; } // This hook does not go to console, so there's no extra processing to be done, we can just run // the callback immediately try { - callback->operator()(line.c_str(), line.size(), cmd_len); + commands::impl::run_command(line); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); } diff --git a/src/unrealsdk/game/bl3/console.cpp b/src/unrealsdk/game/bl3/console.cpp index 772553b..74a0ad2 100644 --- a/src/unrealsdk/game/bl3/console.cpp +++ b/src/unrealsdk/game/bl3/console.cpp @@ -61,8 +61,10 @@ void console_command_hook(UObject* console_obj, UnmanagedFString* raw_line) { try { std::wstring line = *raw_line; - auto [callback, cmd_len] = commands::impl::find_matching_command(line); - if (callback != nullptr) { + // Since this is a hook on UConsole, and since the game doesn't seem to be running it's own + // commands through it, just always consider it direct user input + // I don't really know what interface automated commands would come through, if any + if (commands::impl::is_command_valid(line, true)) { // Update the history buffer { // History buffer is oldest at index 0, newest at count @@ -142,7 +144,7 @@ void console_command_hook(UObject* console_obj, UnmanagedFString* raw_line) { LOG(MIN, L"{}", msg); try { - callback->operator()(line.c_str(), line.size(), cmd_len); + commands::impl::run_command(line); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred while running a console command: {}", ex.what()); } From e5bad41c67050a77cb1bc59062b4d74bb5d43ffb Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sat, 24 May 2025 18:33:51 +1200 Subject: [PATCH 80/81] update changelog been ignoring this for a while... deciding I'm going to drop the commit hashes at this point, it was a bit annoying before, let alone now, and no one really seems to be using them --- CMakeLists.txt | 2 +- changelog.md | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea96ce1..ed64090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) -project(unrealsdk VERSION 1.8.0) +project(unrealsdk VERSION 2.0.0) set(UNREALSDK_FLAVOUR "" CACHE STRING "Which \"flavour\" of the SDK to build.") set_property(CACHE UNREALSDK_FLAVOUR PROPERTY STRINGS "WILLOW" "OAK") diff --git a/changelog.md b/changelog.md index 33fe361..8013571 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,29 @@ # Changelog -## Upcoming +## 2.0.0 (Upcoming) +- Now supports Borderlands 1. Big thanks to Ry for doing basically all the reverse engineering. + +- Major refactor of the core unreal types, to cleanly allow them to change layouts at runtime. All + core fields have changed from members to zero-arg methods, which return a reference to the member. + A few classes (e.g. `UProperty` subclasses) previous had existing methods to deal with the same + problem, these have all been moved to the new system. + + Clang is able to detect this change, and gives a nice error recommending inserting brackets at the + right spot. + +- Removed the `UNREALSDK_UE_VERSION` and `UNREALSDK_ARCH` CMake variables, in favour a new merged + `UNREALSDK_FLAVOUR` variable. + +- Removed the (optional) dependency on libfmt, `std::format` support is now required. + +- Console commands registered using `unrealsdk::commands::NEXT_LINE` now (try to) only fire on + direct user input, and ignore commands send via automated means. - Fixed that assigning an entire array, rather than getting the array and setting it's elements, would likely cause memory corruption. This was most common when using an array of large structs, and when assigning to one which was previously empty. - [275bbc8b](https://github.com/bl-sdk/unrealsdk/commit/275bbc8b) +- Made `unrealsdk::memory::get_exe_range` public. ## 1.8.0 From e3a8847bd341dca6e89591ff1119d231476fe2ab Mon Sep 17 00:00:00 2001 From: apple1417 Date: Sun, 25 May 2025 16:18:02 +1200 Subject: [PATCH 81/81] compeltely rework hook data structures motiviation is discovering a crash in reign of giants, caused by removing a hook, then having a nested call to the same function invalidate the iterator --- src/unrealsdk/game/bl1/hooks.cpp | 16 +- src/unrealsdk/game/bl2/hooks.cpp | 16 +- src/unrealsdk/game/bl3/hooks.cpp | 16 +- src/unrealsdk/hook_manager.cpp | 490 ++++++++++++++++++++++--------- src/unrealsdk/hook_manager.h | 18 +- 5 files changed, 392 insertions(+), 164 deletions(-) diff --git a/src/unrealsdk/game/bl1/hooks.cpp b/src/unrealsdk/game/bl1/hooks.cpp index 5430426..7650c21 100644 --- a/src/unrealsdk/game/bl1/hooks.cpp +++ b/src/unrealsdk/game/bl1/hooks.cpp @@ -62,7 +62,7 @@ void __fastcall process_event_hook(UObject* obj, .ret = {func->find_return_param()}, .func = {.func = func, .object = obj}}; - const bool block_execution = run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + const bool block_execution = run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (!block_execution) { process_event_ptr(obj, edx, func, params, null); @@ -72,7 +72,7 @@ void __fastcall process_event_hook(UObject* obj, hook.ret.copy_to(reinterpret_cast(params)); } - if (!has_post_hooks(*data)) { + if (!has_post_hooks(data)) { return; } @@ -81,10 +81,10 @@ void __fastcall process_event_hook(UObject* obj, } if (!block_execution) { - run_hooks_of_type(*data, hook_manager::Type::POST, hook); + run_hooks_of_type(data, hook_manager::Type::POST, hook); } - run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, hook); + run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; } @@ -158,7 +158,7 @@ void __fastcall call_function_hook(UObject* obj, .ret = {func->find_return_param()}, .func = {.func = func, .object = obj}}; - const bool block_execution = run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + const bool block_execution = run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (block_execution) { stack->Code++; @@ -173,7 +173,7 @@ void __fastcall call_function_hook(UObject* obj, - hook.ret.prop->Offset_Internal()); } - if (!has_post_hooks(*data)) { + if (!has_post_hooks(data)) { return; } @@ -183,10 +183,10 @@ void __fastcall call_function_hook(UObject* obj, } if (!block_execution) { - run_hooks_of_type(*data, hook_manager::Type::POST, hook); + run_hooks_of_type(data, hook_manager::Type::POST, hook); } - run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, hook); + run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; } diff --git a/src/unrealsdk/game/bl2/hooks.cpp b/src/unrealsdk/game/bl2/hooks.cpp index 63ba284..cbaf5e3 100644 --- a/src/unrealsdk/game/bl2/hooks.cpp +++ b/src/unrealsdk/game/bl2/hooks.cpp @@ -79,7 +79,7 @@ void __fastcall process_event_hook(UObject* obj, .func = {.func = func, .object = obj}}; const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (!block_execution) { process_event_ptr(obj, edx, func, params, null); @@ -89,7 +89,7 @@ void __fastcall process_event_hook(UObject* obj, hook.ret.copy_to(reinterpret_cast(params)); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!hook_manager::impl::has_post_hooks(data)) { return; } @@ -98,10 +98,10 @@ void __fastcall process_event_hook(UObject* obj, } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; @@ -191,7 +191,7 @@ void __fastcall call_function_hook(UObject* obj, .func = {.func = func, .object = obj}}; const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (block_execution) { stack->Code++; @@ -206,7 +206,7 @@ void __fastcall call_function_hook(UObject* obj, - hook.ret.prop->Offset_Internal()); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!hook_manager::impl::has_post_hooks(data)) { return; } @@ -216,10 +216,10 @@ void __fastcall call_function_hook(UObject* obj, } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; diff --git a/src/unrealsdk/game/bl3/hooks.cpp b/src/unrealsdk/game/bl3/hooks.cpp index 130e5e0..c66233e 100644 --- a/src/unrealsdk/game/bl3/hooks.cpp +++ b/src/unrealsdk/game/bl3/hooks.cpp @@ -48,7 +48,7 @@ void process_event_hook(UObject* obj, UFunction* func, void* params) { .func = {.func = func, .object = obj}}; const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (!block_execution) { process_event_ptr(obj, func, params); @@ -58,7 +58,7 @@ void process_event_hook(UObject* obj, UFunction* func, void* params) { hook.ret.copy_to(reinterpret_cast(params)); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!hook_manager::impl::has_post_hooks(data)) { return; } @@ -67,10 +67,10 @@ void process_event_hook(UObject* obj, UFunction* func, void* params) { } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; @@ -161,7 +161,7 @@ void call_function_hook(UObject* obj, FFrame* stack, void* result, UFunction* fu .func = {.func = func, .object = obj}}; const bool block_execution = - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::PRE, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::PRE, hook); if (block_execution) { stack->Code++; @@ -176,7 +176,7 @@ void call_function_hook(UObject* obj, FFrame* stack, void* result, UFunction* fu - hook.ret.prop->Offset_Internal()); } - if (!hook_manager::impl::has_post_hooks(*data)) { + if (!hook_manager::impl::has_post_hooks(data)) { return; } @@ -186,10 +186,10 @@ void call_function_hook(UObject* obj, FFrame* stack, void* result, UFunction* fu } if (!block_execution) { - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST, hook); + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST, hook); } - hook_manager::impl::run_hooks_of_type(*data, hook_manager::Type::POST_UNCONDITIONAL, + hook_manager::impl::run_hooks_of_type(data, hook_manager::Type::POST_UNCONDITIONAL, hook); return; diff --git a/src/unrealsdk/hook_manager.cpp b/src/unrealsdk/hook_manager.cpp index 19b61b0..46577ad 100644 --- a/src/unrealsdk/hook_manager.cpp +++ b/src/unrealsdk/hook_manager.cpp @@ -16,84 +16,130 @@ using namespace unrealsdk::unreal; #ifndef UNREALSDK_IMPORTING namespace unrealsdk::hook_manager::impl { -namespace { - /* -We store all hooks in a multi layered mapping. - -The outermost level is the FName of the function, which lets us do a very quick compare to discard -most calls. The second level is the full path name string, what was passed in, which we use to -confirm we've got the right hook. - -`preprocess_hook` looks through these first two levels to determine if the called function has any -hooks, and returns the "List" of all hooks which apply to that function at any stage. - -The list then maps each hook type into "Group"s. Each group is a full set to hooks to run on a -specific stage. +The fact that hooks run arbitrary user provided callbacks means our data structure can get modified +in a number of awkward ways while we're in the middle of iterating over it. There's the obvious case +of a hook can remove itself, but also tricker ones like a hook can invoke a nested hook on itself, +and only remove itself there, or the upper layer hook could remove itself and the nested one could +re-add it, etc. It gets even messier if we consider threading. + +So the number one concern behind this design is robustness - we need to support essentially +arbitrary modification while we're in the middle of iterating through it. This means performance is +a secondary concern - though I still tried keeping it mind - I haven't done any benchmarking. + +The easiest way to keep our iterators valid is to use linked lists. However, using a bunch of +`std::list`s isn't the best for performance, as we'll discuss in a bit, since need several layers +of them, so instead we use a bunch of intrusive linked lists on our own type. + + +The most basic form of the data structure we want is essentially a: + map>>>> + +Matching FNames first lets us discard function calls far quicker than needing to do a string +comparison on the full name. After matching both of them, we then need to split by hook type, which +finally gets us the collection of callbacks to run. The identifiers have no influence when matching +hooks, they're only used when adding/removing them. + +The smallest unit we have to iterate over is the hooks of the same type on the same function - the +one labeled just "collection" above. This is the `next_in_collection` linked list. + +Then we need to be able to jump between the collections on the same hook, but of different type. The +heads of the collection linked lists form into a second `next_type` linked list. We use an intrusive +linked list, so the exact same node from the above list also holds the pointers for this one. + +Above that, we need to jump between groups of hooks which share the same fname, but have different +full function names. The heads of the type linked lists form the third `next_function` linked list. + +Finally, we need to iterate through FNames. We actually do this using a hash table - but we still +need to deal with collisions. The heads of the function linked lists form our fourth and final +`next_collision` linked list. + +Trying to roughly diagram an example, this is what might be in a single hash bucket: + +Collision | [A] -----------------------------> [F] + | : : +Function | [A] ---------------> [D] [F] -> [G] + | : : : : +Type | [A] --------> [C] : : : + | : : : : : +Collection | [A] -> [B] [C] [D] -> [E] [F] [G] + +[A] Class::Func, post-hook +[B] Class::Func, post-hook +[C] Class::Func, pre-hook +[D] OtherClass::Func +[E] OtherClass::Func +[F] ThirdClass::SomeOtherFunc, where `SomeOtherFunc` and `Func` happen to get a hash collision +[G] FourthClass::SomeOtherFunc + +Each column is a single node, a node may be in multiple linked lists. */ -using Group = utils::StringViewMap; - -} // namespace - -struct List { - Group pre; - Group post; - Group post_unconditional; - - /** - * @brief Checks if all groups in the list are empty. - * - * @return True if all groups are empty - */ - [[nodiscard]] bool empty(void) const { - return this->pre.empty() && this->post.empty() && this->post_unconditional.empty(); - } - - /** - * @brief Gets a hook group on this list from it's type. - * - * @param type The hook type to get. - * @return A reference to the selected hook group on this object, or nullptr if calling the safe - * version and the type's invalid. - */ - [[nodiscard]] Group& get_group_by_type(Type type) { - switch (type) { - case Type::PRE: - return this->pre; - case Type::POST: - return this->post; - case Type::POST_UNCONDITIONAL: - return this->post_unconditional; - default: - throw std::invalid_argument("Invalid hook type " + std::to_string((uint8_t)type)); - } - } - // We only need the const version in practice - [[nodiscard]] const Group* get_safe_group_by_type(Type type) const noexcept { - switch (type) { - case Type::PRE: - return &this->pre; - case Type::POST: - return &this->post; - case Type::POST_UNCONDITIONAL: - return &this->post_unconditional; - default: - return nullptr; +struct Node { + public: + FName fname; + std::wstring full_name; + Type type; + std::wstring identifier; + DLLSafeCallback* callback; + + // Using shared pointers because it's easy + // Since we use std::make_shared, we're not really wasting allocations, but as a future + // optimization we could use an intrusive reference count to avoid repeating all the control + // block pointers, and save a little memory. + std::shared_ptr next_collision = nullptr; + std::shared_ptr next_function = nullptr; + std::shared_ptr next_type = nullptr; + std::shared_ptr next_in_collection = nullptr; + + Node(FName fname, + std::wstring_view full_name, + Type type, + std::wstring_view identifier, + DLLSafeCallback* callback) + : fname(fname), + full_name(full_name), + type(type), + identifier(identifier), + callback(callback) {} + Node(const Node&) = default; + Node(Node&&) noexcept = default; + Node& operator=(const Node&) = default; + Node& operator=(Node&&) noexcept = default; + ~Node() { + if (this->callback != nullptr) { + this->callback->destroy(); + this->callback = nullptr; } } }; namespace { -thread_local bool should_inject_next_call = false; +const constexpr auto HASH_TABLE_SIZE = 0x1000; +std::array, HASH_TABLE_SIZE> hooks_hash_table; + +/** + * @brief Hashes the given fname, and returns which index of the table it goes in. + * + * @param name The name to check. + * @return The hash table index. + */ +size_t get_table_index(FName fname) { + static_assert(sizeof(unrealsdk::unreal::FName) == sizeof(uint64_t), + "FName is not same size as a uint64"); + uint64_t val{}; + memcpy(&val, &fname, sizeof(fname)); + + // Identity seems mostly good enough - FNames already are just a relatively small integer value. + // This throws away the number field, but that's very rarely used on functions to begin with. + return val % HASH_TABLE_SIZE; +} bool should_log_all_calls = false; std::wofstream log_all_calls_stream{}; std::mutex log_all_calls_stream_mutex{}; -std::unordered_map> hooks{}; - void log_all_calls(bool should_log) { // Only keep this file stream open while we need it if (should_log) { @@ -112,6 +158,8 @@ void log_all_calls(bool should_log) { } } +thread_local bool should_inject_next_call = false; + void inject_next_call(void) { should_inject_next_call = true; } @@ -136,80 +184,243 @@ bool add_hook(std::wstring_view func, DLLSafeCallback* callback) { auto fname = extract_func_obj_name(func); - auto& path_name_map = hooks[fname]; + auto hash_idx = get_table_index(fname); + auto node = hooks_hash_table.at(hash_idx); + if (node == nullptr) { + // This function isn't in the hash table, can just add directly. + hooks_hash_table.at(hash_idx) = + std::make_shared(fname, func, type, identifier, callback); + return true; + } - // Doing this a bit weirdly to try avoid allocating a new string - we can get via string view, - // but setting requires converting to a full string first - auto iter = path_name_map.find(func); - if (iter == path_name_map.end()) { - iter = path_name_map.emplace(func, List{}).first; + // Look through hash collisions + while (node->fname != fname) { + if (node->next_collision == nullptr) { + // We found a collision, but nothing matched our name, so add it to the end + node->next_collision = std::make_shared(fname, func, type, identifier, callback); + return true; + } + node = node->next_collision; } - auto& group = iter->second.get_group_by_type(type); - if (group.contains(identifier)) { - return false; + // Look though full function names + while (node->full_name != func) { + if (node->next_function == nullptr) { + // We found another function with the same fname, but nothing matches the full name + node->next_function = std::make_shared(fname, func, type, identifier, callback); + return true; + } + node = node->next_function; } - group.emplace(identifier, callback); - return true; + // Look through hook types + while (node->type != type) { + if (node->next_type == nullptr) { + // We found the right function, but it doesn't have any hooks of this type yet + node->next_type = std::make_shared(fname, func, type, identifier, callback); + return true; + } + node = node->next_type; + } + + // Look through all remaining hooks to see if we can match the identifier + while (node->identifier != identifier) { + if (node->next_in_collection == nullptr) { + // Didn't find a matching identifier, add our new hook at the end + node->next_in_collection = + std::make_shared(fname, func, type, identifier, callback); + } + node = node->next_in_collection; + } + + // We already have this identifier, can't insert + return false; } bool has_hook(std::wstring_view func, Type type, std::wstring_view identifier) { auto fname = extract_func_obj_name(func); - auto fname_iter = hooks.find(fname); - if (fname_iter == hooks.end()) { + auto hash_idx = get_table_index(fname); + auto node = hooks_hash_table.at(hash_idx); + if (node == nullptr) { + // This function isn't even in the hash table return false; } - auto& path_name_map = fname_iter->second; - auto path_name_iter = path_name_map.find(func); - if (path_name_iter == path_name_map.end()) { - return false; + // Look through hash collisions + while (node->fname != fname) { + if (node->next_collision == nullptr) { + // We found a collision, but nothing matched our name + return false; + } + node = node->next_collision; + } + + // Look though full function names + while (node->full_name != func) { + if (node->next_function == nullptr) { + // We found another function with the same fname, but nothing matches the full name + return false; + } + node = node->next_function; + } + + // Look through hook types + while (node->type != type) { + if (node->next_type == nullptr) { + // We found the right function, but it doesn't have any hooks of this type + return false; + } + node = node->next_type; } - return path_name_iter->second.get_group_by_type(type).contains(identifier); + // Look through all remaining hooks to see if we can match the identifier + while (node->identifier != identifier) { + if (node->next_in_collection == nullptr) { + // Didn't find a matching identifier + return false; + } + node = node->next_in_collection; + } + + return true; } bool remove_hook(std::wstring_view func, Type type, std::wstring_view identifier) { auto fname = extract_func_obj_name(func); - auto fname_iter = hooks.find(fname); - if (fname_iter == hooks.end()) { + auto hash_idx = get_table_index(fname); + auto node = hooks_hash_table.at(hash_idx); + if (node == nullptr) { + // This function isn't even in the hash table return false; } - auto& path_name_map = fname_iter->second; - auto path_name_iter = path_name_map.find(func); - if (path_name_iter == path_name_map.end()) { - return false; + // Look through hash collisions + decltype(node) prev_collision = nullptr; + while (node->fname != fname) { + if (node->next_collision == nullptr) { + // We found a collision, but nothing matched our name + return false; + } + prev_collision = node; + node = node->next_collision; } - auto& group = path_name_iter->second.get_group_by_type(type); - auto group_iter = group.find(identifier); - if (group_iter == group.end()) { - return false; + // Look though full function names + decltype(node) prev_function = nullptr; + while (node->full_name != func) { + if (node->next_function == nullptr) { + // We found another function with the same fname, but nothing matches the full name + return false; + } + prev_function = node; + node = node->next_function; } - group_iter->second->destroy(); - group.erase(group_iter); + // Look through hook types + decltype(node) prev_type = nullptr; + while (node->type != type) { + if (node->next_type == nullptr) { + // We found the right function, but it doesn't have any hooks of this type + return false; + } + prev_type = node; + node = node->next_type; + } + + // Look through all remaining hooks to see if we can match the identifier + decltype(node) prev_in_collection = nullptr; + while (node->identifier != identifier) { + if (node->next_in_collection == nullptr) { + // Didn't find a matching identifier + return false; + } + prev_in_collection = node; + node = node->next_in_collection; + } /* - Important Note: While it's tempting, we can't also erase the higher levels here if they're - empty, because we may be being called from inside a hook, since this may cause a use after free. + We found a matching hook - 'node' is pointing at it. + + Consider the following diagram the diagram from above: + + Collision | [A] -----------------------------> [F] + | : : + Function | [A] ---------------> [D] [F] + | : : : + Type | [A] : [F] --------> [H] + | : : : : + Collection | [A] -> [B] -> [C] [D] -> [E] [F] -> [G] [H] + + Lets say we want to remove B, D, and F. The new layout we need is: + + Collision | [A] ------------------------------------> [G] + | : : + Function | [A] ----------------------> [E] : + | : : : + Type | [A] : [G] -> [H] + | : : : : + Collection | [A] --------> [C] [E] [G] [H] + + Removing B is easy, we simply set A->next_in_collection = C to bypass it. + + For D, we need to work bottom up. If our node is the head of the list, we need to promote our + next node up a layer. If our node is also the head of the above layer, we need to recurse + another layer up. So since D is the head of both the collection and type linked lists, we move + up to the function linked list, and need to set A->next_function = E. + + F exposes a further complication on top of D, since it was pointing at multiple other nodes. As + part of promoting a node up a layer, we need to insert it into that layer's linked list - that's + how we keep then link to H. In D's case, these were all null. + + Since only the heads of the lower-layer lists are used in the higher layer ones, as soon as we + come across a layer where we're not the head, we must be done, the lower layer nodes cannot have + other references to upper ones. + */ - We make sure to take a copy of the group in `run_hooks_of_type`, so invalidating iterators - within it is not a concern. + if (prev_in_collection != nullptr) { + prev_in_collection->next_in_collection = node->next_in_collection; + return true; + } + if (node->next_in_collection) { + node->next_in_collection->next_type = node->next_type; + node->next_type = node->next_in_collection; + } - Instead, we clean up during `preprocess_hook` - */ + if (prev_type != nullptr) { + prev_type->next_type = node->next_type; + return true; + } + if (node->next_type != nullptr) { + node->next_type->next_function = node->next_function; + node->next_function = node->next_type; + } + if (prev_function != nullptr) { + prev_function->next_function = node->next_function; + return true; + } + if (node->next_function != nullptr) { + node->next_function->next_collision = node->next_collision; + node->next_collision = node->next_function; + } + + if (prev_collision != nullptr) { + prev_collision->next_collision = node->next_collision; + return true; + } + // There's no higher layer linked list left. If we have following collision entries, set the + // hash table to them. If we don't, we want to null it anyway. + hooks_hash_table.at(hash_idx) = node->next_collision; return true; } } // namespace -const List* preprocess_hook(std::wstring_view source, const UFunction* func, const UObject* obj) { +std::shared_ptr preprocess_hook(std::wstring_view source, + const UFunction* func, + const UObject* obj) { if (should_inject_next_call) { should_inject_next_call = false; return nullptr; @@ -229,53 +440,70 @@ const List* preprocess_hook(std::wstring_view source, const UFunction* func, con } } - // Check if anything matches the function FName - auto fname_iter = hooks.find(func->Name()); - if (fname_iter == hooks.end()) { + auto fname = func->Name(); + + auto hash_idx = get_table_index(fname); + auto node = hooks_hash_table.at(hash_idx); + if (node == nullptr) { + // This function isn't even in the hash table return nullptr; } - auto& path_name_map = fname_iter->second; - if (path_name_map.empty()) { - hooks.erase(func->Name()); - return nullptr; + + // Look through hash collisions + while (node->fname != fname) { + if (node->next_collision == nullptr) { + // We found a collision, but nothing matched our name + return nullptr; + } + node = node->next_collision; } - // Now check the full path name + // At this point we need the full path name if (!should_log_all_calls) { func_name = func->get_path_name(); } - auto path_name_iter = path_name_map.find(func_name); - if (path_name_iter == path_name_map.end()) { - return nullptr; - } - auto& list = path_name_iter->second; - if (list.empty()) { - path_name_map.erase(func_name); - return nullptr; + + // Look though full function names + while (node->full_name != func_name) { + if (node->next_function == nullptr) { + // We found another function with the same fname, but nothing matches the full name + return nullptr; + } + node = node->next_function; } - return &list; + // Break off at this point - we know we have hooks on this function, so the hook processing will + // need to start extracting args. + return node; } -bool has_post_hooks(const List& list) { - return !list.post.empty() || !list.post_unconditional.empty(); +bool has_post_hooks(std::shared_ptr node) { + // We got the node from preprocess_hook, it's pointing to the start of the types linked list + for (; node != nullptr; node = node->next_type) { + if (node->type == Type::POST || node->type == Type::POST_UNCONDITIONAL) { + return true; + } + } + return false; } -bool run_hooks_of_type(const List& list, Type type, Details& hook) { - const Group* group_ptr = list.get_safe_group_by_type(type); - if (group_ptr == nullptr) { - LOG(ERROR, "Tried to run hooks of invalid type {}", (uint8_t)type); - return false; - } +bool run_hooks_of_type(std::shared_ptr node, Type type, Details& hook) { + // We got the node from preprocess_hook, it's pointing to the start of the types linked list - // Grab a copy of the revelevant hook group, in case the hook removes itself (which would - // invalidate the iterator) - const Group group = *group_ptr; + // Look through hook types + while (node->type != type) { + if (node->next_type == nullptr) { + // No hooks of this type - return false to not block + return false; + } + node = node->next_type; + } + // We've got the final list of hooks, run them all bool ret = false; - for (const auto& [_, hook_function] : group) { + for (; node != nullptr; node = node->next_in_collection) { try { - ret |= hook_function->operator()(hook); + ret |= node->callback->operator()(hook); } catch (const std::exception& ex) { LOG(ERROR, "An exception occurred during hook processing"); LOG(ERROR, L"Function: {}", hook.func.func->get_path_name()); diff --git a/src/unrealsdk/hook_manager.h b/src/unrealsdk/hook_manager.h index 8cca28e..a45fa67 100644 --- a/src/unrealsdk/hook_manager.h +++ b/src/unrealsdk/hook_manager.h @@ -109,7 +109,7 @@ bool remove_hook(std::wstring_view func, Type type, std::wstring_view identifier #ifndef UNREALSDK_IMPORTING namespace impl { // These functions are only relevant when implementing a game hook -struct List; +struct Node; /* Processing hooks needs to be very optimized, thousands if not tens of thousands of functions calls @@ -140,29 +140,29 @@ to work out if to early exit again. If it does, it can spend a bit longer extrac * @param source The source of the call, used for logging. * @param func The function which was called. * @param obj The object which called the function. - * @return A pointer to the relevant hook list, or nullptr if no hooks match. + * @return A node pointer to pass into the following functions, or nullptr if no hooks match. */ -const List* preprocess_hook(std::wstring_view source, - const unreal::UFunction* func, - const unreal::UObject* obj); +std::shared_ptr preprocess_hook(std::wstring_view source, + const unreal::UFunction* func, + const unreal::UObject* obj); /** * @brief Checks if a hook list contains any post hooks. * - * @param list The hook list, retrieved from `preprocess_hook`. + * @param node The node previously retrieved from `preprocess_hook`. * @return True if the list contains post hooks. */ -bool has_post_hooks(const List& list); +bool has_post_hooks(std::shared_ptr node); /** * @brief Runs all the hooks in a list which match the given type. * - * @param list The hook list, retrieved from `preprocess_hook`. + * @param node The node previously retrieved from `preprocess_hook`. * @param type The type of hooks to run. * @param hook The hook details. * @return The logical or of the hooks' return values. */ -bool run_hooks_of_type(const List& list, Type type, Details& hook); +bool run_hooks_of_type(std::shared_ptr node, Type type, Details& hook); } // namespace impl #endif