From 7692789c3855384fc039908ac758b7b3b0a22a53 Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Mon, 19 Jan 2026 04:42:24 +0100 Subject: [PATCH] Updated CommonLibSSE, added GOG VRAM Leak Fix by DwemerEngineer --- Enderal SE v2.1.4.1 Changelog.txt | 5 +++ SKSE/Plugins/EnderalSE.ini | 1 + source/Enderal DLL/CMakeLists.txt | 18 +++++----- source/Enderal DLL/LICENSE | 3 ++ source/Enderal DLL/src/Main.cpp | 18 +++++++--- source/Enderal DLL/src/PCH.h | 9 +++++ source/Enderal DLL/src/PapyrusFunctions.h | 2 +- .../Enderal DLL/src/Patches/GogVramLeakFix.h | 36 +++++++++++++++++++ source/Enderal DLL/src/Util.h | 13 +++---- 9 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 source/Enderal DLL/LICENSE create mode 100644 source/Enderal DLL/src/Patches/GogVramLeakFix.h diff --git a/Enderal SE v2.1.4.1 Changelog.txt b/Enderal SE v2.1.4.1 Changelog.txt index 2bfa93e03..f38dd3983 100644 --- a/Enderal SE v2.1.4.1 Changelog.txt +++ b/Enderal SE v2.1.4.1 Changelog.txt @@ -11,6 +11,11 @@ Beware, spoilers ahead! - Removed the Blueprint keyword from the Dreamflower Elixir Recipe to avoid sorting it into the blueprint storage (reported by Fubz). - Fixed an exploit allowing to collect more gold from containers (reported by LevinLozenges). +Engine patches: +- Automatically detects and warns about form type collisions when using Skyrim mods. +- `Enderal - Forgotten Stories.esm` is always force-loaded. +- Included GOG Memory (VRAM) Leak Fix by DwemerEngineer. + Lirk: - Fixed several issues with weapon impact sounds. - Fixed normals in agnodwall.nif. diff --git a/SKSE/Plugins/EnderalSE.ini b/SKSE/Plugins/EnderalSE.ini index 2fcab40e8..c0b2766bc 100644 --- a/SKSE/Plugins/EnderalSE.ini +++ b/SKSE/Plugins/EnderalSE.ini @@ -8,3 +8,4 @@ ForceBorderless = true AttachLightHitEffectCrashFix = true AutoScaleHeroMenu = true WarnFormTypeCollisions = true +GogVramLeakFix = true diff --git a/source/Enderal DLL/CMakeLists.txt b/source/Enderal DLL/CMakeLists.txt index d6d2f675e..c6919f1b4 100644 --- a/source/Enderal DLL/CMakeLists.txt +++ b/source/Enderal DLL/CMakeLists.txt @@ -80,7 +80,7 @@ add_library("Microsoft::DirectXTK" ALIAS "DirectXTK") # simpleini FetchContent_Declare( simpleini - URL "https://github.com/brofield/simpleini/archive/refs/tags/v4.22.tar.gz" + URL "https://github.com/brofield/simpleini/archive/refs/tags/v4.25.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP 1 ) FetchContent_MakeAvailable(simpleini) @@ -89,7 +89,7 @@ INCLUDE_DIRECTORIES(${simpleini_SOURCE_DIR}) # rapidcsv FetchContent_Declare( rapidcsv - URL "https://github.com/d99kris/rapidcsv/archive/refs/tags/v8.87.tar.gz" + URL "https://github.com/d99kris/rapidcsv/archive/refs/tags/v8.90.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP 1 OVERRIDE_FIND_PACKAGE ) @@ -97,11 +97,11 @@ FetchContent_MakeAvailable(rapidcsv) set(RAPIDCSV_INCLUDE_DIRS ${rapidcsv_SOURCE_DIR}/src) # spdlog -set(SPDLOG_INSTALL ON CACHE INTERNAL "Install SPDLOG for CommonLibSSE") -set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use std::format in SPDLOG, not fmt") +set(SPDLOG_INSTALL ON CACHE BOOL " " FORCE) +set(SPDLOG_USE_STD_FORMAT ON CACHE BOOL " " FORCE) FetchContent_Declare( spdlog - URL "https://github.com/gabime/spdlog/archive/refs/tags/v1.15.3.tar.gz" + URL "https://github.com/gabime/spdlog/archive/refs/tags/v1.17.0.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP 1 OVERRIDE_FIND_PACKAGE ) @@ -110,7 +110,7 @@ FetchContent_MakeAvailable(spdlog) # xbyak FetchContent_Declare( xbyak - URL "https://github.com/herumi/xbyak/archive/v7.28.tar.gz" + URL "https://github.com/herumi/xbyak/archive/v7.30.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP 1 ) FetchContent_MakeAvailable(xbyak) @@ -121,11 +121,11 @@ set(ENABLE_SKYRIM_SE ON CACHE BOOL " " FORCE) set(ENABLE_SKYRIM_AE ON CACHE BOOL " " FORCE) set(ENABLE_SKYRIM_VR ON CACHE BOOL " " FORCE) set(BUILD_TESTS OFF CACHE BOOL " " FORCE) -message(STATUS "Fetching CommonLibSSE-NG (5e5417e3585c9434295e919bdda27737244e9c5a)...") +message(STATUS "Fetching CommonLibSSE-NG...") FetchContent_Declare( CommonLibSSE - GIT_REPOSITORY https://github.com/eddoursul/CommonLibVR.git - GIT_TAG 5e5417e3585c9434295e919bdda27737244e9c5a + GIT_REPOSITORY https://github.com/alandtse/CommonLibVR + GIT_TAG aacbd76c01bff9381e253ffdfcb4f9d5f263f1df ) FetchContent_MakeAvailable(CommonLibSSE) diff --git a/source/Enderal DLL/LICENSE b/source/Enderal DLL/LICENSE new file mode 100644 index 000000000..bff2fca41 --- /dev/null +++ b/source/Enderal DLL/LICENSE @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 +size 35149 diff --git a/source/Enderal DLL/src/Main.cpp b/source/Enderal DLL/src/Main.cpp index 53d58af3c..20b630260 100644 --- a/source/Enderal DLL/src/Main.cpp +++ b/source/Enderal DLL/src/Main.cpp @@ -15,6 +15,7 @@ #include "Patches/AttachLightHitEffectCrash.h" #include "Patches/PluginsTxtPatch.h" #include "Patches/FormTypeCollisionDetector.h" +#include "Patches/GogVramLeakFix.h" using namespace SKSE; @@ -28,7 +29,8 @@ static std::map g_settings{ { "VideoInterruptPatch", true }, { "ForceBorderless", true }, { "AttachLightHitEffectCrashFix", true }, - { "AutoScaleHeroMenu", true } + { "AutoScaleHeroMenu", true }, + { "GogVramLeakFix", true } }; namespace { @@ -69,6 +71,11 @@ namespace { if (message->type == MessagingInterface::kPostLoad) { if (!REL::Module::IsVR()) { + if (g_settings.at("GogVramLeakFix") && REL::Module::get().version() == REL::Version(1, 6, 1179, 0) && !GetLoadInterface()->GetPluginInfo("GOG-Leak-Fix")) { + logger::info("Installing GOG VRAM leak fix..."); + GogVramLeakFix::Install(); + } + if (g_settings.at("AttachLightHitEffectCrashFix")) { logger::info("Installing light attach crash fix..."); AttachLightHitEffectCrash::Install(); @@ -188,12 +195,15 @@ SKSEPluginLoad(const LoadInterface* skse) { auto* plugin = PluginDeclaration::GetSingleton(); auto version = plugin->GetVersion(); - logger::info("{} {} ({}) is loading...", plugin->GetName(), version, SemVerToInt({ version.major(), version.minor(), version.patch(), version.build() })); + auto versionStr = version.build() > 0 + ? std::format("{}.{}.{}.{}", version.major(), version.minor(), version.patch(), version.build()) + : std::format("{}.{}.{}", version.major(), version.minor(), version.patch()); + logger::info("{} {} ({}) is loading...", plugin->GetName(), versionStr, SemVerToInt(version.major(), version.minor(), version.patch(), version.build())); - Init(skse); + Init(skse, false); InitializeMessaging(); - if (g_settings.at("WarnFormTypeCollisions")) { + if (g_settings.at("WarnFormTypeCollisions") && !!REL::Module::IsVR()) { FormTypeCollisionDetector::Install(); } diff --git a/source/Enderal DLL/src/PCH.h b/source/Enderal DLL/src/PCH.h index 5dd7b6498..cbc4a6c9b 100644 --- a/source/Enderal DLL/src/PCH.h +++ b/source/Enderal DLL/src/PCH.h @@ -1,5 +1,7 @@ #pragma once +#define SPDLOG_COMPILED_LIB + #include #include #include @@ -28,4 +30,11 @@ namespace SKSE::stl { asm_replace(a_from, a_size, reinterpret_cast(T::func)); } + + template + void write_thunk_jump(std::uintptr_t a_src) + { + auto& trampoline = SKSE::GetTrampoline(); + T::func = trampoline.write_branch(a_src, T::thunk); + } } diff --git a/source/Enderal DLL/src/PapyrusFunctions.h b/source/Enderal DLL/src/PapyrusFunctions.h index 9ff493713..e878e564d 100644 --- a/source/Enderal DLL/src/PapyrusFunctions.h +++ b/source/Enderal DLL/src/PapyrusFunctions.h @@ -59,7 +59,7 @@ namespace Papyrus::PapyrusFunctions { const auto pluginVersion = SKSE::PluginDeclaration::GetSingleton()->GetVersion(); - return SemVerToInt({ pluginVersion.major(), pluginVersion.minor(), pluginVersion.patch(), pluginVersion.build() }); + return SemVerToInt(pluginVersion.major(), pluginVersion.minor(), pluginVersion.patch(), pluginVersion.build()); } inline RE::TESObjectREFR* GetCurrentContainer(RE::StaticFunctionTag*) diff --git a/source/Enderal DLL/src/Patches/GogVramLeakFix.h b/source/Enderal DLL/src/Patches/GogVramLeakFix.h new file mode 100644 index 000000000..99ca829b0 --- /dev/null +++ b/source/Enderal DLL/src/Patches/GogVramLeakFix.h @@ -0,0 +1,36 @@ +#pragma once + +// Fix GOG VRAM leak by properly releasing Direct3D texture resources +// Based on https://github.com/SaneEngineer/GOG-Leak-Fix +namespace GogVramLeakFix +{ + struct ReplaceRelease + { + static void thunk(RE::BSGraphics::Renderer* a_renderer, RE::BSGraphics::Texture* a_texture) + { + if (_InterlockedExchangeAdd(&a_texture->unk20, 0xFFFFFFFF) == 1) { + if (a_texture->resourceView) { + a_texture->resourceView->Release(); + } + if (a_texture->texture) { + a_texture->texture->Release(); + } + if (a_texture->unk08) { + reinterpret_cast(a_texture->unk08)->Release(); + } + + REL::Relocation NiMemFree{ REL::RelocationID(102158, 109588) }; + NiMemFree(a_texture, 0x28); + } + } + + static inline REL::Relocation func; + }; + + void Install() + { + SKSE::AllocTrampoline(14); + SKSE::stl::write_thunk_jump(REL::RelocationID(75527, 77322).address()); + logger::info("Initialized GOG VRAM Leak Fix"); + } +} diff --git a/source/Enderal DLL/src/Util.h b/source/Enderal DLL/src/Util.h index 88f9b302e..d10d1b84d 100644 --- a/source/Enderal DLL/src/Util.h +++ b/source/Enderal DLL/src/Util.h @@ -23,14 +23,9 @@ inline uint8_t NewGameCount(bool increment = false) return g_NewGameStarted; } -inline std::uint32_t SemVerToInt(std::vector numbers) +inline std::uint32_t SemVerToInt(std::uint16_t major, std::uint16_t minor, std::uint16_t patch, std::uint16_t build) { - if (numbers.size() < 4) { - logger::error("Invalid SemVerToInt argument"); - return 0; - } - - return (numbers[0] << 24) | (numbers[1] << 16) | (numbers[2] << 8) | numbers[3]; + return (static_cast(major) << 24) | (static_cast(minor) << 16) | (static_cast(patch) << 8) | static_cast(build); } inline void CheckIncompatibleMods() @@ -311,10 +306,10 @@ inline RE::BSFixedString GetPlayerHash() { char buf[] = "DEADBEEF"; auto saveData = RE::BSWin32SaveDataSystemUtility::GetSingleton(); - if (saveData->profileHash == static_cast(-1)) { + if (saveData->currentCharacterID == static_cast(-1)) { std::snprintf(buf, sizeof(buf), "%08o", 0); } else { - std::snprintf(buf, sizeof(buf), "%08X", saveData->profileHash); + std::snprintf(buf, sizeof(buf), "%08X", saveData->currentCharacterID); } return buf; }