Add method to retrieve player Pawn from actor

This commit is contained in:
2026-03-19 19:00:00 +01:00
parent a983681ee8
commit 411dd30323
3 changed files with 46 additions and 49 deletions

View File

@@ -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<AOBScanEntry> 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<uint8_t*>(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<OffsetScanEntry> 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<void(*)(const FName*, FString&)>(FName::AppendString)(&func->Name, FfuncName);
reinterpret_cast<void(*)(const FName*, FString&)>(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<ABP_jRPG_GM_Bootstrap_C*>(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<UUserWidget*>(object), g_PopupWidget);
else if (object->IsA(UWBP_Exploration_HUD_C::StaticClass())) {
HandleWidget(static_cast<UWBP_Exploration_HUD_C*>(object)->WBP_PromptContainer, g_PromptWidget, true);
HandleWidget(static_cast<UWBP_Exploration_HUD_C*>(object), g_ExplorationHUDWidget, true);
}
else if (object->IsA(UWBP_HUD_BattleScreen_C::StaticClass()))
HandleWidget(static_cast<UWBP_HUD_BattleScreen_C*>(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<UObject*>(ctx.rcx);
std::string widgetName = widget->GetName();
if (widgetName.contains("WBP_SplashScreen_Epilepsy_C")) {
auto* epilepsy = static_cast<UUserWidget*>(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() {

View File

@@ -30,6 +30,17 @@ SDK::APawn* GetPawnFromWorld(SDK::UWorld* world) {
return pawn;
}
SDK::APawn* GetPawnFromObject(SDK::UObject* object) {
auto* actor = static_cast<SDK::AActor*> (object);
if (!actor || !actor->Class || !actor->IsA(SDK::APawn::StaticClass())) return nullptr;
auto* pawn = static_cast<SDK::APawn*>(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<spdlog::logger> logger) {
g_logger = logger;

View File

@@ -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.