Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support load package + misc cleanup #30

Merged
merged 3 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .cruft.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"template": "[email protected]:bl-sdk/common_dotfiles.git",
"commit": "a66f9767ed477bfa89d6ca505392d226ebdd4275",
"commit": "fb06ff8c773806b3f8cc69dbda60c0a7b481c6de",
"checkout": null,
"context": {
"cookiecutter": {
Expand Down
4 changes: 2 additions & 2 deletions src/unrealsdk/game/abstract_hook.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ 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
[[nodiscard]] virtual unreal::UObject* load_package(const std::wstring& name,
uint32_t flags) const = 0;
};

#pragma endregion
Expand Down
110 changes: 8 additions & 102 deletions src/unrealsdk/game/bl2/bl2.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
#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)

Expand All @@ -36,6 +29,7 @@ void BL2Hook::hook(void) {
find_fframe_step();
find_gmalloc();
find_construct_object();
find_load_package();

inject_console();

Expand Down Expand Up @@ -102,99 +96,11 @@ void BL2Hook::fframe_step(FFrame* frame, UObject* obj, void* param) const {

#pragma endregion

#pragma region ConstructObject
#pragma region FText::AsCultureInvariant

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<construct_obj_func>();
LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast<void*>(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<UObject*>(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<UStrProperty, UObjectProperty>(
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<UObjectProperty, UStrProperty, UClassProperty>(
name, cls);
void BL2Hook::ftext_as_culture_invariant(unreal::FText* /*text*/,
unreal::TemporaryFString&& /*str*/) const {
throw_version_error("FTexts are not implemented in UE3");
}

#pragma endregion
Expand Down
9 changes: 9 additions & 0 deletions src/unrealsdk/game/bl2/bl2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -103,6 +108,10 @@ 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;
[[nodiscard]] unreal::UObject* load_package(const std::wstring& name,
uint32_t flags) const override;
};

template <>
Expand Down
159 changes: 159 additions & 0 deletions src/unrealsdk/game/bl2/object.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#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<construct_obj_func>();
LOG(MISC, "StaticConstructObject: {:p}", reinterpret_cast<void*>(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<UObject*>(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<UStrProperty, UObjectProperty>(
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<UObjectProperty, UStrProperty, UClassProperty>(
name, cls);
}

#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<load_package_func>();
LOG(MISC, "LoadPackage: {:p}", reinterpret_cast<void*>(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
Loading
Loading