#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); //Starfield.exe + BBA13E - A8 01 - test al, 01 //Starfield.exe + BBA140 - 74 0D - je Starfield.exe + BBA14F //Starfield.exe + BBA142 - C5 FA 10 35 FE 15 23 05 - vmovss xmm6, [Starfield.exe + 5DEB748] //Starfield.exe + BBA14A - E9 01 01 00 00 - jmp Starfield.exe + BBA250 //Starfield.exe + BBA14F - 49 8B CF - mov rcx, r15 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); //Starfield.exe + 12D9F27 - C5 F8 28 C2 - vmovaps xmm0, xmm2 //Starfield.exe + 12D9F2B - 48 8B 05 5E C4 BD 04 - mov rax, [Starfield.exe + 5EB6390] //Starfield.exe + 12D9F32 - C5 FA 11 80 E0 02 00 00 - vmovss[rax + 000002E0], xmm0 //Starfield.exe + 12D9F3A - 80 3D EB A0 AD 04 00 - cmp byte ptr[Starfield.exe + 5DB402C], 00 //Starfield.exe + 12D9F41 - 48 8B 05 10 32 BB 04 - mov rax, [Starfield.exe + 5E8D158] 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); //Starfield.exe + 24D6D3B - C4 61 FA 2C 05 3C 28 38 03 - vcvttss2si r8, [Starfield.exe + 5859580] //Starfield.exe + 24D6D44 - C4 61 FA 2C 0D 13 28 38 03 - vcvttss2si r9, [Starfield.exe + 5859560] //Starfield.exe + 24D6D4D - 45 84 DB - test r11b, r11b //Starfield.exe + 24D6D50 - 74 15 - je Starfield.exe + 24D6D67 //Starfield.exe + 24D6D52 - 8B 42 38 - mov eax, [rdx + 38] 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); //Starfield.exe + 159D76C - C6 87 D2 00 00 00 01 - mov byte ptr[rdi + 000000D2], 01 //Starfield.exe + 159D773 - 40 38 35 96 1F 87 04 - cmp[Starfield.exe + 5E0F710], sil //Starfield.exe + 159D77A - 74 11 - je Starfield.exe + 159D78D //Starfield.exe + 159D77C - 0F BA E8 1B - bts eax, 1B //Starfield.exe + 159D780 - 89 87 C0 00 00 00 - mov[rdi + 000000C0], eax 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; }