Updated the kNoBorder fix to cover all non-VR versions

This commit is contained in:
Eddoursul 2026-07-31 01:10:43 +02:00
parent 9cc519a8aa
commit d710d60519
2 changed files with 38 additions and 20 deletions

View File

@ -255,8 +255,7 @@ SKSEPluginLoad(const LoadInterface* skse) {
logger::info("Initializing Stay At The System Page..."); logger::info("Initializing Stay At The System Page...");
JournalMenuEx::InstallHooks(); JournalMenuEx::InstallHooks();
} }
// Menus are only stretched to fill the viewport since 1.6.1130 if (g_settings.at("MenuAspectRatioFix")) {
if (g_settings.at("MenuAspectRatioFix") && REL::Module::get().version() >= REL::Version(1, 6, 1130, 0)) {
MenuAspectRatioFix::Install(); MenuAspectRatioFix::Install();
} }
LoadScreenFrameFix::Install(g_settings.at("LoadScreenFrameFix")); LoadScreenFrameFix::Install(g_settings.at("LoadScreenFrameFix"));

View File

@ -1,17 +1,21 @@
#pragma once #pragma once
// Restores the Scaleform scale modes menus were loaded with before Skyrim 1.6.1130, and stops // Two independent corrections to the Scaleform scale mode menus are loaded with, both applied
// kNoBorder from cropping them on displays wider than 16:9. // from the same BSScaleformManager::LoadMovie hook:
// //
// 1.6.1130 switched nearly every menu to kExactFit, which stretches the movie across the whole // 1. Restore the modes used before 1.6.1130. That build switched nearly every menu to
// viewport instead of preserving its aspect ratio. Enderal's menus - including the SkyUI-derived // kExactFit, which stretches the movie across the whole viewport instead of preserving its
// widescreen versions shipped with them - are authored for the old modes, so on anything that is // aspect ratio. Enderal's menus - including the SkyUI-derived widescreen versions shipped
// not 16:9 they come out distorted. // 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 // The table below is what 1.6.640 passes to LoadMovie, plus BookMenu and GiftMenu, which 1.6.1130
// GiftMenu, which 1.6.1130 left on kNoBorder and which need the same clamp. Menus that want no // left on kNoBorder and which need the clamp. Menus that want no help (Book, Console, CreditsMenu,
// help (Book, Console, CreditsMenu, CursorMenu, FaderMenu, StartMenu) are absent and keep // CursorMenu, FaderMenu, StartMenu) are absent and keep whatever the caller asked for. It doubles
// whatever the caller asked for. // 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) // Based on Widescreen Scale Removed by SkyHorizon (GPL-3.0)
// https://www.nexusmods.com/skyrimspecialedition/mods/136793 // https://www.nexusmods.com/skyrimspecialedition/mods/136793
@ -19,6 +23,10 @@ namespace MenuAspectRatioFix
{ {
using ScaleModeType = RE::GFxMovieView::ScaleModeType; 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<ScaleModeType> GetScaleMode(const char* a_fileName) std::optional<ScaleModeType> GetScaleMode(const char* a_fileName)
{ {
static const std::map<std::string_view, ScaleModeType> modes{ static const std::map<std::string_view, ScaleModeType> modes{
@ -88,7 +96,7 @@ namespace MenuAspectRatioFix
{ {
if (a_fileName && a_fileName[0]) { if (a_fileName && a_fileName[0]) {
if (const auto mode = GetScaleMode(a_fileName); mode) { 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() 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(); const auto target = REL::RelocationID(80302, 82325).address();
// mov rax,rsp / mov qword ptr [rax+10h],rdx - relocated into the trampoline by the hook, // mov rax,rsp followed by one argument spill - 7 bytes of position-independent code, which
// so bail out rather than corrupt the function if a future runtime changes the prologue // the hook relocates into the trampoline. The spilled argument differs between the runtime
constexpr std::uint8_t prologue[]{ 0x48, 0x8B, 0xC4, 0x48, 0x89, 0x50, 0x10 }; // 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<const void*>(target), prologue, sizeof(prologue)) != 0) { static_assert(sizeof(prologueAE) == sizeof(prologueSE));
const auto prologue = reinterpret_cast<const void*>(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"); logger::error("Unexpected BSScaleformManager::LoadMovie prologue, menu aspect ratio fix is disabled");
return; return;
} }
SKSE::AllocTrampoline(64); SKSE::AllocTrampoline(64);
SKSE::stl::hook_function_prologue<LoadMovie, sizeof(prologue)>(target); SKSE::stl::hook_function_prologue<LoadMovie, sizeof(prologueAE)>(target);
logger::info("Applied menu aspect ratio fix"); logger::info("Applied menu aspect ratio fix{}", g_restoreScaleModes ? "" : " (viewport clamp only)");
} }
} }