From d710d6051953a84b930bc2fd71a5e607e9ca674d Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Fri, 31 Jul 2026 01:10:43 +0200 Subject: [PATCH] Updated the kNoBorder fix to cover all non-VR versions --- source/Enderal DLL/src/Main.cpp | 3 +- .../src/Patches/MenuAspectRatioFix.h | 55 +++++++++++++------ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/source/Enderal DLL/src/Main.cpp b/source/Enderal DLL/src/Main.cpp index 6efe29b1a..8c115a775 100644 --- a/source/Enderal DLL/src/Main.cpp +++ b/source/Enderal DLL/src/Main.cpp @@ -255,8 +255,7 @@ SKSEPluginLoad(const LoadInterface* skse) { logger::info("Initializing Stay At The System Page..."); JournalMenuEx::InstallHooks(); } - // Menus are only stretched to fill the viewport since 1.6.1130 - if (g_settings.at("MenuAspectRatioFix") && REL::Module::get().version() >= REL::Version(1, 6, 1130, 0)) { + if (g_settings.at("MenuAspectRatioFix")) { MenuAspectRatioFix::Install(); } LoadScreenFrameFix::Install(g_settings.at("LoadScreenFrameFix")); diff --git a/source/Enderal DLL/src/Patches/MenuAspectRatioFix.h b/source/Enderal DLL/src/Patches/MenuAspectRatioFix.h index c5187571a..17dbc6200 100644 --- a/source/Enderal DLL/src/Patches/MenuAspectRatioFix.h +++ b/source/Enderal DLL/src/Patches/MenuAspectRatioFix.h @@ -1,17 +1,21 @@ #pragma once -// Restores the Scaleform scale modes menus were loaded with before Skyrim 1.6.1130, and stops -// kNoBorder from cropping them on displays wider than 16:9. +// Two independent corrections to the Scaleform scale mode menus are loaded with, both applied +// from the same BSScaleformManager::LoadMovie hook: // -// 1.6.1130 switched nearly every menu to kExactFit, which stretches the movie across the whole -// viewport instead of preserving its aspect ratio. Enderal's menus - including the SkyUI-derived -// widescreen versions shipped with them - are authored for the old modes, so on anything that is -// not 16:9 they come out distorted. +// 1. Restore the modes used before 1.6.1130. That build switched nearly every menu to +// kExactFit, which stretches the movie across the whole viewport instead of preserving its +// aspect ratio. Enderal's menus - including the SkyUI-derived widescreen versions shipped +// with them - are authored for the old modes, so on anything that is not 16:9 they come out +// distorted. Only 1.6.1130 and later need this; see g_restoreScaleModes. +// 2. Stop kNoBorder from cropping menus on displays wider than 16:9. That one predates +// 1.6.1130 and is needed on every runtime, so it is not version gated; see ClampToViewport. // -// The table below is what 1.6.640 passes to BSScaleformManager::LoadMovie, plus BookMenu and -// GiftMenu, which 1.6.1130 left on kNoBorder and which need the same clamp. Menus that want no -// help (Book, Console, CreditsMenu, CursorMenu, FaderMenu, StartMenu) are absent and keep -// whatever the caller asked for. +// The table below is what 1.6.640 passes to LoadMovie, plus BookMenu and GiftMenu, which 1.6.1130 +// left on kNoBorder and which need the clamp. Menus that want no help (Book, Console, CreditsMenu, +// CursorMenu, FaderMenu, StartMenu) are absent and keep whatever the caller asked for. It doubles +// as the list of menus this patch is allowed to touch at all, so on older runtimes - where the +// engine already passes these very modes - a third-party menu loaded with kNoBorder is left alone. // // Based on Widescreen Scale Removed by SkyHorizon (GPL-3.0) // https://www.nexusmods.com/skyrimspecialedition/mods/136793 @@ -19,6 +23,10 @@ namespace MenuAspectRatioFix { using ScaleModeType = RE::GFxMovieView::ScaleModeType; + // True on the runtimes that stretch menus to fill the viewport, i.e. 1.6.1130 and later. Below + // that the engine already asks for the modes in the table, so only the clamp has work to do. + inline bool g_restoreScaleModes = false; + std::optional GetScaleMode(const char* a_fileName) { static const std::map modes{ @@ -88,7 +96,7 @@ namespace MenuAspectRatioFix { if (a_fileName && a_fileName[0]) { if (const auto mode = GetScaleMode(a_fileName); mode) { - a_mode = ClampToViewport(*mode); + a_mode = ClampToViewport(g_restoreScaleModes ? *mode : a_mode); } } @@ -100,21 +108,32 @@ namespace MenuAspectRatioFix void Install() { - // Checked: 1.6.640, 1.6.659, 1.6.1130, 1.6.1170, 1.6.1179 + g_restoreScaleModes = REL::Module::get().version() >= REL::Version(1, 6, 1130, 0); + + // Checked: 1.5.97, 1.6.640, 1.6.659, 1.6.1130, 1.6.1170, 1.6.1179 const auto target = REL::RelocationID(80302, 82325).address(); - // mov rax,rsp / mov qword ptr [rax+10h],rdx - relocated into the trampoline by the hook, - // so bail out rather than corrupt the function if a future runtime changes the prologue - constexpr std::uint8_t prologue[]{ 0x48, 0x8B, 0xC4, 0x48, 0x89, 0x50, 0x10 }; + // mov rax,rsp followed by one argument spill - 7 bytes of position-independent code, which + // the hook relocates into the trampoline. The spilled argument differs between the runtime + // families, so accept either and bail out rather than corrupt the function if a future + // build changes the prologue again. + constexpr std::uint8_t prologueAE[]{ 0x48, 0x8B, 0xC4, 0x48, 0x89, 0x50, 0x10 }; // mov [rax+10h],rdx + constexpr std::uint8_t prologueSE[]{ 0x48, 0x8B, 0xC4, 0x4C, 0x89, 0x40, 0x18 }; // mov [rax+18h],r8 - if (!target || std::memcmp(reinterpret_cast(target), prologue, sizeof(prologue)) != 0) { + static_assert(sizeof(prologueAE) == sizeof(prologueSE)); + + const auto prologue = reinterpret_cast(target); + + if (!target || + (std::memcmp(prologue, prologueAE, sizeof(prologueAE)) != 0 && + std::memcmp(prologue, prologueSE, sizeof(prologueSE)) != 0)) { logger::error("Unexpected BSScaleformManager::LoadMovie prologue, menu aspect ratio fix is disabled"); return; } SKSE::AllocTrampoline(64); - SKSE::stl::hook_function_prologue(target); + SKSE::stl::hook_function_prologue(target); - logger::info("Applied menu aspect ratio fix"); + logger::info("Applied menu aspect ratio fix{}", g_restoreScaleModes ? "" : " (viewport clamp only)"); } }