From 07bd8652a73c55302e88a51500e0daa027ded573 Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Wed, 20 Aug 2025 11:03:33 +0200 Subject: [PATCH] Added FOV and aspect ratio fixes --- WuchangFF/WuchangFF.vcxproj | 15 --- WuchangFF/dllmain.cpp | 195 ++++++++++++++++++++++++++++++++---- 2 files changed, 175 insertions(+), 35 deletions(-) diff --git a/WuchangFF/WuchangFF.vcxproj b/WuchangFF/WuchangFF.vcxproj index 47dcc12..1a3a766 100644 --- a/WuchangFF/WuchangFF.vcxproj +++ b/WuchangFF/WuchangFF.vcxproj @@ -192,15 +192,6 @@ - - - - - - - - - @@ -209,12 +200,6 @@ - - - - - - diff --git a/WuchangFF/dllmain.cpp b/WuchangFF/dllmain.cpp index aa548a1..121756f 100644 --- a/WuchangFF/dllmain.cpp +++ b/WuchangFF/dllmain.cpp @@ -6,31 +6,56 @@ #include #include - // Constants const std::string PLUGIN_NAME = "WuchangFF"; const std::string PLUGIN_LOG = PLUGIN_NAME + ".log"; const std::string gameExecutable = "Project_Plague-Win64-Shipping.exe"; +const float defaultAspect = 1.777778; // Default aspect that must not be compensated because FOV is already HOR+ // Logger std::shared_ptr logger; +// Screen informations +static int screenWidth = GetSystemMetrics(SM_CXSCREEN); +static int screenHeight = GetSystemMetrics(SM_CYSCREEN); +static float aspectRatio = (float)screenWidth / screenHeight; +static float baseAspect = 2.2222223; // Base game aspect ratio + // Plugin states static bool AOBScanDone = false; static bool g_fix_enabled = false; -static bool g_aspect_ratio_axis_constrain_fix_enabled = false; +static bool g_fov_fix_enabled = false; +static bool g_aspect_ratio_fix_enabled = false; static bool g_DOF_fix_enabled = false; static bool g_Vignetting_fix_enabled = false; static bool g_Fog_fix_enabled = false; +static int g_AdditionalFOVValue = 0; + +// Shared values +static float g_FOV_In = 0; +static float g_Compensated_FOV = 0; +static float g_FOV_Out = 0; // AOB Scan pointers -static uint8_t* HORPLUSaddress = nullptr; +static uint8_t* FOVaddress = nullptr; +static uint8_t* Aspectaddress_1 = nullptr; +static uint8_t* Aspectaddress_2 = nullptr; static uint8_t* DOFaddress = nullptr; static uint8_t* Vignettingaddress = nullptr; static uint8_t* Fogaddress = nullptr; +// FOV Addresses +uintptr_t t_fov_address_to_patch = 0; +uintptr_t t_aspect_address_to_patch = 0; + +// VEH debugger handles +PVOID vehFOVHandle = nullptr; +PVOID vehApectHandle_1 = nullptr; +PVOID vehApectHandle_2 = nullptr; + // Prototypes -static void HORPlusFixEnabled(bool fix_enabled); +static void FOVFixEnabled(bool fix_enabled); +static void AspectFixEnabled(bool fix_enabled); static void DOFFixEnabled(bool fix_enabled); static void VignettingFixEnabled(bool fix_enabled); static void FogFixEnabled(bool fix_enabled); @@ -140,21 +165,24 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) } } - if (HORPLUSaddress && DOFaddress && Vignettingaddress && Fogaddress) { + if (FOVaddress && Aspectaddress_1 && Aspectaddress_2 && DOFaddress && Vignettingaddress && Fogaddress) { logger->info("All AOB signatures found. Ready to patch..."); AOBScanDone = true; } logger->info("--------------- AOB scan finished ---------------"); } + if (g_fix_enabled) { - if (HORPLUSaddress) HORPlusFixEnabled(g_aspect_ratio_axis_constrain_fix_enabled); + if (FOVaddress) FOVFixEnabled(g_fov_fix_enabled || g_aspect_ratio_fix_enabled); + if (Aspectaddress_1 && Aspectaddress_2) AspectFixEnabled(g_aspect_ratio_fix_enabled); if (DOFaddress) DOFFixEnabled(g_DOF_fix_enabled); if (Vignettingaddress) VignettingFixEnabled(g_Vignetting_fix_enabled); if (Fogaddress) FogFixEnabled(g_Fog_fix_enabled); } else { - if (HORPLUSaddress) HORPlusFixEnabled(false); + if (FOVaddress) FOVFixEnabled(false); + if (Aspectaddress_1 && Aspectaddress_2) AspectFixEnabled(false); if (DOFaddress) DOFFixEnabled(false); if (Vignettingaddress) VignettingFixEnabled(false); if (Fogaddress) FogFixEnabled(false); @@ -162,11 +190,17 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) } } -// Setters for Reshade addon call -extern "C" __declspec(dllexport) void SetARAxisConstrainFixEnabled(bool enabled, bool init) +extern "C" __declspec(dllexport) void SetFOVFixEnabled(bool enabled, bool init) { - g_aspect_ratio_axis_constrain_fix_enabled = enabled; - if (!init) HORPlusFixEnabled(g_aspect_ratio_axis_constrain_fix_enabled); + g_fov_fix_enabled = enabled; + if (!init) FOVFixEnabled(g_fov_fix_enabled || g_aspect_ratio_fix_enabled); // FOV fix must be enabled when aspect ratio is too to compensate FOV +} + +// Setters for Reshade addon call +extern "C" __declspec(dllexport) void SetAspectRatioFixEnabled(bool enabled, bool init) +{ + g_aspect_ratio_fix_enabled = enabled; + if (!init) AspectFixEnabled(g_aspect_ratio_fix_enabled); } extern "C" __declspec(dllexport) void SetDOFFixEnabled(bool enabled, bool init) @@ -187,18 +221,138 @@ extern "C" __declspec(dllexport) void SetFogFixEnabled(bool enabled, bool init) if (!init) FogFixEnabled(g_Fog_fix_enabled); } -// Memory patch fixes -static void HORPlusFixEnabled(bool fix_enabled) { - if (g_fix_enabled && fix_enabled && HORPLUSaddress) { - Memory::PatchBytes(HORPLUSaddress, "\x31\xD2\x90\x90\x90\x90\x90\x90", 8); // xor edx,edx AspectRatioAxisConstraint=AspectRatio_MaintainYFOV - logger->info("HOR+ fix enabled"); +extern "C" __declspec(dllexport) void SetFOV(int fov) +{ + g_AdditionalFOVValue = fov; +} + +// Getters for Reshade addon call +extern "C" __declspec(dllexport) float GetFOVIn() { + return g_FOV_In; +} + +extern "C" __declspec(dllexport) float GetCompensatedFOV() { + return g_Compensated_FOV; +} + +extern "C" __declspec(dllexport) float GetFOVOut() { + return g_FOV_Out; +} + +// VEH debugger breakpoint fixes +LONG WINAPI FOVVehHandler(PEXCEPTION_POINTERS ep) { + if (!ep || !ep->ContextRecord || !ep->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; + if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; + + CONTEXT* ctx = ep->ContextRecord; // Vérifie si on est sur l'instruction cible //logger->info("Avant if ep"); + if (ctx->Rip == reinterpret_cast(FOVaddress)) { + t_fov_address_to_patch = static_cast(ctx->Rdi) + 0x30; + + float* xmm0 = reinterpret_cast(&ctx->Xmm0); + g_FOV_In = *xmm0; + // Calculate compensated FOV if aspect fix is checked + if (g_aspect_ratio_fix_enabled && baseAspect > defaultAspect) + g_Compensated_FOV = *xmm0 = Maths::CompensateHorizontalFOV(g_FOV_In, baseAspect, aspectRatio); + else + g_Compensated_FOV = *xmm0; + + *xmm0 += (g_fov_fix_enabled ? g_AdditionalFOVValue : 0); + g_FOV_Out = *xmm0; + + *reinterpret_cast(t_fov_address_to_patch) = g_FOV_Out; // Patch new FOV value in memory + + // Skip original opcode size in bytes otherwise the game will crash + ctx->Rip += 5; + + return EXCEPTION_CONTINUE_EXECUTION; } - if (!fix_enabled && HORPLUSaddress) { - Memory::RestoreBytes(HORPLUSaddress); - logger->info("HOR+ fix disabled"); + + return EXCEPTION_CONTINUE_SEARCH; +} + +LONG WINAPI AspectVehHandler_1(PEXCEPTION_POINTERS ep) { + if (!ep || !ep->ContextRecord || !ep->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; + if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; + + CONTEXT* ctx = ep->ContextRecord; // Check if instruction breakpoint is the one targeted + if (ctx->Rip == reinterpret_cast(Aspectaddress_1)) { + // Retrieves the addresse where aspect ratio will be stored + t_aspect_address_to_patch = static_cast(ctx->Rcx) + 0x48; + // Retrieves where the base aspect ratio is actually stored + baseAspect = *reinterpret_cast(&ctx->Rax); + *reinterpret_cast(t_aspect_address_to_patch) = baseAspect; // Patch base aspect ratio + + // Skip original opcode size in bytes otherwise the game will crash + ctx->Rip += 3; + + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +LONG WINAPI AspectVehHandler_2(PEXCEPTION_POINTERS ep) { + if (!ep || !ep->ContextRecord || !ep->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; + if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; + + CONTEXT* ctx = ep->ContextRecord; // Check if instruction breakpoint is the one targeted + if (ctx->Rip == reinterpret_cast(Aspectaddress_2)) { + // Retrieves the addresse where aspect ratio will be stored + t_aspect_address_to_patch = static_cast(ctx->Rcx) + 0x48; + *reinterpret_cast(t_aspect_address_to_patch) = aspectRatio; // Patch new aspect ratio value in memory + + // Skip original opcode size in bytes otherwise the game will crash + ctx->Rip += 3; + + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +// Code injection functions +static void FOVFixEnabled(bool fix_enabled) { + if (g_fix_enabled && fix_enabled && FOVaddress) { + vehFOVHandle = nullptr; + vehFOVHandle = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(FOVaddress), vehFOVHandle ,true, FOVVehHandler, 0); + if (vehFOVHandle) + logger->info("FOV fix enabled and hardware breakpoint armed"); + } + if (!fix_enabled && FOVaddress) { + if (vehFOVHandle) + vehFOVHandle = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(FOVaddress), vehFOVHandle, false, nullptr, 0); + if (!vehFOVHandle) + logger->info("FOV fix disabled and hardware breakpoint disarmed"); } } +static void AspectFixEnabled(bool fix_enabled) { + if (g_fix_enabled && fix_enabled && Aspectaddress_1 && Aspectaddress_2) { + vehApectHandle_1 = nullptr; + vehApectHandle_2 = nullptr; + vehApectHandle_1 = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(Aspectaddress_1), vehApectHandle_1, true, AspectVehHandler_1, 1); + vehApectHandle_2 = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(Aspectaddress_2), vehApectHandle_2, true, AspectVehHandler_2, 2); + + if (vehApectHandle_1 && vehApectHandle_2) + logger->info("Aspect ratio fix enabled and hardware breakpoint armed"); + + if (FOVaddress) FOVFixEnabled(true); // Compensate FOV if aspect fix is checked + } + if (!fix_enabled && Aspectaddress_1) { + if (vehApectHandle_1) + vehApectHandle_1 = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(Aspectaddress_1), vehApectHandle_1, false, nullptr, 1); + + if (vehApectHandle_2) + vehApectHandle_2 = Memory::SetupOrClearHardwareBreakPointForAllThreads(reinterpret_cast(Aspectaddress_2), vehApectHandle_2, false, nullptr, 2); + if (!vehApectHandle_1 && !vehApectHandle_2) + logger->info("Aspect ratio fix disabled and hardware breakpoint disarmed"); + + // When aspect fix is unchecked no more FOV compensation is needed + if (FOVaddress && !g_fov_fix_enabled) FOVFixEnabled(false); + } +} + +// Memory patch fixes static void DOFFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && DOFaddress) { Memory::PatchBytes(DOFaddress, "\x31\xFF\x90", 3); // xor edi,edi r.DepthOfFieldQuality = 0 @@ -223,7 +377,7 @@ static void VignettingFixEnabled(bool fix_enabled) { static void FogFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && Fogaddress) { - Memory::PatchBytes(Fogaddress, "\xEB", 1); // jmp "Project_Plague-Win64-Shipping.exe"+28E280E + Memory::PatchBytes(Fogaddress, "\xEB", 1); // jmp "Project_Plague-Win64-Shipping.exe"+28E280E r.Fog = 0 logger->info("Fog fix enabled"); } if (!fix_enabled && Fogaddress) { @@ -249,6 +403,7 @@ static void InitializeLogger() } } + // Standard dll entry BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID) {