diff --git a/StyxBladesOfGreed/dllmain.cpp b/StyxBladesOfGreed/dllmain.cpp new file mode 100644 index 0000000..a20c821 --- /dev/null +++ b/StyxBladesOfGreed/dllmain.cpp @@ -0,0 +1,565 @@ +#include +#include "CommonHeaders.h" +#include "UEngine.hpp" +#include "UETools.hpp" +#include "UEvars.hpp" +#include "UEMath.hpp" +#include "Logger.hpp" +#include "SDK/Basic.hpp" +#include "SDK/Engine_classes.hpp" +#include "SDK/UI_Settings_classes.hpp" +#include "SDK/UI_InputButton_Menu_classes.hpp" +#include "SDK/UI_Profile_classes.hpp" +#include "SDK/UI_MainMenu_classes.hpp" +#include "SDK/UI_Difficulty_classes.hpp" +#include "SDK/UI_PauseMenu_classes.hpp" +#include "SDK/Styx3_classes.hpp" + +using namespace SDK; + +// Constants +const std::string PLUGIN_NAME = "Styx"; +const std::string PLUGIN_LOG = PLUGIN_NAME + ".log"; + +// 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; +float g_BaseAspectRatio = 2.39; + +// Plugin states +static bool AOBScanDone = false; +static bool g_Console = 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_DOF_fix_enabled = false; +static std::atomic g_CA_fix_enabled = false; +static std::atomic g_Vignetting_fix_enabled = false; +static std::atomic g_Fog_fix_enabled = false; +static std::atomic g_TimeDilation_fix_enabled = false; +static std::atomic g_GodMode_fix_enabled = false; +static std::atomic g_Amber_fix_enabled = false; +static std::atomic g_Stealth_fix_enabled = false; +static int g_AdditionalFOVValue = 0; +static float g_CameraMultiplier = 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; +static float g_PlayerAmber = 0.f; +static bool user_inputs_logged = false; +// Shared values +static float g_FOV_In = 60.f; +static float g_CompensatedFOV = 60.f; +static float g_FOV_Out = 60.f; +static float g_CameraIn = 200.f; +static float g_CameraOut = 200.f; +// AOB Scan pointers +static uint8_t* DOFaddress = nullptr; +static uint8_t* CAaddress = nullptr; +static uint8_t* Vignettingaddress = nullptr; +static uint8_t* Fogaddress = nullptr; +static uint8_t* CameraComponentaddress = nullptr; +static uint8_t* CameraDistanceaddress = nullptr; +static uint8_t* ConstrainAspectRatioaddress = nullptr; +static uint8_t* WorldTimedilationaddress = nullptr; +static uint8_t* Timedilationaddress = nullptr; +// Hooking +static SafetyHookMid FOVHook{}; +static SafetyHookMid CameraHook{}; +static SafetyHookMid UltraWideHook1{}; +static SafetyHookMid UltraWideHook2{}; +static SafetyHookMid PEHook{}; +static SafetyHookMid WorldTimeDilationHook{}; +static SafetyHookMid TimeDilationHook{}; +// Prototypes +static void FOVFixEnabled(); +static void UltraWideFixEnabled(); +static void CameraDistanceFixEnabled(); +static void HUDUpdate(bool writeLog); +static void HUDFixEnabled(UWidget* widget, float left, float right, bool writeLog, int depth); +static void DOFFixEnabled(); +static void CAFixEnabled(); +static void VignettingFixEnabled(); +static void FogFixEnabled(); +static void EnableConsole(); +static void EnableCheats(Cheat cheat); +static void ProcessEvent(); +// UEngine variable +static UWidget* g_UIMainMenuWidget = nullptr; +static UWidget* g_UISettingsWidget = nullptr; +static UWidget* g_UIDescriptionWidget = nullptr; +static UWidget* g_DifficultyWidget = nullptr; +static UWidget* g_difficultyMenusWidget = nullptr; +static UWidget* g_ProfileWidget = nullptr; +static UWidget* g_PauseWidget = nullptr; +static UWidget* g_StatusWidget = nullptr; +static UWidget* g_GaugesWidget = nullptr; +static UWidget* g_ChangeWheelWidget = nullptr; +static UWidget* g_AbilityWheelWidget = nullptr; +static UWidget* g_CraftingWidget = nullptr; +static AStyx3PlayerCharacter* g_Player = nullptr; + +extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) { + g_fix_enabled = enabled; + if (!AOBScanDone) { // Unreal Engine 5.6 + logger->info("--------------- AOB scan started ---------------"); + constexpr auto CameraComponentStringObfuscated = make_obfuscated<0xF3>("EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 8B 83 ?? ?? ?? ?? 89"); + constexpr auto CameraDistanceStringObfuscated = make_obfuscated<0x66>("0F ?? ?? F3 0F ?? ?? 0F ?? ?? 0F 11 ?? ?? ?? 0F 10 ?? ?? ?? 66 0F ?? ?? F2 0F"); + constexpr auto DOFStringObfuscated = make_obfuscated<0xC1>("8B ?? ?? 48 ?? ?? E8 ?? ?? ?? ?? 0F ?? ?? 48 8D ?? ?? ?? ?? ?? 48 C1"); + constexpr auto CAStringObfuscated = make_obfuscated<0x39>("7F ?? 44 89 ?? ?? ?? ?? ?? 43 8B ?? ?? 39 05 ?? ?? ?? ?? 0F 8F"); + constexpr auto VignettingStringObfuscated = make_obfuscated<0xEB>("8B ?? 83 ?? ?? 7D ?? 44 89 ?? ?? ?? ?? ?? EB"); + constexpr auto FogStringObfuscated = make_obfuscated<0x75>("74 ?? 48 8B ?? ?? ?? ?? ?? 83 ?? ?? ?? 75 ?? 40 ?? ?? EB ?? 40 ?? ?? 48"); + constexpr auto WorldTimeDilationStringObfuscated = make_obfuscated<0xF6>("F6 81 ?? ?? ?? ?? ?? 74 ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ?? C3"); + constexpr auto TimeDilationStringObfuscated = make_obfuscated<0x44>("F3 0F ?? ?? ?? EB ?? F3 0F ?? ?? ?? ?? ?? ?? 48 8B ?? ?? 4C ?? ?? F3 0F ?? ?? 44"); + + using AOBScan::Make; + using OffsetScan::Make; + // Prepare all data for scanning + std::vector signatures = { + Make(&CameraComponentaddress, CameraComponentStringObfuscated, "FOV"), + Make(&CameraDistanceaddress, CameraDistanceStringObfuscated, "Camera distance"), + Make(&DOFaddress, DOFStringObfuscated, "DOF"), + Make(&CAaddress, CAStringObfuscated, "Chromatic aberrations"), + Make(&Vignettingaddress, VignettingStringObfuscated, "Vignetting"), + Make(&Fogaddress, FogStringObfuscated, "Fog"), + Make(&WorldTimedilationaddress, WorldTimeDilationStringObfuscated, "World time dilation"), + Make(&Timedilationaddress, TimeDilationStringObfuscated, "Actor time dilation") + }; + // Scan all signature in a batch + Memory::AOBScanBatch(signatures, logger); + ConstrainAspectRatioaddress = CameraComponentaddress + 0x18; + + if (CameraComponentaddress && CameraDistanceaddress && DOFaddress && CAaddress && Vignettingaddress && + Fogaddress && ConstrainAspectRatioaddress && WorldTimedilationaddress && Timedilationaddress /*&& Stealthaddress*/) + logger->info("All AOB signatures found. Ready to patch..."); + + if (!GObjectsaddress || !AppendStringaddress || !ProcessEventaddress) { + logger->info("------------ UEngine offsets search ------------"); + uint8_t* baseModule = reinterpret_cast(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<0x5B>("48 89 1D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 83 ?? ?? 5B C3"); + 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 ?? ?? ?? ?? ?? 8B 41"); + + // Prepare all data for scanning + std::vector 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) + }; + // Retrieve all Unreal Engine offsets in a batch + Memory::OffsetScanBatch(UEoffsetsScans, baseModule, logger, ""); + } + logger->info("-------------- Fixes initialisation -------------"); + AOBScanDone = true; + } + + if (!init && CameraComponentaddress) FOVFixEnabled(); + if (!init && ConstrainAspectRatioaddress) UltraWideFixEnabled(); + if (!init && CameraDistanceaddress) CameraDistanceFixEnabled(); + if (!init) HUDUpdate(true); + if (!init && DOFaddress) DOFFixEnabled(); + if (!init && CAaddress) CAFixEnabled(); + if (!init && Vignettingaddress) VignettingFixEnabled(); + if (!init && Fogaddress) FogFixEnabled(); + if (!init && WorldTimedilationaddress) { + EnableCheats(Cheat::TimeDilation); + EnableCheats(Cheat::GodMode); + EnableCheats(Cheat::Mana); + EnableCheats(Cheat::Stealth); + } + ProcessEvent(); +} + +// Setters for Reshade addon call +extern "C" __declspec(dllexport) void SetFixesEnabled(GameFixes fix, bool enabled) { // Set each fix individually + if (fix == GameFixes::DevConsole) { g_Console = enabled; EnableConsole(); } + 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; CameraDistanceFixEnabled(); } + if (fix == GameFixes::DOF) { g_DOF_fix_enabled = enabled; DOFFixEnabled(); } + if (fix == GameFixes::ChromaticAberrations) { g_CA_fix_enabled = enabled; CAFixEnabled(); } + if (fix == GameFixes::Vignetting) { g_Vignetting_fix_enabled = enabled; VignettingFixEnabled(); } + if (fix == GameFixes::Fog) { g_Fog_fix_enabled = enabled; FogFixEnabled(); } + if (fix == GameFixes::HUD) { g_HUD_fix_enabled = enabled; HUDUpdate(true); } + 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::Mana) { g_Amber_fix_enabled = enabled; EnableCheats(Cheat::Mana); } + 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_CameraMultiplier = value; + if (setting == GameSetting::HUD) { + g_HUDOffsets = ((int)value * screenWidth) / 100; + HUDUpdate(false); + } + if (setting == GameSetting::UI) { + g_UIOffsets = ((int)value * screenWidth) / 100; + HUDUpdate(false); + } + if (setting == GameSetting::WorldTimeDilation) g_WorldTimeDilationValue = value; + if (setting == GameSetting::AITimeDilation) g_AITimeDilationValue = value; +} +// Getters for Reshade addon call +extern "C" __declspec(dllexport) void GetGameInfos(GameInfos* infos) { + if (!infos) return; + + infos->FOVIn = g_FOV_In; + infos->FOVOut = g_FOV_Out; + infos->cameraIn = g_CameraIn; + infos->cameraOut = g_CameraOut; + infos->CompensatedFOV = g_CompensatedFOV; + infos->Health = g_PlayerHealth; + infos->Mana = g_PlayerAmber; + infos->consoleEnabled = g_Console_Enabled; +} + +// -- Code injection functions -- +static void ProcessEvent() { + if (!PEHook && ProcessEventaddress) { + PEHook = safetyhook::create_mid(ProcessEventaddress + 0xc, + [](SafetyHookContext& ctx) { + UObject* object = (UObject*)ctx.rcx; + UFunction* func = (UFunction*)ctx.rdx; + if (!object || !func) return; + + std::string funcName = func->GetName(); + std::string objectName = object->GetName(); + // Full stealth fix: disable AI perception. + if (funcName == "OnPerceptionIntensityUpdated") { + if (object->IsA(AStyx3AIController::StaticClass())) { + auto* AIController = static_cast(object); + if (g_Player && AIController->PerceptionGaugeComponent) { + if (g_Stealth_fix_enabled) + AIController->PerceptionGaugeComponent->ResetGauges(g_Player); + else + AIController->PerceptionGaugeComponent->SetActiveBufferDetection(g_Player, true); + } + } + } + if (object->IsA(UUserWidget::StaticClass()) && (funcName == "Construct" || funcName == "Destruct" || funcName == "Tick")) { + auto UpdateUI = [&](const std::vector& targets, const std::vector& values) { + if (funcName == "Construct") { + for (size_t i = 0; i < targets.size(); ++i) + *targets[i] = values[i]; + + HUDUpdate(false); + } + else if (funcName == "Destruct") + for (auto* t : targets) *t = nullptr; + }; + // UI scaling + if (object->IsA(UUI_Settings_C::StaticClass())) { + auto* settings = static_cast(object); + auto* widget = static_cast(object); + + UpdateUI({ &g_UISettingsWidget, &g_UIDescriptionWidget }, { widget->WidgetTree->RootWidget, settings->Description_Holder }); + } + else if (object->IsA(UUI_Difficulty_C::StaticClass())) { + auto* difficulty = static_cast(object); + auto* widget = static_cast(object); + + UpdateUI({ &g_DifficultyWidget, &g_difficultyMenusWidget }, { widget->WidgetTree->RootWidget, difficulty->VerticalBox_Menu }); + } + else if (object->IsA(UUI_PauseMenu_C::StaticClass())) { + auto* pauseMenu = static_cast(object); + + UpdateUI({ &g_PauseWidget }, { pauseMenu->WidgetTree->RootWidget }); + } + else if (object->IsA(UUI_Profile_C::StaticClass())) { + auto* profile = static_cast(object); + + UpdateUI({ &g_ProfileWidget }, { profile->WidgetTree->RootWidget }); + } + else if (object->IsA(UUI_MainMenu_C::StaticClass())) { + auto* mainmenu = static_cast(object); + + UpdateUI({ &g_UIMainMenuWidget }, { mainmenu->WidgetTree->RootWidget }); + } + // HUD scaling + else { + std::function FindWidgets; + FindWidgets = [&](SDK::UWidget* Widget) { // Find all HUD important widgets in container + if (!Widget) return; + + const std::string widgetName = Widget->GetName(); + const std::string className = Widget->Class ? Widget->Class->GetName() : ""; + + auto match = [&](const std::string& pattern) { + return widgetName.find(pattern) != std::string::npos || className.find(pattern) != std::string::npos; + }; + + if (!g_StatusWidget && match("StatusWidget")) g_StatusWidget = Widget; + else if (!g_GaugesWidget && match("Gauges")) g_GaugesWidget = Widget; + else if (!g_ChangeWheelWidget && match("ChangeWheel")) g_ChangeWheelWidget = Widget; + else if (!g_AbilityWheelWidget && match("AbilityWheel")) g_AbilityWheelWidget = Widget; + // Stop early if everything found- + if (g_StatusWidget && g_GaugesWidget && g_ChangeWheelWidget && g_AbilityWheelWidget /*&& g_CraftingDescWidget*/) + return; + // Descend Panel children + if (Widget->IsA(SDK::UPanelWidget::StaticClass())) { + auto* Panel = static_cast(Widget); + for (int i = 0; i < Panel->GetChildrenCount(); ++i) + FindWidgets(Panel->GetChildAt(i)); + } + // Descend UserWidget root + if (Widget->IsA(SDK::UUserWidget::StaticClass())) { + auto* UW = static_cast(Widget); + if (UW->WidgetTree && UW->WidgetTree->RootWidget) + FindWidgets(UW->WidgetTree->RootWidget); + } + }; + + if (objectName.contains("Crafting")) { + auto* crafting = static_cast(object); + if (funcName == "Construct") { + g_CraftingWidget = crafting->WidgetTree->RootWidget; + HUDUpdate(false); + } + else if (funcName == "Destruct") { + g_CraftingWidget = nullptr; + //g_CraftingDescWidget = nullptr; + } + } + else if (objectName.contains("HUD_Widget_C")) { + auto* HUDWidget = static_cast(object); + if (funcName == "Construct") { + if (HUDWidget && HUDWidget->Class) { + FindWidgets(HUDWidget->WidgetTree->RootWidget); + HUDUpdate(false); + } + } + else if (funcName == "Destruct") { + g_StatusWidget = nullptr; + g_GaugesWidget = nullptr; + g_ChangeWheelWidget = nullptr; + g_AbilityWheelWidget = nullptr; + } + } + } + } + }); + } +} +// -- HUD positionning -- +static void HUDUpdate(bool writeLog) { + HUDFixEnabled(g_UIMainMenuWidget, g_UIOffsets, g_UIOffsets , false, 1); + HUDFixEnabled(g_UISettingsWidget, g_UIOffsets, g_UIOffsets, false, 1); + HUDFixEnabled(g_UIDescriptionWidget, g_UIOffsets + 1500.f, 675.f, false, 1); + HUDFixEnabled(g_ProfileWidget, g_UIOffsets, g_UIOffsets, false, 1); + HUDFixEnabled(g_DifficultyWidget, g_UIOffsets, g_UIOffsets, false, 1); + HUDFixEnabled(g_difficultyMenusWidget, g_UIOffsets + 576.f + 100.f, 576, false, 1); + HUDFixEnabled(g_PauseWidget, g_UIOffsets, g_UIOffsets, false, 2); + HUDFixEnabled(g_StatusWidget, g_HUDOffsets, 0.f, false, 1); + HUDFixEnabled(g_GaugesWidget, 0 , g_HUDOffsets + 690.f, false, 1); // Use original left and right offset + for Anchors of type 1, 1, 1, 1 + HUDFixEnabled(g_AbilityWheelWidget,0 , g_HUDOffsets + 78.f, false, 1); // Use original left and right offset + for Anchors of type 1, 1, 1, 1 + HUDFixEnabled(g_ChangeWheelWidget, 0, g_HUDOffsets + 24.f, false, 1); // Use original left and right offset for Anchors of type 1, 1, 1, 1 + HUDFixEnabled(g_CraftingWidget, g_UIOffsets, g_UIOffsets, false, 1); // Use original left and right offset + for Anchors of type 1, 1, 1, 1 +} + +//static bool g_offsetsInitialized = false; +static void HUDFixEnabled(UWidget* widget, float left, float right, bool writeLog, int depth) { + if (writeLog) logger->info("HUD fix {}", g_fix_enabled && g_HUD_fix_enabled ? "enabled" : "disabled"); + if (!widget) return; + + float targetOffsetLeft = g_fix_enabled && g_HUD_fix_enabled ? left : 100.f; + float targetOffsetRight = g_fix_enabled && g_HUD_fix_enabled ? right : 100.f; + + if (widget->IsA(UCanvasPanel::StaticClass()) || (widget->Slot && widget->Slot->IsA(UCanvasPanelSlot::StaticClass()))) // Apply offsets To CanvasPanel + ApplyOffsetsSmart(widget, targetOffsetLeft, targetOffsetRight, depth); +} + +static void FOVFixEnabled() { + if (g_fix_enabled && (g_fov_fix_enabled || g_ultrawide_fix_enabled) && CameraComponentaddress) { + 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) + ctx.xmm0.f32[0] = Maths::CompensateHorizontalFOV(g_FOV_In, g_BaseAspectRatio, 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]; + }); + } + else FOVHook.enable(); + logger->info("FOV fix enabled"); + } + if (!(g_fix_enabled && (g_fov_fix_enabled || g_ultrawide_fix_enabled)) && CameraComponentaddress) { + if (FOVHook) FOVHook.disable(); + logger->info("FOV fix disabled"); + } +} + +static void UltraWideFixEnabled() { + if (g_fix_enabled && g_ultrawide_fix_enabled && CameraComponentaddress) { + if (!UltraWideHook1) { // Hook only once + UltraWideHook1 = safetyhook::create_mid(CameraComponentaddress + 0x15, + [](SafetyHookContext& ctx) { + g_BaseAspectRatio = std::bit_cast(static_cast(ctx.rax)); // Retrieve in real time desired aspect ratio + ctx.rax = std::bit_cast(g_AspectRatio); // Force our aspect ratio + }); + } + else UltraWideHook1.enable(); + if (!UltraWideHook2) { + UltraWideHook2 = safetyhook::create_mid(CameraComponentaddress + 0x39, + [](SafetyHookContext& ctx) { + ctx.rdx = 0; // bOverrideAspectRatioAxisConstraint = 0 + }); + } + else UltraWideHook2.enable(); + logger->info("Ultrawide fix enabled"); + } + if (!(g_fix_enabled && g_ultrawide_fix_enabled) && CameraComponentaddress) { + if (UltraWideHook1) UltraWideHook1.disable(); + if (UltraWideHook2) UltraWideHook2.disable(); + logger->info("Ultrawide fix disabled"); + } +} + +static void CameraDistanceFixEnabled() { + if (g_fix_enabled && g_Camera_fix_enabled && CameraDistanceaddress) { + if (!CameraHook) { // Hook only once + CameraHook = safetyhook::create_mid(CameraDistanceaddress, + [](SafetyHookContext& ctx) { + g_CameraIn = -ctx.xmm0.f32[0]; + ctx.xmm0.f32[0] *= g_CameraMultiplier; + g_CameraOut = -ctx.xmm0.f32[0]; + }); + } + else CameraHook.enable(); + logger->info("Camera distance fix enabled"); + } + if (!(g_fix_enabled && g_Camera_fix_enabled) && CameraDistanceaddress) { + if (CameraHook) CameraHook.disable(); + logger->info("Camera distance fix disabled"); + } +} +// -- Cheats -- +static void EnableCheats(Cheat cheat) { + if (WorldTimedilationaddress && !WorldTimeDilationHook) { + WorldTimeDilationHook = safetyhook::create_mid(WorldTimedilationaddress + 0x19, + [](SafetyHookContext& ctx) { // From AWorldSettings retrieved from world->K2_GetWorldSettings() + ctx.xmm0.f32[0] *= g_TimeDilation_fix_enabled ? g_WorldTimeDilationValue : 1.f; + }); + } + // Enemies time dilation + if (Timedilationaddress && !TimeDilationHook) { + TimeDilationHook = safetyhook::create_mid(Timedilationaddress, + [](SafetyHookContext& ctx) { + if (!ctx.rbx) return; + + UObject* object = (UObject*)ctx.rbx; + if (!object || !object->Class) return; + + if (object->IsA(AStyx3PlayerCharacter::StaticClass())) { + g_Player = static_cast(object); + g_PlayerHealth = g_Player->CurrentHealth; + if (g_GodMode_fix_enabled) { + float percentageHealth = g_Player->GetHealthPercent(); + if (percentageHealth > 0.0001f) + g_Player->CurrentHealth = g_Player->CurrentHealth / percentageHealth; + } + + g_PlayerAmber = g_Player->CurrentAmber; + if (g_Amber_fix_enabled) g_Player->CurrentAmber = g_Player->MaxAmber; + + g_Player->bIsPerceptible = false; + } + if (object->IsA(AStyx3AICharacter::StaticClass())) { + AStyx3AICharacter* enemy = static_cast(object); + if (enemy) enemy->CustomTimeDilation = g_TimeDilation_fix_enabled ? g_AITimeDilationValue : 1.f; // Enemy time dilation + } + }); + } + + if (cheat == Cheat::TimeDilation) logger->info("Time dilation cheat {}", g_TimeDilation_fix_enabled ? "enabled" : "disabled"); + if (cheat == Cheat::GodMode) logger->info("God mode cheat {}", g_GodMode_fix_enabled ? "enabled" : "disabled"); + if (cheat == Cheat::Mana) logger->info("Amber cheat {}", g_Amber_fix_enabled ? "enabled" : "disabled"); + if (cheat == Cheat::Stealth) logger->info("Stealth cheat {}", g_Stealth_fix_enabled ? "enabled" : "disabled"); +} + +// Memory patch fixes +static void DOFFixEnabled() { + if (g_fix_enabled && g_DOF_fix_enabled && DOFaddress) { + Memory::PatchBytes(DOFaddress, "\x31\xFF\x90", 3); // xor edi,edi r.DepthOfFieldQuality = 0 + logger->info("Depth of field fix enabled"); + } + if (!(g_fix_enabled && g_DOF_fix_enabled) && DOFaddress) { + Memory::RestoreBytes(DOFaddress); + logger->info("Depth of field fix disabled"); + } +} + +static void CAFixEnabled() { + if (g_fix_enabled && g_CA_fix_enabled && CAaddress) { + Memory::PatchBytes(CAaddress, "\x90\x90", 2); // NOP x 2 r.SceneColorFringeQuality = 0 + logger->info("Chromatics aberrations fix enabled"); + } + if (!(g_fix_enabled && g_CA_fix_enabled) && CAaddress) { + Memory::RestoreBytes(CAaddress); + logger->info("Chromatics aberrations fix disabled"); + } +} + +static void VignettingFixEnabled() { + if (g_fix_enabled && g_Vignetting_fix_enabled && Vignettingaddress) { + Memory::PatchBytes(Vignettingaddress, "\x31\xC9", 2); // xor ecx,ecx r.Tonemapper.Quality=0 + logger->info("Vignetting fix enabled"); + } + if (!(g_fix_enabled && g_Vignetting_fix_enabled) && Vignettingaddress) { + Memory::RestoreBytes(Vignettingaddress); + logger->info("Vignetting fix disabled"); + } +} + +static void FogFixEnabled() { + if (g_Fog_fix_enabled) + DumpUIAnalysis(logger); + if (g_fix_enabled && g_Fog_fix_enabled && Fogaddress) + Memory::PatchBytes(Fogaddress + 0xd, "\xEB", 1); // jmp -> r.Fog 0 + if (!(g_fix_enabled && g_Fog_fix_enabled) && Fogaddress) + Memory::RestoreBytes(Fogaddress + 0xd); + + if (Fogaddress) { + if (g_fix_enabled && g_Fog_fix_enabled) logger->info("Fog fix enabled"); + else logger->info("Fog fix 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) { + if (reason == DLL_PROCESS_ATTACH) { + logger = InitializeLogger("Styx: Blades Of Green", PLUGIN_LOG); + logger->info("Plugin {} loaded.", PLUGIN_NAME); + } + else if (reason == DLL_PROCESS_DETACH) { + logger->info("Plugin {} unloaded.", PLUGIN_NAME); + spdlog::drop_all(); + } + return TRUE; +} \ No newline at end of file