#include "Memory.hpp"; #include "Maths.hpp"; #include "ObfuscateString.h" #include #include #include #include // Constants const std::string PLUGIN_NAME = "Starfield"; const std::string PLUGIN_LOG = "Starfield.log"; const std::string gameExecutable = "Starfield.exe"; // 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; // Plugin states static bool AOBScanDone = false; static bool g_fix_enabled = false; static bool g_dialog_fov_fix_enabled = false; static bool g_weapon_fov_fix_enabled = false; static bool g_HUD_fix_enabled = false; static bool g_photomode_fix_enabled = false; static int g_AdditionalDialogFOVValue = 0; static int g_AdditionalWeaponFOVValue = 0; static float g_HUDXValue = 0; static float g_HUDYValue = 0; // Shared values static float g_Dialog_FOV_In = 0; static float g_Dialog_FOV_Out = 0; static float g_Weapon_FOV_In = 0; static float g_Weapon_FOV_Out = 0; // AOB Scan pointers static uint8_t* DialogFOVAddress = nullptr; static uint8_t* WeaponFOVAddress = nullptr; static uint8_t* HUDAddress = nullptr; static uint8_t* PhotomodeAddress = nullptr; // Hooking static SafetyHookMid DialogFOVHook{}; static SafetyHookMid WeaponFOVHook{}; static SafetyHookMid HUDHook{}; // Prototypes static void DialogFOVFixEnabled(bool fix_enabled); static void WeaponFOVFixEnabled(bool fix_enabled); static void HUDFixEnabled(bool fix_enabled); static void PhotomodeFixEnabled(bool fix_enabled); extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled) { g_fix_enabled = enabled; if (g_fix_enabled && !AOBScanDone) { logger->info("--------------- AOB scan started ---------------"); // === AOB Scans === if (!DialogFOVAddress) { constexpr auto DialogFOVPattern = make_obfuscated<0x4A>("E9 ?? ?? ?? ?? 49 ?? ?? E8 ?? ?? ?? ?? 84 ?? 0F 84 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 89"); DialogFOVAddress = Memory::aob_scan(gameExecutable, DialogFOVPattern.decrypt(), PAGE_EXECUTE_READ); if (DialogFOVAddress) { logger->info("Dialog FOV signature found at address: 0x{:X}.", reinterpret_cast(DialogFOVAddress)); } else logger->warn("Dialog FOV signature not found. Maybe the game was updated."); } if (!WeaponFOVAddress) { constexpr auto WeaponFOVPattern = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 74"); WeaponFOVAddress = Memory::aob_scan(gameExecutable, WeaponFOVPattern.decrypt(), PAGE_EXECUTE_READ); if (WeaponFOVAddress) { logger->info("Weapon FOV signature found at address: 0x{:X}.", reinterpret_cast(WeaponFOVAddress)); } else logger->warn("Weapon FOV signature not found. Maybe the game was updated."); } if (!HUDAddress) { constexpr auto HUDPattern = make_obfuscated<0x4A>("C4 61 ?? ?? ?? ?? ?? ?? ?? C4 61 ?? ?? ?? ?? ?? ?? ?? 45 ?? ?? 74"); HUDAddress = Memory::aob_scan(gameExecutable, HUDPattern.decrypt(), PAGE_EXECUTE_READ); if (HUDAddress) { logger->info("HUD safe zone signature found at address: 0x{:X}.", reinterpret_cast(HUDAddress)); HUDAddress += 0x12; } else logger->warn("HUD safe zone signature not found. Maybe the game was updated."); } if (!PhotomodeAddress) { constexpr auto PhotomodePattern = make_obfuscated<0x4A>("74 ?? 0F BA ?? ?? 89 87 ?? ?? ?? ?? C6 87 ?? ?? ?? ?? ?? E8"); PhotomodeAddress = Memory::aob_scan(gameExecutable, PhotomodePattern.decrypt(), PAGE_EXECUTE_READ); if (PhotomodeAddress) { logger->info("Photo mode signature found at address: 0x{:X}.", reinterpret_cast(PhotomodeAddress)); } else logger->warn("Photo mode signature not found. Maybe the game was updated."); if (DialogFOVAddress && WeaponFOVAddress && HUDAddress && PhotomodeAddress) { logger->info("All AOB signatures found. Ready to patch..."); logger->info("--------------- AOB scan finished ---------------"); AOBScanDone = true; } else logger->warn("Some AOB signatures could not be found. Fixes may be partially unavailable."); } } // === Activer ou désactiver les patchs === if (DialogFOVAddress && WeaponFOVAddress && HUDAddress && PhotomodeAddress) { if (g_fix_enabled) { DialogFOVFixEnabled(g_dialog_fov_fix_enabled); WeaponFOVFixEnabled(g_weapon_fov_fix_enabled); HUDFixEnabled(g_HUD_fix_enabled); PhotomodeFixEnabled(g_photomode_fix_enabled); } else { DialogFOVFixEnabled(false); WeaponFOVFixEnabled(false); HUDFixEnabled(false); PhotomodeFixEnabled(false); logger->info("All fixes disabled."); } } } // Setters for Reshade addon call extern "C" __declspec(dllexport) void SetDialogFOVFixEnabled(bool enabled, bool init) { g_dialog_fov_fix_enabled = enabled; if (!init) DialogFOVFixEnabled(g_dialog_fov_fix_enabled); } extern "C" __declspec(dllexport) void SetWeaponFOVFixEnabled(bool enabled, bool init) { g_weapon_fov_fix_enabled = enabled; if (!init) WeaponFOVFixEnabled(g_weapon_fov_fix_enabled); } extern "C" __declspec(dllexport) void SetHUDFixEnabled(bool enabled, bool init) { g_HUD_fix_enabled = enabled; if (!init) HUDFixEnabled(g_HUD_fix_enabled); } extern "C" __declspec(dllexport) void SetPhotoModeFixEnabled(bool enabled, bool init) { g_photomode_fix_enabled = enabled; if (!init) PhotomodeFixEnabled(g_photomode_fix_enabled); } extern "C" __declspec(dllexport) void SetDialogFOV(int fov) { g_AdditionalDialogFOVValue = fov; } extern "C" __declspec(dllexport) void SetWeaponFOV(int fov) { g_AdditionalWeaponFOVValue = fov; } extern "C" __declspec(dllexport) void SetHUDX(int HUDX) { g_HUDXValue = HUDX; } extern "C" __declspec(dllexport) void SetHUDY(int HUDY) { g_HUDYValue = HUDY; } // Getters for Reshade addon call extern "C" __declspec(dllexport) float GetDialogFOVIn() { return g_Dialog_FOV_In; } extern "C" __declspec(dllexport) float GetDialogFOVOut() { return g_Dialog_FOV_Out; } extern "C" __declspec(dllexport) float GetWeaponFOVIn() { return g_Weapon_FOV_In; } extern "C" __declspec(dllexport) float GetWeaponFOVOut() { return g_Weapon_FOV_Out; } // Assembly code injections functions static void DialogFOVFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && DialogFOVAddress != nullptr) { if (!DialogFOVHook) { // Hook only once DialogFOVHook = safetyhook::create_mid(DialogFOVAddress, [](SafetyHookContext& ctx) { g_Dialog_FOV_In = ctx.xmm6.f32[0]; g_Dialog_FOV_Out = ctx.xmm6.f32[0] += (g_dialog_fov_fix_enabled ? g_AdditionalDialogFOVValue : 0); // World FOV }); } else DialogFOVHook.enable(); logger->info("FOV fix enabled"); } if (!fix_enabled && DialogFOVHook) { DialogFOVHook.disable(); logger->info("FOV fix disabled"); } } static void WeaponFOVFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && WeaponFOVAddress != nullptr) { if (!WeaponFOVHook) { WeaponFOVHook = safetyhook::create_mid(WeaponFOVAddress, [](SafetyHookContext& ctx) { g_Weapon_FOV_In = ctx.xmm0.f32[0]; g_Weapon_FOV_Out = ctx.xmm0.f32[0] += (g_weapon_fov_fix_enabled ? g_AdditionalWeaponFOVValue : 0); }); } else WeaponFOVHook.enable(); logger->info("Weapon FOV fix enabled"); } if (!fix_enabled && WeaponFOVHook) { WeaponFOVHook.disable(); logger->info("Weapon FOV fix disabled"); } } static void HUDFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && HUDAddress) { if (!HUDHook) { HUDHook = safetyhook::create_mid(HUDAddress, [](SafetyHookContext& ctx) { int safeZoneX = (screenWidth / 2) * (g_HUDXValue / 100); int safeZoneY = (screenHeight / 2) * (g_HUDYValue / 100); ctx.r9 = *reinterpret_cast(&safeZoneX); // X axis ctx.r8 = *reinterpret_cast(&safeZoneY); // Y axis }); } else HUDHook.enable(); logger->info("HUD fix enabled"); } if (!fix_enabled && HUDHook) { HUDHook.disable(); logger->info("HUD fix disabled"); } } static void PhotomodeFixEnabled(bool fix_enabled) { if (g_fix_enabled && fix_enabled && PhotomodeAddress != nullptr) { Memory::PatchBytes(PhotomodeAddress, "\xEB", 1); // je ==> jmp logger->info("Photomode fix enabled"); } if (!fix_enabled && PhotomodeAddress) { Memory::RestoreBytes(PhotomodeAddress); logger->info("Photomode fix disabled"); } } // spdlog init with specific format static void InitializeLogger() { try { logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true); spdlog::set_default_logger(logger); // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v"); spdlog::set_level(spdlog::level::debug); logger->flush_on(spdlog::level::debug); // Flush automatically } catch (const spdlog::spdlog_ex& ex) { std::string plugin_error_message = "Could not open " + PLUGIN_LOG; MessageBoxA(nullptr, plugin_error_message.c_str(), "Logger Error", MB_ICONERROR | MB_OK); } } // Entry point BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { InitializeLogger(); logger->info("Plugin {} loaded.", PLUGIN_NAME); } else if (reason == DLL_PROCESS_DETACH) { logger->info("Plugin {} unloaded.", PLUGIN_NAME); spdlog::drop_all(); } return TRUE; }