From bbf946761589810f77164e70e3dcbc977b598b50 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 26 Dec 2023 13:00:26 +1300 Subject: [PATCH 1/3] throw version errors instead of conditionally compiling ftext --- .clang-tidy | 1 + .cruft.json | 2 +- src/unrealsdk/game/abstract_hook.h | 2 -- src/unrealsdk/game/bl2/bl2.cpp | 10 ++++++++++ src/unrealsdk/game/bl2/bl2.h | 2 ++ src/unrealsdk/unrealsdk.h | 4 ---- src/unrealsdk/unrealsdk_fw.inl | 3 --- src/unrealsdk/unrealsdk_main.cpp | 4 ---- src/unrealsdk/unrealsdk_wrappers.cpp | 4 ---- 9 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 4885f75..9534cad 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -36,6 +36,7 @@ Checks: > -cppcoreguidelines-non-private-member-variables-in-classes, CheckOptions: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true + cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams: true misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: true readability-identifier-naming.GlobalConstantCase: UPPER_CASE diff --git a/.cruft.json b/.cruft.json index ca2a03f..4bec843 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "git@github.com:bl-sdk/common_dotfiles.git", - "commit": "a66f9767ed477bfa89d6ca505392d226ebdd4275", + "commit": "fb06ff8c773806b3f8cc69dbda60c0a7b481c6de", "checkout": null, "context": { "cookiecutter": { diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index 202b04a..29fb895 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -59,10 +59,8 @@ struct AbstractHook { [[nodiscard]] virtual std::wstring uobject_path_name(const unreal::UObject* obj) const = 0; [[nodiscard]] virtual unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const = 0; -#ifdef UE4 virtual void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str) const = 0; -#endif }; #pragma endregion diff --git a/src/unrealsdk/game/bl2/bl2.cpp b/src/unrealsdk/game/bl2/bl2.cpp index 7848d7e..20c8513 100644 --- a/src/unrealsdk/game/bl2/bl2.cpp +++ b/src/unrealsdk/game/bl2/bl2.cpp @@ -15,6 +15,7 @@ #include "unrealsdk/unreal/wrappers/unreal_pointer.h" #include "unrealsdk/unreal/wrappers/unreal_pointer_funcs.h" #include "unrealsdk/unrealsdk.h" +#include "unrealsdk/version_error.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -199,6 +200,15 @@ UObject* BL2Hook::find_object(UClass* cls, const std::wstring& name) const { #pragma endregion +#pragma region FText::AsCultureInvariant + +void BL2Hook::ftext_as_culture_invariant(unreal::FText* /*text*/, + unreal::TemporaryFString&& /*str*/) const { + throw_version_error("FTexts are not implemented in UE3"); +} + +#pragma endregion + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index 670ea14..e90ab51 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -103,6 +103,8 @@ class BL2Hook : public AbstractHook { [[nodiscard]] std::wstring uobject_path_name(const unreal::UObject* obj) const override; [[nodiscard]] unreal::UObject* find_object(unreal::UClass* cls, const std::wstring& name) const override; + void ftext_as_culture_invariant(unreal::FText* text, + unreal::TemporaryFString&& str) const override; }; template <> diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index 6f2d41a..1be879a 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -177,8 +177,6 @@ void uconsole_output_text(std::wstring_view str); [[nodiscard]] unreal::UObject* find_object(const unreal::FName& cls, std::wstring_view name); [[nodiscard]] unreal::UObject* find_object(std::wstring_view cls, std::wstring_view name); -#ifdef UE4 - /** * @brief Calls `FText::AsCultureInvariant`. * @@ -187,8 +185,6 @@ void uconsole_output_text(std::wstring_view str); */ void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str); -#endif - } // namespace unrealsdk #endif /* UNREALSDK_UNREALSDK_H */ diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index 31f8275..9448cef 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -52,9 +52,6 @@ UNREALSDK_CAPI([[nodiscard]] UObject*, UClass* cls, const wchar_t* name, size_t name_size); - -#ifdef UE4 UNREALSDK_CAPI(void, ftext_as_culture_invariant, FText* text, TemporaryFString&& str); -#endif } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index a78e408..3d34e37 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -168,8 +168,6 @@ UNREALSDK_CAPI([[nodiscard]] UObject*, return hook_instance->find_object(cls, {name, name_size}); } -#ifdef UE4 - UNREALSDK_CAPI(void, ftext_as_culture_invariant, unreal::FText* text, @@ -177,8 +175,6 @@ UNREALSDK_CAPI(void, hook_instance->ftext_as_culture_invariant(text, std::move(str)); } -#endif - } // namespace unrealsdk #endif diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index 2713fba..e1cc2cb 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -86,12 +86,8 @@ UObject* find_object(std::wstring_view cls, std::wstring_view name) { return UNREALSDK_MANGLE(find_object)(find_class(cls), name.data(), name.size()); } -#ifdef UE4 - void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str) { UNREALSDK_MANGLE(ftext_as_culture_invariant)(text, std::move(str)); } -#endif - } // namespace unrealsdk From 8d2c31ae182f9f5efa4be574cff18cac7d82d1af Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 26 Dec 2023 13:52:56 +1300 Subject: [PATCH 2/3] split out uobject related hooks for better readability --- src/unrealsdk/game/bl2/bl2.cpp | 109 +----------------------- src/unrealsdk/game/bl2/object.cpp | 119 ++++++++++++++++++++++++++ src/unrealsdk/game/bl3/bl3.cpp | 123 +-------------------------- src/unrealsdk/game/bl3/object.cpp | 135 ++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 227 deletions(-) create mode 100644 src/unrealsdk/game/bl2/object.cpp create mode 100644 src/unrealsdk/game/bl3/object.cpp diff --git a/src/unrealsdk/game/bl2/bl2.cpp b/src/unrealsdk/game/bl2/bl2.cpp index 20c8513..83db6b8 100644 --- a/src/unrealsdk/game/bl2/bl2.cpp +++ b/src/unrealsdk/game/bl2/bl2.cpp @@ -1,20 +1,12 @@ #include "unrealsdk/pch.h" #include "unrealsdk/game/bl2/bl2.h" -#include "unrealsdk/hook_manager.h" #include "unrealsdk/memory.h" -#include "unrealsdk/unreal/classes/properties/uclassproperty.h" -#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" -#include "unrealsdk/unreal/classes/properties/ustrproperty.h" -#include "unrealsdk/unreal/classes/uclass.h" #include "unrealsdk/unreal/classes/uobject.h" #include "unrealsdk/unreal/structs/fframe.h" #include "unrealsdk/unreal/structs/fname.h" -#include "unrealsdk/unreal/wrappers/bound_function.h" -#include "unrealsdk/unreal/wrappers/gobjects.h" -#include "unrealsdk/unreal/wrappers/unreal_pointer.h" -#include "unrealsdk/unreal/wrappers/unreal_pointer_funcs.h" -#include "unrealsdk/unrealsdk.h" +#include "unrealsdk/unreal/structs/fstring.h" +#include "unrealsdk/unreal/structs/ftext.h" #include "unrealsdk/version_error.h" #if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) @@ -103,103 +95,6 @@ void BL2Hook::fframe_step(FFrame* frame, UObject* obj, void* param) const { #pragma endregion -#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{ - "55" // push ebp - "8B EC" // mov ebp, esp - "6A FF" // push -01 - "68 ????????" // push Borderlands2.exe+1107DCB - "64 A1 ????????" // mov eax, fs:[00000000] - "50" // push eax - "83 EC 10" // sub esp, 10 - "53" // push ebx - "56" // push esi - "57" // push edi - "A1 ????????" // mov eax, [Borderlands2.g_LEngineDefaultPoolId+B2DC] - "33 C5" // xor eax, ebp - "50" // push eax - "8D 45 ??" // lea eax, [ebp-0C] - "64 A3 ????????" // mov fs:[00000000], eax - "8B 7D ??" // mov edi, [ebp+08] - "8A 87 ????????" // mov al, [edi+000001CC] -}; - -} // namespace - -void BL2Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); - LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); -} - -UObject* BL2Hook::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 - -#pragma region PathName - -std::wstring BL2Hook::uobject_path_name(const UObject* obj) const { - static UFunction* pathname_func = nullptr; - - // Optimize so we only call find once - if (pathname_func == nullptr) { - pathname_func = obj->Class->find_func_and_validate(L"PathName"_fn); - } - - // Bound functions need mutable references, since they might actually modify the object - // Object properties need mutable references, since you may want to modify the object you get - // We know when calling PathName neither of these apply, so we want this function to explicitly - // take a const reference - meaning we need a cast - // While this is technically undefined behaviour, we only pass the reference around, so this - // should be safe enough - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - auto mutable_obj = const_cast(obj); - - // The hook manager calls this function to work out if to run a hook, so we need to inject next - // call to avoid recursion - hook_manager::inject_next_call(); - return BoundFunction{pathname_func, mutable_obj}.call( - mutable_obj); -} - -#pragma endregion - -#pragma region FindObject - -UObject* BL2Hook::find_object(UClass* cls, const std::wstring& name) const { - static UFunction* findobject_func = nullptr; - - if (findobject_func == nullptr) { - findobject_func = cls->find_func_and_validate(L"FindObject"_fn); - } - - return BoundFunction{findobject_func, cls}.call( - name, cls); -} - -#pragma endregion - #pragma region FText::AsCultureInvariant void BL2Hook::ftext_as_culture_invariant(unreal::FText* /*text*/, diff --git a/src/unrealsdk/game/bl2/object.cpp b/src/unrealsdk/game/bl2/object.cpp new file mode 100644 index 0000000..d28c54a --- /dev/null +++ b/src/unrealsdk/game/bl2/object.cpp @@ -0,0 +1,119 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl2/bl2.h" +#include "unrealsdk/hook_manager.h" +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/classes/properties/uclassproperty.h" +#include "unrealsdk/unreal/classes/properties/uobjectproperty.h" +#include "unrealsdk/unreal/classes/properties/ustrproperty.h" +#include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/wrappers/bound_function.h" + +#if defined(UE3) && defined(ARCH_X86) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::unreal; +using namespace unrealsdk::memory; + +namespace unrealsdk::game { + +#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{ + "55" // push ebp + "8B EC" // mov ebp, esp + "6A FF" // push -01 + "68 ????????" // push Borderlands2.exe+1107DCB + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "83 EC 10" // sub esp, 10 + "53" // push ebx + "56" // push esi + "57" // push edi + "A1 ????????" // mov eax, [Borderlands2.g_LEngineDefaultPoolId+B2DC] + "33 C5" // xor eax, ebp + "50" // push eax + "8D 45 ??" // lea eax, [ebp-0C] + "64 A3 ????????" // mov fs:[00000000], eax + "8B 7D ??" // mov edi, [ebp+08] + "8A 87 ????????" // mov al, [edi+000001CC] +}; + +} // namespace + +void BL2Hook::find_construct_object(void) { + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); + LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); +} + +UObject* BL2Hook::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 + +#pragma region PathName + +std::wstring BL2Hook::uobject_path_name(const UObject* obj) const { + static UFunction* pathname_func = nullptr; + + // Optimize so we only call find once + if (pathname_func == nullptr) { + pathname_func = obj->Class->find_func_and_validate(L"PathName"_fn); + } + + // Bound functions need mutable references, since they might actually modify the object + // Object properties need mutable references, since you may want to modify the object you get + // We know when calling PathName neither of these apply, so we want this function to explicitly + // take a const reference - meaning we need a cast + // While this is technically undefined behaviour, we only pass the reference around, so this + // should be safe enough + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + auto mutable_obj = const_cast(obj); + + // The hook manager calls this function to work out if to run a hook, so we need to inject next + // call to avoid recursion + hook_manager::inject_next_call(); + return BoundFunction{pathname_func, mutable_obj}.call( + mutable_obj); +} + +#pragma endregion + +#pragma region FindObject + +UObject* BL2Hook::find_object(UClass* cls, const std::wstring& name) const { + static UFunction* findobject_func = nullptr; + + if (findobject_func == nullptr) { + findobject_func = cls->find_func_and_validate(L"FindObject"_fn); + } + + return BoundFunction{findobject_func, cls}.call( + name, cls); +} + +#pragma endregion + +} // namespace unrealsdk::game + +#endif diff --git a/src/unrealsdk/game/bl3/bl3.cpp b/src/unrealsdk/game/bl3/bl3.cpp index dd513b5..c3cdb96 100644 --- a/src/unrealsdk/game/bl3/bl3.cpp +++ b/src/unrealsdk/game/bl3/bl3.cpp @@ -1,11 +1,11 @@ #include "unrealsdk/pch.h" - #include "unrealsdk/game/bl3/bl3.h" #include "unrealsdk/memory.h" +#include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/structs/fframe.h" +#include "unrealsdk/unreal/structs/fname.h" #include "unrealsdk/unreal/structs/fstring.h" #include "unrealsdk/unreal/structs/ftext.h" -#include "unrealsdk/unreal/structs/tarray.h" -#include "unrealsdk/unrealsdk.h" #if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) @@ -89,123 +89,6 @@ void BL3Hook::fframe_step(FFrame* frame, UObject* obj, void* param) const { #pragma endregion -#pragma region ConstructObject - -namespace { - -using construct_obj_func = UObject* (*)(UClass* cls, - UObject* obj, - FName name, - uint32_t flags, - uint32_t internal_flags, - UObject* template_obj, - uint32_t copy_transients_from_class_defaults, - void* instance_graph, - uint32_t assume_template_is_archetype); -construct_obj_func construct_obj_ptr; - -const constinit Pattern<55> CONSTRUCT_OBJECT_PATTERN{ - "48 89 5C 24 18" // mov [rsp+18], rbx - "55" // push rbp - "56" // push rsi - "57" // push rdi - "41 54" // push r12 - "41 55" // push r13 - "41 56" // push r14 - "41 57" // push r15 - "48 8D AC 24 ????????" // lea rbp, [rsp-000000C0] - "48 81 EC C0010000" // sub rsp, 000001C0 - "48 8B 05 ????????" // mov rax, [Borderlands3.exe+683B348] - "48 33 C4" // xor rax, rsp - "48 89 85 ????????" // mov [rbp+000000B0], rax - "44 8B A5 ????????" // mov r12d, [rbp+00000120] -}; - -} // namespace - -void BL3Hook::find_construct_object(void) { - construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); - LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); -} - -UObject* BL3Hook::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, 0, template_obj, 0 /* false */, nullptr, - 0 /* false */); -} - -#pragma endregion - -#pragma region PathName - -namespace { - -using get_path_name_func = void (*)(const UObject* self, - const UObject* stop_outer, - ManagedFString* str); -get_path_name_func get_path_name_ptr; - -const constinit Pattern<21> GET_PATH_NAME_PATTERN{ - "48 89 5C 24 ??" // mov [rsp+18], rbx - "48 89 6C 24 ??" // mov [rsp+20], rbp - "57" // push rdi - "48 83 EC 20" // sub rsp, 20 - "49 8B F8" // mov rdi, r8 - "48 8B E9" // mov rbp, rcx -}; - -} // namespace - -void BL3Hook::find_get_path_name(void) { - get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan(); - LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); -} - -std::wstring BL3Hook::uobject_path_name(const UObject* obj) const { - ManagedFString str{}; - get_path_name_ptr(obj, nullptr, &str); - return str; -} - -#pragma endregion - -#pragma region StaticFindObject - -namespace { - -using static_find_object_safe_func = UObject* (*)(const UClass* cls, - intptr_t package, - const wchar_t* str, - uint32_t exact_class); -static_find_object_safe_func static_find_object_ptr; - -const constinit Pattern<27> STATIC_FIND_OBJECT_PATTERN{ - "48 89 5C 24 ??" // mov [rsp+08], rbx - "48 89 6C 24 ??" // mov [rsp+10], rbp - "48 89 74 24 ??" // mov [rsp+18], rsi - "57" // push rdi - "48 83 EC 30" // sub rsp, 30 - "80 3D ???????? 00" // cmp byte ptr [Borderlands3.exe+69EAA10], 00 -}; - -const constexpr intptr_t ANY_PACKAGE = -1; - -} // namespace - -void BL3Hook::find_static_find_object(void) { - static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan(); - LOG(MISC, "StaticFindObjectSafe: {:p}", reinterpret_cast(static_find_object_ptr)); -} - -UObject* BL3Hook::find_object(UClass* cls, const std::wstring& name) const { - return static_find_object_ptr(cls, ANY_PACKAGE, name.c_str(), 0 /* false */); -} - -#pragma endregion - #pragma region FText::AsCultureInvariant namespace { diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp new file mode 100644 index 0000000..9335a59 --- /dev/null +++ b/src/unrealsdk/game/bl3/object.cpp @@ -0,0 +1,135 @@ +#include "unrealsdk/pch.h" +#include "unrealsdk/game/bl3/bl3.h" +#include "unrealsdk/memory.h" +#include "unrealsdk/unreal/classes/uclass.h" +#include "unrealsdk/unreal/classes/uobject.h" +#include "unrealsdk/unreal/structs/fname.h" +#include "unrealsdk/unreal/structs/fstring.h" + +#if defined(UE4) && defined(ARCH_X64) && !defined(UNREALSDK_IMPORTING) + +using namespace unrealsdk::memory; +using namespace unrealsdk::unreal; + +namespace unrealsdk::game { + +#pragma region ConstructObject + +namespace { + +using construct_obj_func = UObject* (*)(UClass* cls, + UObject* obj, + FName name, + uint32_t flags, + uint32_t internal_flags, + UObject* template_obj, + uint32_t copy_transients_from_class_defaults, + void* instance_graph, + uint32_t assume_template_is_archetype); +construct_obj_func construct_obj_ptr; + +const constinit Pattern<55> CONSTRUCT_OBJECT_PATTERN{ + "48 89 5C 24 18" // mov [rsp+18], rbx + "55" // push rbp + "56" // push rsi + "57" // push rdi + "41 54" // push r12 + "41 55" // push r13 + "41 56" // push r14 + "41 57" // push r15 + "48 8D AC 24 ????????" // lea rbp, [rsp-000000C0] + "48 81 EC C0010000" // sub rsp, 000001C0 + "48 8B 05 ????????" // mov rax, [Borderlands3.exe+683B348] + "48 33 C4" // xor rax, rsp + "48 89 85 ????????" // mov [rbp+000000B0], rax + "44 8B A5 ????????" // mov r12d, [rbp+00000120] +}; + +} // namespace + +void BL3Hook::find_construct_object(void) { + construct_obj_ptr = CONSTRUCT_OBJECT_PATTERN.sigscan(); + LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast(construct_obj_ptr)); +} + +UObject* BL3Hook::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, 0, template_obj, 0 /* false */, nullptr, + 0 /* false */); +} + +#pragma endregion + +#pragma region PathName + +namespace { + +using get_path_name_func = void (*)(const UObject* self, + const UObject* stop_outer, + ManagedFString* str); +get_path_name_func get_path_name_ptr; + +const constinit Pattern<21> GET_PATH_NAME_PATTERN{ + "48 89 5C 24 ??" // mov [rsp+18], rbx + "48 89 6C 24 ??" // mov [rsp+20], rbp + "57" // push rdi + "48 83 EC 20" // sub rsp, 20 + "49 8B F8" // mov rdi, r8 + "48 8B E9" // mov rbp, rcx +}; + +} // namespace + +void BL3Hook::find_get_path_name(void) { + get_path_name_ptr = GET_PATH_NAME_PATTERN.sigscan(); + LOG(MISC, "GetPathName: {:p}", reinterpret_cast(get_path_name_ptr)); +} + +std::wstring BL3Hook::uobject_path_name(const UObject* obj) const { + ManagedFString str{}; + get_path_name_ptr(obj, nullptr, &str); + return str; +} + +#pragma endregion + +#pragma region StaticFindObject + +namespace { + +using static_find_object_safe_func = UObject* (*)(const UClass* cls, + intptr_t package, + const wchar_t* str, + uint32_t exact_class); +static_find_object_safe_func static_find_object_ptr; + +const constinit Pattern<27> STATIC_FIND_OBJECT_PATTERN{ + "48 89 5C 24 ??" // mov [rsp+08], rbx + "48 89 6C 24 ??" // mov [rsp+10], rbp + "48 89 74 24 ??" // mov [rsp+18], rsi + "57" // push rdi + "48 83 EC 30" // sub rsp, 30 + "80 3D ???????? 00" // cmp byte ptr [Borderlands3.exe+69EAA10], 00 +}; + +const constexpr intptr_t ANY_PACKAGE = -1; + +} // namespace + +void BL3Hook::find_static_find_object(void) { + static_find_object_ptr = STATIC_FIND_OBJECT_PATTERN.sigscan(); + LOG(MISC, "StaticFindObjectSafe: {:p}", reinterpret_cast(static_find_object_ptr)); +} + +UObject* BL3Hook::find_object(UClass* cls, const std::wstring& name) const { + return static_find_object_ptr(cls, ANY_PACKAGE, name.c_str(), 0 /* false */); +} + +#pragma endregion + +} // namespace unrealsdk::game + +#endif From 70245fac3376f88c1f03762b1a4ce2a9b357cff6 Mon Sep 17 00:00:00 2001 From: apple1417 Date: Tue, 26 Dec 2023 18:00:02 +1300 Subject: [PATCH 3/3] support load_package --- src/unrealsdk/game/abstract_hook.h | 2 ++ src/unrealsdk/game/bl2/bl2.cpp | 1 + src/unrealsdk/game/bl2/bl2.h | 7 +++++ src/unrealsdk/game/bl2/object.cpp | 40 ++++++++++++++++++++++++++++ src/unrealsdk/game/bl3/bl3.cpp | 1 + src/unrealsdk/game/bl3/bl3.h | 7 +++++ src/unrealsdk/game/bl3/object.cpp | 32 ++++++++++++++++++++++ src/unrealsdk/unrealsdk.h | 11 ++++++++ src/unrealsdk/unrealsdk_fw.inl | 5 ++++ src/unrealsdk/unrealsdk_main.cpp | 8 ++++++ src/unrealsdk/unrealsdk_wrappers.cpp | 4 +++ 11 files changed, 118 insertions(+) diff --git a/src/unrealsdk/game/abstract_hook.h b/src/unrealsdk/game/abstract_hook.h index 29fb895..85388e9 100644 --- a/src/unrealsdk/game/abstract_hook.h +++ b/src/unrealsdk/game/abstract_hook.h @@ -61,6 +61,8 @@ struct AbstractHook { const std::wstring& name) const = 0; virtual void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str) const = 0; + [[nodiscard]] virtual unreal::UObject* load_package(const std::wstring& name, + uint32_t flags) const = 0; }; #pragma endregion diff --git a/src/unrealsdk/game/bl2/bl2.cpp b/src/unrealsdk/game/bl2/bl2.cpp index 83db6b8..8022228 100644 --- a/src/unrealsdk/game/bl2/bl2.cpp +++ b/src/unrealsdk/game/bl2/bl2.cpp @@ -29,6 +29,7 @@ void BL2Hook::hook(void) { find_fframe_step(); find_gmalloc(); find_construct_object(); + find_load_package(); inject_console(); diff --git a/src/unrealsdk/game/bl2/bl2.h b/src/unrealsdk/game/bl2/bl2.h index e90ab51..df844ff 100644 --- a/src/unrealsdk/game/bl2/bl2.h +++ b/src/unrealsdk/game/bl2/bl2.h @@ -75,6 +75,11 @@ class BL2Hook : public AbstractHook { */ static void find_construct_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. */ @@ -105,6 +110,8 @@ class BL2Hook : public AbstractHook { const std::wstring& name) const override; void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str) const override; + [[nodiscard]] unreal::UObject* load_package(const std::wstring& name, + uint32_t flags) const override; }; template <> diff --git a/src/unrealsdk/game/bl2/object.cpp b/src/unrealsdk/game/bl2/object.cpp index d28c54a..fa4c339 100644 --- a/src/unrealsdk/game/bl2/object.cpp +++ b/src/unrealsdk/game/bl2/object.cpp @@ -114,6 +114,46 @@ UObject* BL2Hook::find_object(UClass* cls, const std::wstring& name) const { #pragma endregion +#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<46> LOAD_PACKAGE_PATTERN{ + "55" // push ebp + "8B EC" // mov ebp, esp + "6A FF" // push -01 + "68 ????????" // push Borderlands2.exe+1108180 + "64 A1 ????????" // mov eax, fs:[00000000] + "50" // push eax + "83 EC 68" // sub esp, 68 + "A1 ????????" // mov eax, [Borderlands2.g_LEngineDefaultPoolId+B2DC] + "33 C5" // xor eax, ebp + "89 45 ??" // mov [ebp-14], eax + "53" // push ebx + "56" // push esi + "57" // push edi + "50" // push eax + "8D 45 ??" // lea eax, [ebp-0C] + "64 A3 ????????" // mov fs:[00000000], eax + "89 65 ??" // mov [ebp-10], esp +}; + +} // namespace + +void BL2Hook::find_load_package(void) { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan(); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); +} + +[[nodiscard]] UObject* BL2Hook::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/bl3/bl3.cpp b/src/unrealsdk/game/bl3/bl3.cpp index c3cdb96..b2efdb1 100644 --- a/src/unrealsdk/game/bl3/bl3.cpp +++ b/src/unrealsdk/game/bl3/bl3.cpp @@ -27,6 +27,7 @@ void BL3Hook::hook(void) { find_get_path_name(); find_static_find_object(); find_ftext_as_culture_invariant(); + find_load_package(); inject_console(); } diff --git a/src/unrealsdk/game/bl3/bl3.h b/src/unrealsdk/game/bl3/bl3.h index 220611f..e1c52e2 100644 --- a/src/unrealsdk/game/bl3/bl3.h +++ b/src/unrealsdk/game/bl3/bl3.h @@ -68,6 +68,11 @@ class BL3Hook : public AbstractHook { */ static void find_ftext_as_culture_invariant(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. */ @@ -98,6 +103,8 @@ class BL3Hook : public AbstractHook { const std::wstring& name) const override; void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str) const override; + [[nodiscard]] unreal::UObject* load_package(const std::wstring& name, + uint32_t flags) const override; }; template <> diff --git a/src/unrealsdk/game/bl3/object.cpp b/src/unrealsdk/game/bl3/object.cpp index 9335a59..eca6a0a 100644 --- a/src/unrealsdk/game/bl3/object.cpp +++ b/src/unrealsdk/game/bl3/object.cpp @@ -130,6 +130,38 @@ UObject* BL3Hook::find_object(UClass* cls, const std::wstring& name) const { #pragma endregion +#pragma region LoadPackage + +namespace { + +using load_package_func = UObject* (*)(const UObject* outer, + const wchar_t* name, + uint32_t flags, + void* reader_override); +load_package_func load_package_ptr; + +const constinit Pattern<16> LOAD_PACKAGE_PATTERN{ + "48 8B C4" // mov rax, rsp + "53" // push rbx + "56" // push rsi + "48 83 EC 68" // sub rsp, 68 + "48 89 68 ??" // mov [rax+08], rbp + "48 8B EA" // mov rbp, rdx +}; + +} // namespace + +void BL3Hook::find_load_package(void) { + load_package_ptr = LOAD_PACKAGE_PATTERN.sigscan(); + LOG(MISC, "LoadPackage: {:p}", reinterpret_cast(load_package_ptr)); +} + +[[nodiscard]] UObject* BL3Hook::load_package(const std::wstring& name, uint32_t flags) const { + return load_package_ptr(nullptr, name.data(), flags, nullptr); +} + +#pragma endregion + } // namespace unrealsdk::game #endif diff --git a/src/unrealsdk/unrealsdk.h b/src/unrealsdk/unrealsdk.h index 1be879a..a8ed903 100644 --- a/src/unrealsdk/unrealsdk.h +++ b/src/unrealsdk/unrealsdk.h @@ -185,6 +185,17 @@ void uconsole_output_text(std::wstring_view str); */ void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& str); +/** + * @brief Loads a package, and all it's contained objects. + * @note This function may block for several seconds while the package is loaded. + * + * @param name The package's name. + * @param flags The loading flags to use. + * @return The loaded `Package` object. + */ +// NOLINTNEXTLINE(modernize-use-nodiscard) +unreal::UObject* load_package(std::wstring_view name, uint32_t flags = 0); + } // namespace unrealsdk #endif /* UNREALSDK_UNREALSDK_H */ diff --git a/src/unrealsdk/unrealsdk_fw.inl b/src/unrealsdk/unrealsdk_fw.inl index 9448cef..26721ff 100644 --- a/src/unrealsdk/unrealsdk_fw.inl +++ b/src/unrealsdk/unrealsdk_fw.inl @@ -53,5 +53,10 @@ UNREALSDK_CAPI([[nodiscard]] UObject*, const wchar_t* name, size_t name_size); UNREALSDK_CAPI(void, ftext_as_culture_invariant, FText* text, TemporaryFString&& str); +UNREALSDK_CAPI([[nodiscard]] UObject*, + load_package, + const wchar_t* name, + size_t size, + uint32_t flags); } // namespace unrealsdk diff --git a/src/unrealsdk/unrealsdk_main.cpp b/src/unrealsdk/unrealsdk_main.cpp index 3d34e37..538e37f 100644 --- a/src/unrealsdk/unrealsdk_main.cpp +++ b/src/unrealsdk/unrealsdk_main.cpp @@ -175,6 +175,14 @@ UNREALSDK_CAPI(void, hook_instance->ftext_as_culture_invariant(text, std::move(str)); } +UNREALSDK_CAPI([[nodiscard]] UObject*, + load_package, + const wchar_t* name, + size_t size, + uint32_t flags) { + return hook_instance->load_package({name, size}, flags); +} + } // namespace unrealsdk #endif diff --git a/src/unrealsdk/unrealsdk_wrappers.cpp b/src/unrealsdk/unrealsdk_wrappers.cpp index e1cc2cb..946b52f 100644 --- a/src/unrealsdk/unrealsdk_wrappers.cpp +++ b/src/unrealsdk/unrealsdk_wrappers.cpp @@ -90,4 +90,8 @@ void ftext_as_culture_invariant(unreal::FText* text, unreal::TemporaryFString&& UNREALSDK_MANGLE(ftext_as_culture_invariant)(text, std::move(str)); } +UObject* load_package(std::wstring_view name, uint32_t flags) { + return UNREALSDK_MANGLE(load_package)(name.data(), name.size(), flags); +} + } // namespace unrealsdk