2026-03-18 18:14:50 +01:00
# include " CommonHeaders.h "
2026-04-04 17:58:37 +02:00
# include "CommonUEHeaders.h"
2026-03-22 17:29:43 +01:00
# include "SDK.h"
2026-03-18 18:14:50 +01:00
using namespace SDK ;
// Constants
const std : : string PLUGIN_NAME = " ClairObscur " ;
const std : : string PLUGIN_LOG = PLUGIN_NAME + " .log " ;
constexpr ULONGLONG DEFAULT_ACTORS_SCAN_BETWEEN_TICKS = 100 ; // Used for enemies time dilation
constexpr ULONGLONG DEFAULT_DELAY_BETWEEN_TICK = 500 ; // Used for retrieving Game resolution
// Logger
std : : shared_ptr < spdlog : : logger > logger ;
// Screen informations
static int screenWidth = GetSystemMetrics ( SM_CXSCREEN ) ;
static int screenHeight = GetSystemMetrics ( SM_CYSCREEN ) ;
static int maxScreeHeight = screenHeight ;
static float g_AspectRatio = ( float ) screenWidth / screenHeight ;
float g_BaseAspectRatio = 1.777778f ;
// Plugin states
static bool AOBScanDone = false ;
static bool g_Console = false ;
static std : : atomic < bool > g_fix_enabled = false ;
static std : : atomic < bool > g_fov_fix_enabled = false ;
static std : : atomic < bool > g_ultrawide_fix_enabled = false ;
static std : : atomic < bool > g_CutscenesFPS_fix_enabled = false ;
static std : : atomic < bool > g_HUD_fix_enabled = false ;
static std : : atomic < bool > g_camera_fix_enabled = false ;
static std : : atomic < bool > g_DOF_fix_enabled = false ;
static std : : atomic < bool > g_CA_fix_enabled = false ;
static std : : atomic < bool > g_Fog_fix_enabled = false ;
static std : : atomic < bool > g_TimeDilation_fix_enabled = false ;
static std : : atomic < bool > g_GodMode_fix_enabled = false ;
static std : : atomic < bool > g_Stealth_fix_enabled = false ;
static std : : atomic < bool > g_SkipIntros_enabled = false ;
static int g_AdditionalFOVValue = 0 ;
static float g_cameraDistanceMultiplier = 1.f ;
static int g_HUDOffsets = 0 ;
static int g_UIOffsets = 0 ;
static float g_HUDAspect = g_BaseAspectRatio ;
static float g_UIAspect = g_BaseAspectRatio ;
static float g_WorldTimeDilationValue = 1.f ;
static float g_AITimeDilationValue = 1.f ;
static bool user_inputs_logged = false ;
static bool bHasToModifyFOV = true ;
// Shared values
static float g_FOV_In = 80.f ;
static float g_CompensatedFOV = 80.f ;
static float g_FOV_Out = 80.f ;
static float g_Camera_In = 280.f ;
static float g_Camera_Out = 280.f ;
// AOB Scan pointers
static uint8_t * FOVaddress = nullptr ;
static uint8_t * CameraComponentaddress = nullptr ;
static uint8_t * Cameraaddress = nullptr ;
2026-03-22 17:29:43 +01:00
static uint8_t * CutscenesFPSaddress1 = nullptr ;
static uint8_t * CutscenesFPSaddress2 = nullptr ;
2026-03-18 18:14:50 +01:00
static uint8_t * WorldTimedilationaddress = nullptr ;
static uint8_t * Timedilationaddress = nullptr ;
static uint8_t * CameraStructaddress = nullptr ;
// Hooking
static SafetyHookMid FOVHook { } ;
static SafetyHookMid CameraHook { } ;
static SafetyHookMid CutscenesFPSHook { } ;
static SafetyHookMid PEHook { } ;
static SafetyHookMid WorldTimeDilationHook { } ;
static SafetyHookMid TimeDilationHook { } ;
static SafetyHookMid UltraWideHook { } ;
// Prototypes
static void FOVFixEnabled ( ) ;
2026-04-04 18:07:02 +02:00
static void UltraWideFixEnabled ( ) ;
2026-03-18 18:14:50 +01:00
static void CameraFixEnabled ( ) ;
static void CutscenesFPSFixEnabled ( ) ;
static void HUDUpdate ( bool writeLog ) ;
static void EnableConsole ( ) ;
static void EnableCheats ( Cheat cheat ) ;
static void ProcessEvent ( ) ;
extern " C " __declspec ( dllexport ) void SetFixEnabled ( bool enabled , bool init ) {
g_fix_enabled = enabled ;
2026-04-04 17:58:37 +02:00
if ( init ) {
logger = InitializeLogger ( " Clair Obscur: Expedition 33 " , PLUGIN_LOG ) ;
logger - > info ( " Plugin {} loaded. " , PLUGIN_NAME ) ;
}
2026-03-18 18:14:50 +01:00
if ( ! AOBScanDone ) { // Unreal Engine 5.4.4
logger - > info ( " --------------- AOB scan started --------------- " ) ;
constexpr auto CameraComponentStringObfuscated = make_obfuscated < 0xF3 > ( " EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 8B 83 " ) ;
constexpr auto FOVStringObfuscated = make_obfuscated < 0xF3 > ( " 77 ?? 48 ?? ?? FF 90 ?? ?? ?? ?? F3 0F ?? ?? ?? 48 83 ?? ?? C3 " ) ;
2026-03-22 17:29:43 +01:00
constexpr auto CutscenesFPS1StringObfuscated = make_obfuscated < 0x0F > ( " EB ?? 0F ?? ?? 48 8B ?? ?? ?? 0F 28 ?? ?? ?? 44 0F ?? ?? ?? ?? 44 0F " ) ;
constexpr auto CutscenesFPS2StringObfuscated = make_obfuscated < 0x0F > ( " 0F 84 ?? ?? ?? ?? 48 8D ?? ?? ?? 48 89 ?? ?? ?? E8 ?? ?? ?? ?? 83 78 " ) ;
2026-03-18 18:14:50 +01:00
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 WorldTimeDilationStringObfuscated = make_obfuscated < 0x59 > ( " F3 0F 10 ?? ?? ?? ?? ?? F3 0F 59 ?? ?? ?? ?? ?? F3 0F 59 ?? ?? ?? ?? ?? C3 " ) ;
constexpr auto TimeDilationStringObfuscated = make_obfuscated < 0x59 > ( " F3 0F ?? ?? ?? EB ?? F3 0F ?? ?? ?? ?? ?? ?? 48 8B ?? ?? 4C ?? ?? F3 0F ?? ?? 44 " ) ;
using AOBScan : : Make ;
using OffsetScan : : Make ;
// Prepare all data for scanning
std : : vector < AOBScanEntry > signatures = {
Make ( & CameraComponentaddress , CameraComponentStringObfuscated , " Camera component " ) ,
Make ( & FOVaddress , FOVStringObfuscated , " FOV " ) ,
2026-03-22 17:29:43 +01:00
Make ( & CutscenesFPSaddress1 , CutscenesFPS1StringObfuscated , " FPS unlock " ) ,
Make ( & CutscenesFPSaddress2 , CutscenesFPS2StringObfuscated , " FPS unlock " ) ,
2026-03-18 18:14:50 +01:00
Make ( & CameraStructaddress , CameraStruct1StringObfuscated , " Camera struct " ) ,
Make ( & Cameraaddress , CameraStringObfuscated , " Camera " ) ,
Make ( & WorldTimedilationaddress , WorldTimeDilationStringObfuscated , " World time dilation " ) ,
Make ( & Timedilationaddress , TimeDilationStringObfuscated , " AI time dilation " ) ,
} ;
// Scan all signature in a batch
Memory : : AOBScanBatch ( signatures , logger ) ;
2026-04-04 17:58:37 +02:00
if ( FOVaddress & & WorldTimedilationaddress & & Timedilationaddress & & CutscenesFPSaddress1 & &
CutscenesFPSaddress2 & & CameraComponentaddress & & CameraStructaddress & & Cameraaddress )
2026-03-18 18:14:50 +01:00
logger - > info ( " All AOB signatures found. Ready to patch... " ) ;
if ( ! GObjectsaddress | | ! AppendStringaddress | | ! ProcessEventaddress ) {
logger - > info ( " ------------ UEngine offsets search ------------ " ) ;
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 " ) ;
2026-03-19 19:00:00 +01:00
constexpr auto GWorldStringObfuscated = make_obfuscated < 0x8D > ( " 48 89 1D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 83 ?? ?? 5B C3 48 8D 0D " ) ;
2026-03-18 18:14:50 +01:00
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 ) ,
2026-03-19 19:00:00 +01:00
Make ( & GWorldaddress , GWorldStringObfuscated , " Gworld " , OffsetCalcType : : GetOffsetFromOpcode , & Offsets : : GWorld , 0x3 ) ,
2026-03-18 18:14:50 +01:00
Make ( & AppendStringaddress , AppendStringStringObfuscated , " AppendString " , OffsetCalcType : : UE_CalculateOffset , & Offsets : : AppendString ) ,
Make ( & ProcessEventaddress , ProcessEventStringObfuscated , " ProcessEvent " , OffsetCalcType : : UE_CalculateOffset , & Offsets : : ProcessEvent )
} ;
// Retrieve all Unreal Engine offsets in a batch
Memory : : OffsetScanBatch ( UEoffsetsScans , baseModule , logger , " " ) ;
}
logger - > info ( " -------------- Fixes initialisation ------------- " ) ;
AOBScanDone = true ;
}
ProcessEvent ( ) ;
2026-04-04 17:58:37 +02:00
if ( init ) return ;
FOVFixEnabled ( ) ;
2026-04-04 18:07:02 +02:00
UltraWideFixEnabled ( ) ;
2026-04-04 17:58:37 +02:00
CutscenesFPSFixEnabled ( ) ;
CameraFixEnabled ( ) ;
HUDUpdate ( true ) ;
gPendingDOF = true ;
gPendingCA = true ;
gPendingFog = true ;
LogFixToggle ( GameFixes : : None , g_fix_enabled ) ;
2026-03-18 18:14:50 +01:00
}
// Setters for Reshade addon call
extern " C " __declspec ( dllexport ) void SetFixesEnabled ( GameFixes fix , bool enabled ) { // Set each fix individually
2026-04-04 17:58:37 +02:00
bool bVisual = g_fix_enabled & & enabled ;
2026-03-18 18:14:50 +01:00
if ( fix = = GameFixes : : DevConsole ) { g_Console = enabled ; EnableConsole ( ) ; }
if ( fix = = GameFixes : : SkipIntro ) { g_SkipIntros_enabled = enabled ; }
if ( fix = = GameFixes : : FOV ) { g_fov_fix_enabled = enabled ; FOVFixEnabled ( ) ; }
if ( fix = = GameFixes : : Framerate ) { g_CutscenesFPS_fix_enabled = enabled ; CutscenesFPSFixEnabled ( ) ; }
2026-04-04 18:07:02 +02:00
if ( fix = = GameFixes : : UltraWide ) { g_ultrawide_fix_enabled = enabled ; UltraWideFixEnabled ( ) ; }
2026-03-18 18:14:50 +01:00
if ( fix = = GameFixes : : Camera ) { g_camera_fix_enabled = enabled ; CameraFixEnabled ( ) ; }
if ( fix = = GameFixes : : HUD ) { g_HUD_fix_enabled = enabled ; HUDUpdate ( true ) ; }
2026-04-04 17:58:37 +02:00
if ( fix = = GameFixes : : DOF ) { g_DOF_fix_enabled = enabled ; gPendingDOF = true ; LogFixToggle ( fix , bVisual ) ; }
if ( fix = = GameFixes : : ChromaticAberrations ) { g_CA_fix_enabled = enabled ; gPendingCA = true ; LogFixToggle ( fix , bVisual ) ; }
if ( fix = = GameFixes : : Fog ) { g_Fog_fix_enabled = enabled ; gPendingFog = true ; LogFixToggle ( fix , bVisual ) ; }
2026-03-18 18:14:50 +01:00
if ( fix = = GameFixes : : TimeDilation ) { g_TimeDilation_fix_enabled = enabled ; EnableCheats ( Cheat : : TimeDilation ) ; }
if ( fix = = GameFixes : : GodMode ) { g_GodMode_fix_enabled = enabled ; EnableCheats ( Cheat : : GodMode ) ; }
if ( fix = = GameFixes : : Stealth ) { g_Stealth_fix_enabled = enabled ; EnableCheats ( Cheat : : Stealth ) ; }
}
extern " C " __declspec ( dllexport ) void SetValues ( GameSetting setting , float value ) {
if ( setting = = GameSetting : : FOV ) g_AdditionalFOVValue = ( int ) ( value ) ;
if ( setting = = GameSetting : : CameraDistance ) g_cameraDistanceMultiplier = value ;
if ( setting = = GameSetting : : WorldTimeDilation ) g_WorldTimeDilationValue = value ;
if ( setting = = GameSetting : : AITimeDilation ) g_AITimeDilationValue = value ;
if ( setting = = GameSetting : : HUD ) {
g_HUDOffsets = ( ( int ) value * screenWidth ) / 100 ;
g_HUDAspect = ( screenWidth - g_HUDOffsets * 2 ) / ( float ) screenHeight ;
HUDUpdate ( false ) ;
}
if ( setting = = GameSetting : : UI ) {
g_UIOffsets = ( ( int ) value * screenWidth ) / 100 ;
g_UIAspect = ( screenWidth - g_UIOffsets * 2 ) / ( float ) screenHeight ;
HUDUpdate ( false ) ;
}
}
// Getters for Reshade addon call
extern " C " __declspec ( dllexport ) void GetGameInfos ( GameInfos * infos ) {
if ( ! infos ) return ;
infos - > FOVIn = g_FOV_In ;
infos - > CompensatedFOV = g_CompensatedFOV ;
infos - > FOVOut = g_FOV_Out ;
infos - > consoleEnabled = g_Console_Enabled ;
infos - > screenWidth = screenWidth ;
infos - > screenHeight = screenHeight ;
infos - > aspectRatio = g_AspectRatio ;
infos - > cameraIn = g_Camera_In ;
infos - > cameraOut = g_Camera_Out ;
}
// Code injection functions
static UUserWidget * g_PopupWidget = nullptr ;
2026-03-19 19:00:00 +01:00
static UUserWidget * g_ExplorationHUDWidget = nullptr ;
static UUserWidget * g_PromptWidget = nullptr ;
2026-03-18 18:14:50 +01:00
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 ;
2026-03-19 19:00:00 +01:00
static ABP_jRPG_GM_Bootstrap_C * bootStrap = nullptr ;
2026-03-18 18:14:50 +01:00
static ULONGLONG lastScanTick = 0 ; // Last time tick was called
2026-03-19 19:00:00 +01:00
static UC : : int32 NAME_FindPlayerStart = - 1 ;
2026-03-18 18:14:50 +01:00
static void ProcessEvent ( ) {
if ( ! PEHook & & ProcessEventaddress ) {
PEHook = safetyhook : : create_mid ( ProcessEventaddress + 0xc ,
[ ] ( SafetyHookContext & ctx ) {
UObject * object = ( UObject * ) ctx . rcx ;
UFunction * func = ( UFunction * ) ctx . rdx ;
ULONGLONG now = GetTickCount64 ( ) ;
if ( now - lastScanTick > = DEFAULT_DELAY_BETWEEN_TICK ) { // Get game resolution every 500 ms, (like when alt-tabbing or changing settings)
lastScanTick = now ;
GetResolution ( screenWidth , screenHeight , g_AspectRatio ) ;
if ( screenHeight > maxScreeHeight ) {
screenHeight = maxScreeHeight ;
g_AspectRatio = ( float ) screenWidth / screenHeight ;
}
}
2026-03-19 19:00:00 +01:00
if ( ! object | | ! func ) return ;
2026-03-19 19:04:50 +01:00
UC : : int32 comparisonIndex = func - > Name . ComparisonIndex ; // Make function comparision faster than allocating std::srting
2026-03-19 19:00:00 +01:00
if ( NAME_FindPlayerStart = = - 1 & & func - > Name . ToString ( ) = = " FindPlayerStart " ) NAME_FindPlayerStart = comparisonIndex ;
2026-03-18 18:14:50 +01:00
2026-03-19 19:00:00 +01:00
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 ( ) ;
2026-03-18 18:14:50 +01:00
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 " ) {
if ( typedWidget ) gWidgetPtr = typedWidget ;
HUDUpdate ( false ) ;
}
else if ( funcName = = " Destruct " ) gWidgetPtr = nullptr ;
if ( forceModifyFOV ) { bHasToModifyFOV = true ; return ; }
if ( funcName = = " BP_OnActivated " ) bHasToModifyFOV = false ;
if ( funcName = = " BP_OnDeactivated " ) bHasToModifyFOV = true ;
} ;
2026-03-19 19:00:00 +01:00
if ( g_SkipIntros_enabled & & funcName = = " Construct " & & bootStrap & & objectName . rfind ( " WBP_SplashScreen_Epilepsy_C " , 0 ) = = 0 )
bootStrap - > LoadMainMenuLevel ( ) ; // Skip intros
2026-03-18 18:14:50 +01:00
else if ( object - > IsA ( UWBP_DialogNotifBox_C : : StaticClass ( ) ) )
HandleWidget ( static_cast < UUserWidget * > ( object ) , g_PopupWidget ) ;
2026-03-19 19:00:00 +01:00
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 ) ;
}
2026-03-18 18:14:50 +01:00
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 ( ) ) )
HandleWidget ( static_cast < UWBP_GameMenu_v3_C * > ( object ) - > WBP_WidescreenRatioBox - > ConstrainSizeBox , g_gameMenu ) ;
else if ( object - > IsA ( UWBP_GM_CharacterMenuContainer_C : : StaticClass ( ) ) )
HandleWidget ( static_cast < UWBP_GM_CharacterMenuContainer_C * > ( object ) - > WBP_WidescreenRatioBox - > ConstrainSizeBox , g_characterMenu , true ) ;
else if ( object - > IsA ( UWBP_GM_MainMenuContainer_C : : StaticClass ( ) ) )
HandleWidget ( static_cast < UWBP_GM_MainMenuContainer_C * > ( object ) - > WBP_WidescreenRatioBox - > ConstrainSizeBox , g_MainMenu ) ;
else if ( object - > IsA ( UWBP_Merchant_Panel_C : : StaticClass ( ) ) )
HandleWidget ( static_cast < UWBP_Merchant_Panel_C * > ( object ) - > WBP_WidescreenRatioBox - > ConstrainSizeBox , g_merchantPanel ) ;
else if ( object - > IsA ( UWBP_PauseMenu_C : : StaticClass ( ) ) )
HandleWidget ( static_cast < UWBP_PauseMenu_C * > ( object ) - > WBP_WidescreenRatioBox - > ConstrainSizeBox , g_pauseMenu , true ) ;
2026-03-19 13:55:00 +01:00
else if ( object - > IsA ( UWBP_CinematicTransition_C : : StaticClass ( ) ) ) { // Remove aspect constrain from cinematics
2026-03-18 22:40:22 +01:00
auto * cinematicTransition = static_cast < UWBP_CinematicTransition_C * > ( object ) ;
if ( g_ultrawide_fix_enabled & & cinematicTransition ) cinematicTransition - > ScreenRatio = ( double ) g_AspectRatio ;
}
2026-03-18 18:14:50 +01:00
}
}
} ) ;
}
}
static void HUDUpdate ( bool writeLog ) {
if ( writeLog ) logger - > info ( " HUD & UI scaling fix {} " , g_fix_enabled & & g_HUD_fix_enabled ? " enabled " : " disabled " ) ;
float UIoffset = ( g_fix_enabled & & g_HUD_fix_enabled ) ? g_UIOffsets : 0.f ;
float HUDoffset = ( g_fix_enabled & & g_HUD_fix_enabled ) ? g_HUDOffsets : 0.f ;
// Force UI & HUD aspect ratios
if ( g_HUDBattle ) { g_HUDBattle - > SetMinAspectRatio ( g_HUDAspect ) ; g_HUDBattle - > SetMaxAspectRatio ( g_HUDAspect ) ; }
if ( g_pauseMenu ) { g_pauseMenu - > SetMinAspectRatio ( g_UIAspect ) ; g_pauseMenu - > SetMaxAspectRatio ( g_UIAspect ) ; }
if ( g_characterMenu ) { g_characterMenu - > SetMinAspectRatio ( g_UIAspect ) ; g_characterMenu - > SetMaxAspectRatio ( g_UIAspect ) ; }
if ( g_MainMenu ) { g_MainMenu - > SetMinAspectRatio ( g_UIAspect ) ; g_MainMenu - > SetMaxAspectRatio ( g_UIAspect ) ; }
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 ) ; }
2026-03-19 19:04:50 +01:00
if ( g_PopupWidget ) ApplyTransformOffset ( g_PopupWidget , - HUDoffset ) ;
if ( g_PromptWidget ) ApplyTransformOffset ( g_PromptWidget , - HUDoffset * ( ( float ) 1080 / screenHeight ) ) ;
if ( g_ExplorationHUDWidget ) CenterWidget ( g_ExplorationHUDWidget , HUDoffset , screenWidth , screenHeight , 1920 , 1080 ) ;
2026-03-18 18:14:50 +01:00
}
static void FOVFixEnabled ( ) {
if ( FOVaddress ) {
if ( ! FOVHook ) { // Hook only once
FOVHook = safetyhook : : create_mid ( FOVaddress + 0x10 ,
[ ] ( SafetyHookContext & ctx ) {
bool alterFOV = g_fix_enabled & & g_fov_fix_enabled & & bHasToModifyFOV ;
bool compensateFOV = g_fix_enabled & & g_ultrawide_fix_enabled & & bHasToModifyFOV ;
g_FOV_In = ctx . xmm0 . f32 [ 0 ] ;
g_CompensatedFOV = ctx . xmm0 . f32 [ 0 ] ;
if ( compensateFOV )
g_CompensatedFOV = ctx . xmm0 . f32 [ 0 ] = Maths : : CompensateHorizontalFOV ( g_FOV_In , g_BaseAspectRatio , g_AspectRatio ) ;
ctx . xmm0 . f32 [ 0 ] + = ( alterFOV ? g_AdditionalFOVValue : 0 ) ;
g_FOV_Out = ctx . xmm0 . f32 [ 0 ] ;
} ) ;
}
}
logger - > info ( " FOV fix {} " , g_fix_enabled & & g_fov_fix_enabled ? " enabled " : " disabled " ) ;
}
2026-04-04 18:07:02 +02:00
static void UltraWideFixEnabled ( ) {
2026-03-18 18:14:50 +01:00
if ( g_fix_enabled & & g_ultrawide_fix_enabled & & CameraComponentaddress & & CameraStructaddress ) { // Taken FROM UUU
if ( ! UltraWideHook ) { // Hook only once
UltraWideHook = safetyhook : : create_mid ( CameraComponentaddress + 0x15 ,
[ ] ( SafetyHookContext & ctx ) {
g_BaseAspectRatio = std : : bit_cast < float > ( static_cast < uint32_t > ( ctx . rax ) ) ; // Retrieve in real time desired aspect ratio
ctx . rax = std : : bit_cast < uint32_t > ( g_AspectRatio ) ; // Force our aspect ratio
} ) ;
}
Memory : : PatchBytes ( CameraComponentaddress + 0x1f , " \x31 \xC9 \x90 " , 3 ) ; // bConstrainAspectRatio = 0
}
if ( ! ( g_fix_enabled & & g_ultrawide_fix_enabled ) & & CameraComponentaddress & & CameraStructaddress )
Memory : : RestoreBytes ( CameraComponentaddress + 0x1f ) ;
logger - > info ( " Ultrawide fix {} " , g_fix_enabled & & g_ultrawide_fix_enabled ? " enabled " : " disabled " ) ;
}
static USmartSpringArmComponent * g_PlayerSpringArm = nullptr ;
static void CameraFixEnabled ( ) {
if ( ! Cameraaddress ) return ;
if ( g_fix_enabled & & g_camera_fix_enabled ) {
if ( ! CameraHook ) { // Hook only once
CameraHook = safetyhook : : create_mid ( Cameraaddress ,
[ ] ( SafetyHookContext & ctx ) {
if ( ! ctx . rbx ) return ;
USmartSpringArmComponent * springArmComponent = reinterpret_cast < USmartSpringArmComponent * > ( ctx . rbx ) ;
if ( springArmComponent = = g_PlayerSpringArm ) {
g_Camera_In = ctx . xmm0 . f32 [ 0 ] ;
ctx . xmm0 . f32 [ 0 ] * = g_cameraDistanceMultiplier ;
g_Camera_Out = ctx . xmm0 . f32 [ 0 ] ;
}
} ) ;
}
else CameraHook . enable ( ) ;
}
else if ( CameraHook ) CameraHook . disable ( ) ;
logger - > info ( " Camera distance fix {} " , g_fix_enabled & & g_camera_fix_enabled ? " enabled " : " disabled " ) ;
}
static void CutscenesFPSFixEnabled ( ) {
2026-03-22 17:29:43 +01:00
if ( ! CutscenesFPSaddress1 | | ! CutscenesFPSaddress2 ) return ;
2026-03-18 18:14:50 +01:00
if ( g_fix_enabled & & g_CutscenesFPS_fix_enabled ) {
2026-03-22 17:29:43 +01:00
if ( ! CutscenesFPSHook ) CutscenesFPSHook = safetyhook : : create_mid ( CutscenesFPSaddress1 + 0xa ,
2026-03-18 18:14:50 +01:00
[ ] ( SafetyHookContext & ctx ) {
ctx . xmm0 . f32 [ 0 ] = 0.f ;
} ) ;
else CutscenesFPSHook . enable ( ) ;
2026-03-22 17:29:43 +01:00
Memory : : PatchBytes ( CutscenesFPSaddress2 , " \x90 \xE9 " , 2 ) ;
}
else {
if ( CutscenesFPSHook ) CutscenesFPSHook . disable ( ) ;
Memory : : RestoreBytes ( CutscenesFPSaddress2 ) ;
2026-03-18 18:14:50 +01:00
}
logger - > info ( " Cutscenes FPS unlocker fix {} " , g_fix_enabled & & g_CutscenesFPS_fix_enabled ? " enabled " : " disabled " ) ;
}
// Cheats
static ULONGLONG lastActorScanTick = 2000 ;
static void EnableCheats ( Cheat cheat ) {
if ( WorldTimedilationaddress & & ! WorldTimeDilationHook ) {
WorldTimeDilationHook = safetyhook : : create_mid ( WorldTimedilationaddress + 0x10 ,
[ ] ( SafetyHookContext & ctx ) {
// From AWorldSettings retrieved from world->K2_GetWorldSettings()
ctx . xmm0 . f32 [ 0 ] * = g_TimeDilation_fix_enabled ? g_WorldTimeDilationValue : 1.f ;
2026-04-04 17:58:37 +02:00
// Apply visual effects only in hook main thread to ensure they will be applied correctly
if ( ! g_Console_Enabled ) return ; // It relies on dev console being reactivated
if ( gPendingFog . exchange ( false ) )
ApplyVisualEffect ( GameFixes : : Fog , g_fix_enabled & & g_Fog_fix_enabled ) ;
if ( gPendingDOF . exchange ( false ) )
ApplyVisualEffect ( GameFixes : : DOF , g_fix_enabled & & g_DOF_fix_enabled ) ;
if ( gPendingCA . exchange ( false ) )
ApplyVisualEffect ( GameFixes : : ChromaticAberrations , g_fix_enabled & & g_CA_fix_enabled ) ;
2026-03-18 18:14:50 +01:00
} ) ;
if ( Timedilationaddress & & ! TimeDilationHook ) {
TimeDilationHook = safetyhook : : create_mid ( Timedilationaddress ,
[ ] ( SafetyHookContext & ctx ) {
if ( ! ctx . rbx ) return ;
UObject * object = ( UObject * ) ctx . rbx ;
if ( ! object | | ! object - > Class ) return ;
2026-03-19 19:04:50 +01:00
auto * actor = static_cast < AActor * > ( object ) ;
if ( actor & & actor - > IsA ( ABP_jRPG_Enemy_World_Base_Seamless_C : : StaticClass ( ) ) )
actor - > CustomTimeDilation = g_TimeDilation_fix_enabled ? g_AITimeDilationValue : 1.f ;
auto * pawn = GetPawnFromObject ( object ) ;
if ( ! pawn ) return ;
2026-03-18 18:14:50 +01:00
ULONGLONG now = GetTickCount64 ( ) ;
if ( now - lastActorScanTick > = DEFAULT_ACTORS_SCAN_BETWEEN_TICKS ) {
lastActorScanTick = now ;
2026-03-19 19:04:50 +01:00
if ( pawn - > IsA ( ABP_jRPG_Character_Battle_Base_C : : StaticClass ( ) ) ) {
auto * playerBattle = static_cast < ABP_jRPG_Character_Battle_Base_C * > ( pawn ) ;
2026-03-18 18:14:50 +01:00
if ( playerBattle & & playerBattle - > AC_jRPG_CharacterStats ) {
double maxHP ;
playerBattle - > AC_jRPG_CharacterStats - > GetMaxHP ( & maxHP ) ;
if ( g_GodMode_fix_enabled ) playerBattle - > AC_jRPG_CharacterStats - > SetHP ( maxHP ) ; // God mode
playerBattle - > DEBUG_IgnoreDamages = g_GodMode_fix_enabled ; // God mode
}
}
2026-03-19 19:04:50 +01:00
else if ( pawn - > IsA ( ABP_jRPG_Character_World_C : : StaticClass ( ) ) ) {
auto * playerWorld = static_cast < ABP_jRPG_Character_World_C * > ( pawn ) ;
2026-03-18 18:14:50 +01:00
if ( g_fix_enabled & & g_camera_fix_enabled & & playerWorld - > SmartSpringArm_MainCamera )
g_PlayerSpringArm = playerWorld - > SmartSpringArm_MainCamera ; // Store custom player Springarm component
playerWorld - > DEBUG_IsInvisible = g_Stealth_fix_enabled ; // Stealth mode
}
}
} ) ;
}
}
if ( cheat = = Cheat : : TimeDilation ) logger - > info ( " Time dilation cheat {} " , g_TimeDilation_fix_enabled ? " enabled " : " disabled " ) ;
if ( cheat = = Cheat : : GodMode ) logger - > info ( " God mode {} " , g_GodMode_fix_enabled ? " enabled " : " disabled " ) ;
if ( cheat = = Cheat : : Stealth ) logger - > info ( " Stealth mode {} " , g_Stealth_fix_enabled ? " enabled " : " disabled " ) ;
if ( cheat = = Cheat : : Other ) logger - > info ( " Camera distance {} " , g_fix_enabled & & g_camera_fix_enabled ? " enabled " : " disabled " ) ;
}
// UE Console creation
static void EnableConsole ( ) {
if ( g_Console_Enabled | | ! g_Console | | ! GObjectsaddress | | ! AppendStringaddress | | ! ProcessEventaddress ) {
if ( ! g_Console & & ! user_inputs_logged ) {
logger - > info ( " ------------------ User inputs ------------------ " ) ;
user_inputs_logged = true ;
}
return ;
}
logger - > info ( " -------------- Console re-enabling -------------- " ) ;
ReactivateDevConsole ( logger ) ;
}
// Standard dll entry
BOOL APIENTRY DllMain ( HMODULE hModule , DWORD reason , LPVOID ) {
2026-04-04 17:58:37 +02:00
if ( reason = = DLL_PROCESS_DETACH ) {
2026-03-18 18:14:50 +01:00
logger - > info ( " Plugin {} unloaded. " , PLUGIN_NAME ) ;
spdlog : : drop_all ( ) ;
}
return TRUE ;
}