From 1389103e7d78ace7c7b4b0c916788b777ec30d8d Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Sun, 12 Apr 2026 11:09:54 +0200 Subject: [PATCH] Add ultrawide. Add vehicle camera fix. --- Samson/dllmain.cpp | 126 ++++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/Samson/dllmain.cpp b/Samson/dllmain.cpp index 402f9fe..6db4f01 100644 --- a/Samson/dllmain.cpp +++ b/Samson/dllmain.cpp @@ -2,30 +2,30 @@ #include "CommonHeaders.h" #include "CommonUEHeaders.h" #include "SDK/CJ_classes.hpp" +#include "SDK/CJScreenNavigation_classes.hpp" #include "SDK/BP_VehicleBase_classes.hpp" #include "SDK/BP_NitroComponent_classes.hpp" +#include "SDK/WBP_UILayout_classes.hpp" using namespace SDK; // Constants const std::string PLUGIN_NAME = "Samson"; const std::string PLUGIN_LOG = PLUGIN_NAME + ".log"; -constexpr ULONGLONG DEFAULT_DELAY_BETWEEN_TICK = 100; // Used in ProcessEvent +constexpr ULONGLONG DEFAULT_DELAY_BETWEEN_TICK = 250; // Used in ProcessEvent constexpr ULONGLONG DEFAULT_DELAY_BETWEEN_WTD_TICK = 100; // Used for World time dilation constexpr ULONGLONG DEFAULT_DELAY_BETWEEN_TD_TICK = 200; // Used for AActor custom time dilation - // Logger std::shared_ptr logger; - // Screen informations static int screenWidth = GetSystemMetrics(SM_CXSCREEN); static int screenHeight = GetSystemMetrics(SM_CYSCREEN); float g_AspectRatio = (float)screenWidth / screenHeight; - // Plugin states static bool AOBScanDone = false; static std::atomic g_fix_enabled = false; static std::atomic g_fov_fix_enabled = false; +static std::atomic g_ultrawide_fix_enabled = false; static std::atomic g_HUD_fix_enabled = false; static std::atomic g_Camera_fix_enabled = false; static std::atomic g_Letterboxing_fix_enabled = false; @@ -38,13 +38,16 @@ static std::atomic g_Stealth_fix_enabled = false; static std::atomic g_Nitro_fix_enabled = false; static int g_AdditionalFOVValue = 0; static float g_CameraMultiplier = 1.f; +static float g_CamVehicleMultiplier = 1.f; static float g_WorldTimeDilationValue = 1.f; static float g_AITimeDilationValue = 1.f; static int g_HUDOffsets = 0; static int g_UIOffsets = 0; static float g_PlayerHealth = 0.f; +bool g_bIsInVehicule = false; // Shared values static float g_FOV_In = 75.f; +static float g_CompensatedFOV = 75.f; static float g_FOV_Out = 75.f; static float g_CameraIn = 189.26f; static float g_CameraOut = 189.26f; @@ -55,6 +58,7 @@ static uint8_t* WorldTimedilationaddress = nullptr; static uint8_t* Timedilationaddress = nullptr; // Hooking static SafetyHookMid FOVHook{}; +static SafetyHookMid UWHook{}; static SafetyHookMid CameraHook{}; static SafetyHookMid PEHook{}; static SafetyHookMid WorldTimeDilationHook{}; @@ -62,6 +66,7 @@ static SafetyHookMid TimeDilationHook{}; // Prototypes static void FOVFixEnabled(); static void CameraFixEnabled(); +static void UltraWideFixEnabled(); static void HUDUpdate(bool writeLog); static void EnableConsole(); static void EnableCheats(Cheat cheat); @@ -121,8 +126,8 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { ProcessEvent(); if (init) return; FOVFixEnabled(); + UltraWideFixEnabled(); CameraFixEnabled(); - HUDUpdate(false); SetAllEffectsToBeToggled(); LogFixToggle(GameFixes::None, g_fix_enabled); } @@ -131,12 +136,13 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { extern "C" __declspec(dllexport) void SetFixesEnabled(GameFixes fix, bool enabled) { // Set each fix individually bool bVisual = g_fix_enabled && enabled; if (fix == GameFixes::FOV) { g_fov_fix_enabled = enabled; FOVFixEnabled(); } + if (fix == GameFixes::UltraWide) { g_ultrawide_fix_enabled = enabled; UltraWideFixEnabled(); } if (fix == GameFixes::Camera) { g_Camera_fix_enabled = enabled; CameraFixEnabled(); } if (fix == GameFixes::Cutscenes) { g_Letterboxing_fix_enabled = enabled; } if (fix == GameFixes::ChromaticAberrations) { g_CA_fix_enabled = enabled; gPendingCA = true; LogFixToggle(fix, bVisual); } if (fix == GameFixes::Vignetting) { g_Vignetting_fix_enabled = enabled; gPendingVignetting = true; LogFixToggle(fix, bVisual); } if (fix == GameFixes::Fog) { g_Fog_fix_enabled = enabled; gPendingFog = true; LogFixToggle(fix, bVisual); } - if (fix == GameFixes::HUD) { g_HUD_fix_enabled = enabled; HUDUpdate(true); } + if (fix == GameFixes::HUD) { g_HUD_fix_enabled = enabled; } 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); } @@ -145,15 +151,10 @@ extern "C" __declspec(dllexport) void SetFixesEnabled(GameFixes fix, bool enable extern "C" __declspec(dllexport) void SetValues(GameSetting setting, float value) { if (setting == GameSetting::FOV) g_AdditionalFOVValue = (int)(value); - if (setting == GameSetting::HUD) { - g_HUDOffsets = (value * screenWidth) / 100; - HUDUpdate(false); - } - if (setting == GameSetting::UI) { - g_UIOffsets = (value * screenWidth) / 100; - HUDUpdate(false); - } + if (setting == GameSetting::HUD) g_HUDOffsets = (value * screenWidth) / 100; + if (setting == GameSetting::UI) g_UIOffsets = (value * screenWidth) / 100; if (setting == GameSetting::CameraDistance) g_CameraMultiplier = value; + if (setting == GameSetting::CamAltDistance) g_CamVehicleMultiplier = value; if (setting == GameSetting::WorldTimeDilation) g_WorldTimeDilationValue = value; if (setting == GameSetting::AITimeDilation) g_AITimeDilationValue = value; } @@ -163,6 +164,7 @@ extern "C" __declspec(dllexport) void GetGameInfos(GameInfos* infos) { infos->FOVIn = g_FOV_In; infos->FOVOut = g_FOV_Out; infos->cameraIn = g_CameraIn; + infos->CompensatedFOV = g_CompensatedFOV; infos->cameraOut = g_CameraOut; infos->Health = g_PlayerHealth; infos->screenWidth = screenWidth; @@ -179,22 +181,20 @@ std::unordered_set g_TrackedRightSidedWidgets; // Left, right sided and centered widgets std::unordered_set g_LeftSidedWidgets = { "WBP_MiniMap_C", "WBP_HUD_MissionGameplay_C", "WBP_TimeTrialTimeItem_C", - "WBP_HUD_HealthStatus_C", "WBP_HUD_CurrentLocation_C", "WBP_HUD_HintItem_C", - "WBP_LocateItem_C", -}; -std::unordered_set g_CenteredUIWidgets = { - "WBP_StartMenu_C", "WBP_GameMenu_C" -}; -std::unordered_set g_RightSidedWidgets = { - "WBP_HUD_VehicleWidget_C", "WBP_HUD_HourToHour_C", + "WBP_HUD_HealthStatus_C", "WBP_HUD_CurrentLocation_C", "WBP_HUD_HintItem_C", "WBP_LocateItem_C" }; +std::unordered_set g_CenteredUIWidgets = { "WBP_UILayout_C" }; +std::unordered_set g_RightSidedWidgets = { "WBP_HUD_VehicleWidget_C", "WBP_HUD_HourToHour_C" }; static UUserWidget* letterBoxWidget = nullptr; -static ULONGLONG lastScanTick = 0; // Last time tick was called +static UWidget* menu = nullptr; // Container menu +bool bInMainMenu = false; static void ProcessEvent() { if (!PEHook && ProcessEventaddress) { PEHook = safetyhook::create_mid(ProcessEventaddress + 0xc, [](SafetyHookContext& ctx) { + static ULONGLONG lastScanTick = 0; // Last time tick was called + ULONGLONG now = GetTickCount64(); if (now - lastScanTick >= DEFAULT_DELAY_BETWEEN_TICK) { // Delay between each tick to avoid ProcessEvent stuttering lastScanTick = now; @@ -206,40 +206,42 @@ static void ProcessEvent() { UFunction* func = (UFunction*)ctx.rdx; if (!object || !func) return; - const std::string funcName = func->GetName(); - const std::string className = object->Class->GetName(); - const std::string objectFullName = object->GetFullName(); - if (object->IsA(UUserWidget::StaticClass())) { + const std::string funcName = func->GetName(); + const std::string className = object->Class->GetName(); + const std::string objectFullName = object->GetFullName(); auto* widget = static_cast(object); if (funcName == "Construct") { - TrackWidgetConstruct(widget); - if (g_LeftSidedWidgets.contains(className)) g_TrackedLeftSidedWidgets.insert(widget); if (objectFullName.find("ExitProgressBar") != std::string::npos) g_TrackedLeftSidedWidgets.insert(widget); if (g_RightSidedWidgets.contains(className)) g_TrackedRightSidedWidgets.insert(widget); if (g_CenteredUIWidgets.contains(className)) g_TrackedCenteredUIWidgets.insert(widget); - if (g_fix_enabled && g_HUD_fix_enabled) - HUDUpdate(false); // Called once - if (className == "WBP_Letterbox_Overlay_C") { // Letterboxing removal letterBoxWidget = widget; if (IsValidUObj(widget) && g_fix_enabled && g_Letterboxing_fix_enabled) widget->SetVisibility(ESlateVisibility::Collapsed); } - - if (className == "WBP_StartMenu_C") EnableConsole(); // Enable console only when Main menu is reached + if (className == "WBP_UILayout_C") { + if (object->IsA(UWBP_UILayout_C::StaticClass())) { + auto* UILayout = static_cast(object); + if (UILayout && IsValidUObj(UILayout->Menu)) menu = UILayout->Menu; + } + } + if (className == "WBP_StartMenu_C") { + EnableConsole(); // Enable console only when Main menu is reached + bInMainMenu = true; + } } - if (funcName == "Destruct") { - TrackWidgetDestruct(widget); g_TrackedLeftSidedWidgets.erase(widget); g_TrackedRightSidedWidgets.erase(widget); g_TrackedCenteredUIWidgets.erase(widget); if (className == "WBP_Letterbox_Overlay_C") letterBoxWidget = nullptr; + if (className == "WBP_UILayout_C") menu = nullptr; + if (className == "WBP_StartMenu_C") bInMainMenu = false; } } }); @@ -253,6 +255,7 @@ static void HUDUpdate(bool writeLog) { // Copy sets to avoid race conditions std::unordered_set copyLeftWidgets = g_TrackedLeftSidedWidgets; + std::unordered_set copyCenteredWidgets = g_TrackedCenteredUIWidgets; std::unordered_set copyRightWidgets = g_TrackedRightSidedWidgets; for (auto* widget : copyLeftWidgets) { @@ -263,14 +266,25 @@ static void HUDUpdate(bool writeLog) { if (!IsValidUObj(widget)) continue; // Ensure the widget is valid ApplyTransformOffset(widget, -HUDoffset); } + for (auto* widget : copyCenteredWidgets) { + if (!IsValidUObj(widget)) continue; + if (IsValidUObj(menu)) { + if (menu->IsVisible()) CenterWidget(widget, g_UIOffsets, screenWidth, screenHeight); + else CenterWidget(widget, 0.f, screenWidth, screenHeight); + } + } } +float desiredAspect = 0.f; static void FOVFixEnabled() { if (!CameraComponentaddress) return; if (!FOVHook) { // Hook only once FOVHook = safetyhook::create_mid(CameraComponentaddress + 0xa, [](SafetyHookContext& ctx) { g_FOV_In = ctx.xmm0.f32[0]; + if (g_fix_enabled && g_ultrawide_fix_enabled && !bInMainMenu) + ctx.xmm0.f32[0] = Maths::CompensateHorizontalFOV(g_FOV_In, desiredAspect, g_AspectRatio); + g_CompensatedFOV = ctx.xmm0.f32[0]; ctx.xmm0.f32[0] += g_fix_enabled && g_fov_fix_enabled ? g_AdditionalFOVValue : 0.f; g_FOV_Out = ctx.xmm0.f32[0]; }); @@ -278,6 +292,23 @@ static void FOVFixEnabled() { logger->info("FOV fix {}", g_fix_enabled && g_fov_fix_enabled ? "enabled" : "disabled"); } +static void UltraWideFixEnabled() { + if (!CameraComponentaddress) return; + if (g_fix_enabled && g_ultrawide_fix_enabled) { + if (!UWHook) { + UWHook = safetyhook::create_mid(CameraComponentaddress + 0x15, + [](SafetyHookContext& ctx) { + desiredAspect = std::bit_cast(static_cast(ctx.rax & 0xFFFFFFFF)); // Retrieve desired aspect ratio from rax + ctx.rax = static_cast(std::bit_cast(g_AspectRatio)); // Force rendering aspect ratio if not HOR+ + }); + } + else UWHook.enable(); + } + else if (UWHook) UWHook.disable(); + + logger->info("Ultrawide fix {}", g_fix_enabled && g_ultrawide_fix_enabled ? "enabled" : "disabled"); +} + static void CameraFixEnabled() { if (!Cameraaddress) return; if (g_fix_enabled && g_Camera_fix_enabled) { @@ -285,7 +316,8 @@ static void CameraFixEnabled() { CameraHook = safetyhook::create_mid(Cameraaddress, [](SafetyHookContext& ctx) { g_CameraIn = ctx.xmm1.f32[0]; - ctx.xmm1.f32[0] *= g_fix_enabled && g_Camera_fix_enabled ? g_CameraMultiplier : 1.f; + ctx.xmm1.f32[0] *= g_fix_enabled && g_Camera_fix_enabled ? + (!g_bIsInVehicule ? g_CameraMultiplier : g_CamVehicleMultiplier) : 1.f; g_CameraOut = ctx.xmm1.f32[0]; }); } @@ -307,12 +339,9 @@ static void EnableCheats(Cheat cheat) { ULONGLONG now = GetTickCount64(); if (now - lastScanWTDTick >= DEFAULT_DELAY_BETWEEN_WTD_TICK) { // Delay between each tick to avoid ProcessEvent stuttering lastScanWTDTick = now; - - if (IsValidUObj(letterBoxWidget)) - letterBoxWidget->SetVisibility(g_fix_enabled && g_Letterboxing_fix_enabled ? - ESlateVisibility::Collapsed : ESlateVisibility::Visible); + if (letterBoxWidget) letterBoxWidget->SetVisibility(g_fix_enabled && g_Letterboxing_fix_enabled ? + ESlateVisibility::Collapsed : ESlateVisibility::Visible); } - // 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)) @@ -334,20 +363,22 @@ static void EnableCheats(Cheat cheat) { if (object->IsA(ACJCharacter::StaticClass())) { ACJCharacter* character = static_cast(object); - if (!IsValidUObj(character)) return; + if (!character || !character->Class) return; if (character->IsPlayer()) { + g_bIsInVehicule = character->IsInVehicle(); static ULONGLONG lastScanTDTick = 0; // Last time tick was called ULONGLONG now = GetTickCount64(); + if (now - lastScanTDTick >= DEFAULT_DELAY_BETWEEN_TD_TICK) { // Delay between each tick to avoid spamming lastScanTDTick = now; - if (g_GodMode_fix_enabled && IsValidUObj(character->HealthComponent)) // God mode + + if (g_GodMode_fix_enabled && character->HealthComponent) // God mode character->HealthComponent->FullyRestore(); g_PlayerHealth = character->GetHealth(); // Retrieves player health - if (auto vehicle = character->GetVehicle()) { - if (g_GodMode_fix_enabled && IsValidUObj(vehicle) && IsValidUObj(vehicle->DestructionComponent)) // Indestructible vehicle + if (g_GodMode_fix_enabled && vehicle && vehicle->DestructionComponent) // Indestructible vehicle vehicle->DestructionComponent->RestoreVehicle(); if (g_Nitro_fix_enabled && vehicle->IsA(ABP_VehicleBase_C::StaticClass())) { @@ -362,8 +393,7 @@ static void EnableCheats(Cheat cheat) { } } } - if (g_Stealth_fix_enabled && IsValidUObj(character->StimEventSourceComponent) && - IsValidUObj(character->SpatialAwarenessComponent)) { // Stealth + if (g_Stealth_fix_enabled && character->StimEventSourceComponent && character->SpatialAwarenessComponent) { // Stealth character->StimEventSourceComponent->ActiveStimEvents = {}; character->StimEventSourceComponent->Deactivate(); character->SpatialAwarenessComponent->Deactivate();