From 411dd30323dbd54b407890c786ee7272c1acfb2b Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Thu, 19 Mar 2026 19:00:00 +0100 Subject: [PATCH] Add method to retrieve player Pawn from actor --- ClairObscur/dllmain.cpp | 76 ++++++++++++++-------------------------- libs/UEngine/UETools.cpp | 11 ++++++ libs/UEngine/UETools.hpp | 8 +++++ 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/ClairObscur/dllmain.cpp b/ClairObscur/dllmain.cpp index d94c0c6..41d0f2e 100644 --- a/ClairObscur/dllmain.cpp +++ b/ClairObscur/dllmain.cpp @@ -7,6 +7,7 @@ #include "SDK/Basic.hpp" #include "SDK/Engine_classes.hpp" #include "SDK/SandFall_classes.hpp" +#include "SDK/BP_jRPG_GM_Bootstrap_classes.hpp" #include "SDK/BP_jRPG_Character_World_classes.hpp" #include "SDK/BP_jRPG_Character_Battle_Base_classes.hpp" #include "SDK/AC_jRPG_CharacterBattleStats_classes.hpp" @@ -21,6 +22,8 @@ #include "SDK/WBP_Merchant_Panel_classes.hpp" #include "SDK/WBP_WidescreenRatioBox_classes.hpp" #include "SDK/WBP_DialogNotifBox_classes.hpp" +#include "SDK/WBP_PromptContainer_classes.hpp" +#include "SDK/WBP_Exploration_HUD_classes.hpp" using namespace SDK; @@ -75,7 +78,6 @@ static float g_Camera_In = 280.f; static float g_Camera_Out = 280.f; // AOB Scan pointers -static uint8_t* Disclaimeraddress = nullptr; static uint8_t* FOVaddress = nullptr; static uint8_t* CameraComponentaddress = nullptr; static uint8_t* Cameraaddress = nullptr; @@ -89,7 +91,6 @@ static uint8_t* CameraStructaddress = nullptr; // Hooking static SafetyHookMid FOVHook{}; -static SafetyHookMid ATVPHook{}; static SafetyHookMid CameraHook{}; static SafetyHookMid CutscenesFPSHook{}; static SafetyHookMid PEHook{}; @@ -109,7 +110,6 @@ static void FogFixEnabled(); static void EnableConsole(); static void EnableCheats(Cheat cheat); static void ProcessEvent(); -static void ATVPFixEnabled(); extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { g_fix_enabled = enabled; @@ -120,7 +120,6 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { constexpr auto CutscenesFPSStringObfuscated = make_obfuscated<0x0F>("EB ?? 0F ?? ?? 48 8B ?? ?? ?? 0F 28 ?? ?? ?? 44 0F ?? ?? ?? ?? 44 0F"); constexpr auto CameraStruct1StringObfuscated = make_obfuscated<0x48>("48 89 ?? ?? ?? 57 48 83 ?? ?? 0F ?? ?? 48 8B FA 48 ?? ?? 0F ?? ?? F2 0F 10"); constexpr auto CameraStringObfuscated = make_obfuscated<0x28>("F2 0F 10 ?? ?? ?? 0F 28 ?? ?? ?? ?? ?? F2 0F ?? ?? ?? F2 0F ?? ?? ?? 0F ?? ?? 0F"); - constexpr auto ATVPStringObfuscated = make_obfuscated<0x59>("48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 81 EC ?? ?? ?? ?? 33 FF 48 ?? ?? 89 BC 24"); constexpr auto CAStringObfuscated = make_obfuscated<0x39>("7F ?? 44 89 ?? ?? ?? ?? ?? 43 8B ?? ?? 39 05 ?? ?? ?? ?? 0F 8F"); constexpr auto DOFStringObfuscated = make_obfuscated<0xC1>("8B ?? ?? 48 ?? ?? E8 ?? ?? ?? ?? 0F ?? ?? 48 6B ?? ?? 48 8D"); constexpr auto FogStringObfuscated = make_obfuscated<0x75>("74 ?? 48 8B ?? ?? ?? ?? ?? 83 ?? ?? ?? 75 ?? 40 ?? ?? EB ?? 40 ?? ?? 48 8B"); @@ -132,7 +131,6 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { // Prepare all data for scanning std::vector signatures = { Make(&CameraComponentaddress, CameraComponentStringObfuscated, "Camera component"), - Make(&Disclaimeraddress, ATVPStringObfuscated, "Disclaimers"), Make(&FOVaddress, FOVStringObfuscated, "FOV"), Make(&CutscenesFPSaddress, CutscenesFPSStringObfuscated, "FPS unlock"), Make(&CameraStructaddress, CameraStruct1StringObfuscated, "Camera struct"), @@ -146,8 +144,8 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { // Scan all signature in a batch Memory::AOBScanBatch(signatures, logger); - if (FOVaddress && DOFaddress && Fogaddress && WorldTimedilationaddress && Timedilationaddress && CutscenesFPSaddress && - Disclaimeraddress && CAaddress && CameraComponentaddress && CameraStructaddress && Cameraaddress) + if (FOVaddress && DOFaddress && Fogaddress && WorldTimedilationaddress && Timedilationaddress && + CutscenesFPSaddress && CAaddress && CameraComponentaddress && CameraStructaddress && Cameraaddress) logger->info("All AOB signatures found. Ready to patch..."); if (!GObjectsaddress || !AppendStringaddress || !ProcessEventaddress) { @@ -155,12 +153,14 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { uint8_t* baseModule = reinterpret_cast(GetModuleHandleA(nullptr)); // Get game base address constexpr auto GObjetcsStringObfuscated = make_obfuscated<0x8D>("48 8B ?? ?? ?? ?? ?? 48 8B ?? ?? 48 8D ?? ?? EB ?? 33"); + constexpr auto GWorldStringObfuscated = make_obfuscated<0x8D>("48 89 1D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 83 ?? ?? 5B C3 48 8D 0D"); constexpr auto AppendStringStringObfuscated = make_obfuscated<0x80>("48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 ?? ?? 80 3D ?? ?? ?? ?? ?? 48 ?? F2 8B ?? 48 ?? ?? 74 ?? 4C 8D ?? ?? ?? ?? ?? EB ?? 48 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C"); constexpr auto ProcessEventStringObfuscated = make_obfuscated<0x56>("40 ?? 56 57 41 ?? 41 ?? 41 ?? 41 ?? 48 81 ?? ?? ?? ?? ?? 48 8D ?? ?? ?? 48 89 ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 ?? ?? 48 89 ?? ?? ?? ?? ?? 4D"); // Prepare all data for scanning std::vector UEoffsetsScans = { Make(&GObjectsaddress, GObjetcsStringObfuscated, "GObjects", OffsetCalcType::GetOffsetFromOpcode, &Offsets::GObjects, 0x3), + Make(&GWorldaddress, GWorldStringObfuscated, "Gworld", OffsetCalcType::GetOffsetFromOpcode, &Offsets::GWorld, 0x3), Make(&AppendStringaddress, AppendStringStringObfuscated, "AppendString", OffsetCalcType::UE_CalculateOffset, &Offsets::AppendString), Make(&ProcessEventaddress, ProcessEventStringObfuscated, "ProcessEvent", OffsetCalcType::UE_CalculateOffset, &Offsets::ProcessEvent) }; @@ -179,13 +179,7 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { if (!init && DOFaddress) DOFFixEnabled(); if (!init && CAaddress) CAFixEnabled(); if (!init && Fogaddress) FogFixEnabled(); - if (!init && WorldTimedilationaddress) { - EnableCheats(Cheat::TimeDilation); - EnableCheats(Cheat::GodMode); - EnableCheats(Cheat::Stealth); - } ProcessEvent(); - ATVPFixEnabled(); } // Setters for Reshade addon call @@ -237,15 +231,18 @@ extern "C" __declspec(dllexport) void GetGameInfos(GameInfos* infos) { } // Code injection functions -static UUserWidget* epilepsyWarning = nullptr; static UUserWidget* g_PopupWidget = nullptr; +static UUserWidget* g_ExplorationHUDWidget = nullptr; +static UUserWidget* g_PromptWidget = nullptr; static USizeBox* g_gameMenu = nullptr; static USizeBox* g_characterMenu = nullptr; static USizeBox* g_MainMenu = nullptr; static USizeBox* g_HUDBattle = nullptr; static USizeBox* g_merchantPanel = nullptr; static USizeBox* g_pauseMenu = nullptr; +static ABP_jRPG_GM_Bootstrap_C* bootStrap = nullptr; static ULONGLONG lastScanTick = 0; // Last time tick was called +static UC::int32 NAME_FindPlayerStart = -1; static void ProcessEvent() { if (!PEHook && ProcessEventaddress) { PEHook = safetyhook::create_mid(ProcessEventaddress + 0xc, @@ -262,16 +259,16 @@ static void ProcessEvent() { g_AspectRatio = (float)screenWidth / screenHeight; } } + if (!object || !func) return; + UC::int32 comparisonIndex = func->Name.ComparisonIndex; + if (NAME_FindPlayerStart == -1 && func->Name.ToString() == "FindPlayerStart") NAME_FindPlayerStart = comparisonIndex; - FString FfuncName; - FString FobjectName; - reinterpret_cast(FName::AppendString)(&func->Name, FfuncName); - reinterpret_cast(FName::AppendString)(&object->Name, FobjectName); - std::string funcName = FfuncName.ToString(); - std::string objectName = FobjectName.ToString(); - - if (object->IsA(UUserWidget::StaticClass())) { + if (!bootStrap && NAME_FindPlayerStart == comparisonIndex && object->IsA(ABP_jRPG_GM_Bootstrap_C::StaticClass())) + bootStrap = static_cast(object); // find bootstrap to skip disclaimers + else if (object->IsA(UUserWidget::StaticClass())) { + std::string funcName = func->GetName(); + std::string objectName = object->GetName(); if (funcName == "Construct" || funcName == "Destruct" || funcName == "BP_OnActivated" || funcName == "BP_OnDeactivated") { auto HandleWidget = [&](auto* typedWidget, auto*& gWidgetPtr, bool forceModifyFOV = false) { // Lambda to initialize pointers on Construct and nullify them on Destruct if (funcName == "Construct") { @@ -284,18 +281,14 @@ static void ProcessEvent() { if (funcName == "BP_OnDeactivated") bHasToModifyFOV = true; }; - if (g_SkipIntros_enabled && objectName.rfind("WBP_SplashScreen_Epilepsy_C", 0) == 0) { - if (funcName == "Construct" && epilepsyWarning) { - UWorld* world = UWorld::GetWorld(); - if (world && world->OwningGameInstance) { - auto* gameMode = world->AuthorityGameMode; - UFunction* function = gameMode->Class->GetFunction("BP_jRPG_GM_Bootstrap_C", "LoadMainMenuLevel"); // Skip to main menu - if (function) gameMode->ProcessEvent(function, nullptr); - } - } - } + if (g_SkipIntros_enabled && funcName == "Construct" && bootStrap && objectName.rfind("WBP_SplashScreen_Epilepsy_C", 0) == 0) + bootStrap->LoadMainMenuLevel(); // Skip intros else if (object->IsA(UWBP_DialogNotifBox_C::StaticClass())) HandleWidget(static_cast(object), g_PopupWidget); + else if (object->IsA(UWBP_Exploration_HUD_C::StaticClass())) { + HandleWidget(static_cast(object)->WBP_PromptContainer, g_PromptWidget, true); + HandleWidget(static_cast(object), g_ExplorationHUDWidget, true); + } else if (object->IsA(UWBP_HUD_BattleScreen_C::StaticClass())) HandleWidget(static_cast(object)->WBP_WidescreenRatioBox->ConstrainSizeBox, g_HUDBattle, true); else if (object->IsA(UWBP_GameMenu_v3_C::StaticClass())) @@ -330,23 +323,8 @@ static void HUDUpdate(bool writeLog) { if (g_merchantPanel) { g_merchantPanel->SetMinAspectRatio(g_UIAspect); g_merchantPanel->SetMaxAspectRatio(g_UIAspect); } if (g_gameMenu) { g_gameMenu->SetMinAspectRatio(g_UIAspect); g_gameMenu->SetMaxAspectRatio(g_UIAspect); } ApplyTransformOffset(g_PopupWidget, -HUDoffset); -} - -static void ATVPFixEnabled() { - if (Disclaimeraddress) { - if (!ATVPHook) { // Hook only once - ATVPHook = safetyhook::create_mid(Disclaimeraddress, - [](SafetyHookContext& ctx) { - if (!ctx.rcx) return; - UObject* widget = reinterpret_cast(ctx.rcx); - std::string widgetName = widget->GetName(); - if (widgetName.contains("WBP_SplashScreen_Epilepsy_C")) { - auto* epilepsy = static_cast(widget); - if (epilepsy) epilepsyWarning = epilepsy; // GetWidget pointer to skip intro in ProcessEvent - } - }); - } - } + ApplyTransformOffset(g_PromptWidget, -HUDoffset * ((float)1080 / screenHeight)); + CenterWidget(g_ExplorationHUDWidget, HUDoffset, screenWidth, screenHeight, 1920, 1080); } static void FOVFixEnabled() { diff --git a/libs/UEngine/UETools.cpp b/libs/UEngine/UETools.cpp index d1994b2..5ce3653 100644 --- a/libs/UEngine/UETools.cpp +++ b/libs/UEngine/UETools.cpp @@ -30,6 +30,17 @@ SDK::APawn* GetPawnFromWorld(SDK::UWorld* world) { return pawn; } + +SDK::APawn* GetPawnFromObject(SDK::UObject* object) { + auto* actor = static_cast (object); + if (!actor || !actor->Class || !actor->IsA(SDK::APawn::StaticClass())) return nullptr; + auto* pawn = static_cast(actor); + if (!pawn) return nullptr; + auto* controller = pawn->Controller; + if (!controller || !controller->IsA(SDK::APlayerController::StaticClass())) return nullptr; + + return pawn; +} // Console void ReactivateDevConsole(std::shared_ptr logger) { g_logger = logger; diff --git a/libs/UEngine/UETools.hpp b/libs/UEngine/UETools.hpp index 1cf1249..defaa6d 100644 --- a/libs/UEngine/UETools.hpp +++ b/libs/UEngine/UETools.hpp @@ -9,6 +9,7 @@ namespace SDK { class UObject; class UWorld; class UWidget; + class AActor; class APawn; class UGameplayStatics; class UConsole; @@ -35,6 +36,13 @@ void GetResolution(int& outWidth, int& outHeight, float& outAspectRatio); */ SDK::APawn* GetPawnFromWorld(SDK::UWorld* world = nullptr); +/** + * @brief Gets the current player Pawn from an actor object. + * @param object pointer (must be an UObject at least). + * @return Player Pawn or nullptr. + */ +SDK::APawn* GetPawnFromObject(SDK::UObject* object); + /** * @brief Reactivate the development console. * @param logger spdlog.