Files

312 lines
12 KiB
C++
Raw Permalink Normal View History

#include "Memory.hpp";
#include "Maths.hpp";
#include "ObfuscateString.h"
#include <string>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <safetyhook.hpp>
// Constants
const std::string PLUGIN_NAME = "Starfield";
const std::string PLUGIN_LOG = "Starfield.log";
const std::string gameExecutable = "Starfield.exe";
// Logger
std::shared_ptr<spdlog::logger> 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");
2025-08-20 10:53:58 +02:00
DialogFOVAddress = Memory::AOBScan(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<uintptr_t>(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");
2025-08-20 10:53:58 +02:00
WeaponFOVAddress = Memory::AOBScan(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<uintptr_t>(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");
2025-08-20 10:53:58 +02:00
HUDAddress = Memory::AOBScan(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<uintptr_t>(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");
2025-08-20 10:53:58 +02:00
PhotomodeAddress = Memory::AOBScan(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<uintptr_t>(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<uint32_t*>(&safeZoneX); // X axis
ctx.r8 = *reinterpret_cast<uint32_t*>(&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;
}