From 1c0f9146c659d87630878bf2eee659e8b2ba24d9 Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Sun, 12 Oct 2025 19:45:40 +0200 Subject: [PATCH] Add fog fixes and skip intro fix. Update game name. --- LittleNightmareIII/dllmain.cpp | 108 +++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/LittleNightmareIII/dllmain.cpp b/LittleNightmareIII/dllmain.cpp index 6adb668..3a74905 100644 --- a/LittleNightmareIII/dllmain.cpp +++ b/LittleNightmareIII/dllmain.cpp @@ -10,11 +10,12 @@ #include "UEngine.hpp"; #include "SDK/Basic.hpp" #include "SDK/Engine_classes.hpp" +#include "SDK/LVL_SplashScreens_classes.hpp" using namespace SDK; // Constants -const std::string PLUGIN_NAME = "LittleNightmareIII"; +const std::string PLUGIN_NAME = "LittleNightmaresIII"; const std::string PLUGIN_LOG = PLUGIN_NAME + ".log"; const std::string gameExecutable = "LittleNightmaresIII.exe"; @@ -29,6 +30,8 @@ static bool g_ultrawide_fix_enabled = false; static bool g_DOF_fix_enabled = false; static bool g_CA_fix_enabled = false; static bool g_Vignetting_fix_enabled = false; +static bool g_Fog_fix_enabled = false; +static bool g_VolumetricFog_fix_enabled = false; static int g_AdditionalFOVValue = 0; // Shared values @@ -47,12 +50,16 @@ static uint8_t* FOVaddress = nullptr; static uint8_t* DOFaddress = nullptr; static uint8_t* CAaddress = nullptr; static uint8_t* Vignettingaddress = nullptr; +static uint8_t* VolumetricFogaddress = nullptr; +static uint8_t* Fogaddress = nullptr; static uint8_t* CameraComponentaddress = nullptr; static uint8_t* ConstrainAspectRatioaddress = nullptr; static uint8_t* AspectRatioAxisConstraintaddress = nullptr; // Hooking static SafetyHookMid FOVHook{}; +static SafetyHookMid FogHook{}; +static SafetyHookMid ProcessEventHook{}; // Prototypes static void FOVFixEnabled(); @@ -60,7 +67,10 @@ static void UltraWideFixEnabled(); static void DOFFixEnabled(); static void CAFixEnabled(); static void VignettingFixEnabled(); +static void FogFixEnabled(); +static void VolumetricFogFixEnabled(); static void EnableConsole(); +static void SkipIntro(); extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) { @@ -130,8 +140,28 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) logger->info("Vignetting signature found at address: 0x{:X}.", reinterpret_cast(Vignettingaddress)); } - if (CameraComponentaddress && FOVaddress && DOFaddress && CAaddress && Vignettingaddress - && ConstrainAspectRatioaddress && AspectRatioAxisConstraintaddress) { + if (!Fogaddress) { + constexpr auto FogStringObfuscated = make_obfuscated<0x4A>("83 ?? ?? ?? 75 ?? B3 ?? EB ?? 32 ?? 48 8B"); + Fogaddress = Memory::AOBScan(gameExecutable, FogStringObfuscated.decrypt(), PAGE_EXECUTE_READ); + + if (!Fogaddress) + logger->warn("Fog signature not found. Maybe your game has been updated and is no more compatible with this plugin."); + else + logger->info("Fog signature found at address: 0x{:X}.", reinterpret_cast(Fogaddress)); + } + + if (!VolumetricFogaddress){ + constexpr auto VolumetricFogStringObfuscated = make_obfuscated<0x4A>("74 ?? F7 47 ?? ?? ?? ?? ?? 74 ?? 83 BB"); + VolumetricFogaddress = Memory::AOBScan(gameExecutable, VolumetricFogStringObfuscated.decrypt(), PAGE_EXECUTE_READ); + + if (!VolumetricFogaddress) + logger->warn("Volumetric fog signature not found. Maybe your game has been updated and is no more compatible with this plugin."); + else + logger->info("Volumetric fog signature found at address: 0x{:X}.", reinterpret_cast(VolumetricFogaddress)); + } + + if (CameraComponentaddress && FOVaddress && DOFaddress && CAaddress && Vignettingaddress && + Fogaddress && VolumetricFogaddress && ConstrainAspectRatioaddress && AspectRatioAxisConstraintaddress) { logger->info("All AOB signatures found. Ready to patch..."); AOBScanDone = true; } @@ -180,6 +210,8 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) if (DOFaddress) DOFFixEnabled(); if (CAaddress) CAFixEnabled(); if (Vignettingaddress) VignettingFixEnabled(); + if (Fogaddress) FogFixEnabled(); + if (VolumetricFogaddress) VolumetricFogFixEnabled(); if (AspectRatioAxisConstraintaddress && ConstrainAspectRatioaddress) UltraWideFixEnabled(); if (!g_Console_Enabled && GObjectsaddress && GNamesaddress && ProcessEventaddress) EnableConsole(); @@ -216,6 +248,24 @@ extern "C" __declspec(dllexport) void SetVignettingFixEnabled(bool enabled, bool if (!init) VignettingFixEnabled(); } +extern "C" __declspec(dllexport) void SetFogFixEnabled(bool enabled, bool init) +{ + g_Fog_fix_enabled = enabled; + if (!init) FogFixEnabled(); +} + +extern "C" __declspec(dllexport) void SetVolumetricFogFixEnabled(bool enabled, bool init) +{ + g_VolumetricFog_fix_enabled = enabled; + if (!init) VolumetricFogFixEnabled(); +} + +extern "C" __declspec(dllexport) void SetSkipIntroEnabled(bool enabled) +{ + if (enabled) SkipIntro(); +} + + extern "C" __declspec(dllexport) void SetFOV(int fov) { g_AdditionalFOVValue = fov; @@ -250,6 +300,45 @@ static void FOVFixEnabled() { } } +static void FogFixEnabled() { + if (g_fix_enabled && g_Fog_fix_enabled && Fogaddress) { + if (!FogHook) { + FogHook = safetyhook::create_mid(Fogaddress, + [](SafetyHookContext& ctx) { + if (!ctx.rax) return; + uintptr_t* rax = reinterpret_cast(ctx.rax); + bool* bFog = reinterpret_cast(reinterpret_cast(rax) + 0x04); + *bFog = g_fix_enabled && g_Fog_fix_enabled ? false : true; + }); + } + logger->info("Fog fix enabled"); + } + if (!(g_fix_enabled && g_Fog_fix_enabled) && Fogaddress) + logger->info("Fog fix disabled"); +} + +static void SkipIntro() { + if (!ProcessEventHook && ProcessEventaddress) { + ProcessEventHook = safetyhook::create_mid(ProcessEventaddress + 0xc, + [](SafetyHookContext& ctx) { + auto* object = reinterpret_cast(ctx.rcx); + auto* function = reinterpret_cast(ctx.rdx); + // Hook ProcessEvent to skip intros + if (object && function) { + std::string className = object->GetName(); + std::string functionName = function->GetName(); + + if (className.contains("LVL_SplashScreens_C_0") && functionName.contains("ReceiveBeginPlay")) { + if (object->IsA(ALVL_SplashScreens_C::StaticClass())) { + auto* SplashScreen = static_cast(object); + if (SplashScreen) SplashScreen->GoToMainMenuLevel(); // skip intros + } + } + } + }); + } +} + // Memory patch fixes static void UltraWideFixEnabled() { if (g_fix_enabled && g_ultrawide_fix_enabled && AspectRatioAxisConstraintaddress && ConstrainAspectRatioaddress) { @@ -297,6 +386,17 @@ static void VignettingFixEnabled() { } } +static void VolumetricFogFixEnabled() { + if (g_fix_enabled && g_VolumetricFog_fix_enabled && VolumetricFogaddress) { + Memory::PatchBytes(VolumetricFogaddress, "\xEB", 1); // je-> jmp r.Fog=0 + logger->info("Volumetric fog fix enabled"); + } + if (!(g_fix_enabled && g_VolumetricFog_fix_enabled) && VolumetricFogaddress) { + Memory::RestoreBytes(VolumetricFogaddress); + logger->info("Volumetric fog fix disabled"); + } +} + // UE Console creation static void EnableConsole() { @@ -356,7 +456,7 @@ static void InitializeLogger() std::filesystem::path log_path = std::filesystem::absolute(PLUGIN_LOG); if (std::filesystem::exists(log_path)) std::filesystem::remove(log_path); - logger = std::make_shared("Little Nightmare III", std::make_shared(PLUGIN_LOG, 10 * 1024 * 1024, 1)); + logger = std::make_shared("Little Nightmares III", std::make_shared(PLUGIN_LOG, 10 * 1024 * 1024, 1)); logger->set_level(spdlog::level::debug); logger->flush_on(spdlog::level::debug); // Flush automatically }