Compare commits
No commits in common. "cd5820e029aa04e8d69c1b8e0c58f68c77b13f15" and "35af9226ed34bdcb4c753ad5e54c5c759be6e709" have entirely different histories.
cd5820e029
...
35af9226ed
@ -10,4 +10,3 @@ AutoScaleHeroMenu = true
|
|||||||
WarnFormTypeCollisions = true
|
WarnFormTypeCollisions = true
|
||||||
MenuAspectRatioFix = true
|
MenuAspectRatioFix = true
|
||||||
LoadScreenFrameFix = true
|
LoadScreenFrameFix = true
|
||||||
TextureDoubleReleaseFix = true
|
|
||||||
|
|||||||
@ -35,12 +35,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
# Link-time optimization for release builds only (debug builds stay fast).
|
# Link-time optimization for release builds only (debug builds stay fast).
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||||
|
|
||||||
# Static CRT: the plugin must not depend on the user's VC Redist version. The MSVC STL
|
# CommonLibSSE-NG requires the dynamic CRT. Dependencies used to be supplied by
|
||||||
# requires msvcp140.dll to be at least as new as the toolset that built the plugin, so a
|
# vcpkg (which set this via a preset); now that they come from FetchContent we
|
||||||
# /MD build crashes on load against an outdated redistributable. All dependencies build
|
# set it here so both build.cmd and Visual Studio "Open Folder" pick it up.
|
||||||
# from source under this setting; CommonLibSSE-GG's prebuilt bundle is /MD only and is
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||||
# skipped automatically.
|
|
||||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
||||||
|
|
||||||
add_compile_definitions(NOMINMAX _USE_MATH_DEFINES WIN32_LEAN_AND_MEAN)
|
add_compile_definitions(NOMINMAX _USE_MATH_DEFINES WIN32_LEAN_AND_MEAN)
|
||||||
add_definitions(-DUNICODE -D_UNICODE)
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
#include "Patches/FormTypeCollisionDetector.h"
|
#include "Patches/FormTypeCollisionDetector.h"
|
||||||
#include "Patches/ForceLanguage.h"
|
#include "Patches/ForceLanguage.h"
|
||||||
#include "Patches/MenuAspectRatioFix.h"
|
#include "Patches/MenuAspectRatioFix.h"
|
||||||
#include "Patches/TextureDoubleReleaseFix.h"
|
|
||||||
|
|
||||||
using namespace SKSE;
|
using namespace SKSE;
|
||||||
|
|
||||||
@ -33,8 +32,7 @@ static std::map<std::string, bool> g_settings{
|
|||||||
{ "AttachLightHitEffectCrashFix", true },
|
{ "AttachLightHitEffectCrashFix", true },
|
||||||
{ "AutoScaleHeroMenu", true },
|
{ "AutoScaleHeroMenu", true },
|
||||||
{ "MenuAspectRatioFix", true },
|
{ "MenuAspectRatioFix", true },
|
||||||
{ "LoadScreenFrameFix", true },
|
{ "LoadScreenFrameFix", true }
|
||||||
{ "TextureDoubleReleaseFix", true }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -260,9 +258,6 @@ SKSEPluginLoad(const LoadInterface* skse) {
|
|||||||
MenuAspectRatioFix::Install();
|
MenuAspectRatioFix::Install();
|
||||||
}
|
}
|
||||||
LoadScreenFrameFix::Install(g_settings.at("LoadScreenFrameFix"));
|
LoadScreenFrameFix::Install(g_settings.at("LoadScreenFrameFix"));
|
||||||
if (g_settings.at("TextureDoubleReleaseFix")) {
|
|
||||||
TextureDoubleReleaseFix::Install();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger::info("{} has finished loading.", plugin->GetName());
|
logger::info("{} has finished loading.", plugin->GetName());
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// From 1.6.1179 on, the renderer function that frees a BSGraphics::Texture (AE id 77322) calls
|
|
||||||
// Release() twice on each of its three D3D11 objects: once in the original sequence, then again
|
|
||||||
// in what looks like an inlined destructor. The texture loader takes one reference per object,
|
|
||||||
// so the second batch underflows. D3D11 usually defers the destruction of an object that a
|
|
||||||
// pending command list still references, which is why the second call normally lands on live
|
|
||||||
// memory. When the runtime frees the object at once, the game crashes in this function with
|
|
||||||
// `call [rax+0x10]` on a zeroed vtable. The patch jumps over the second batch.
|
|
||||||
namespace TextureDoubleReleaseFix
|
|
||||||
{
|
|
||||||
// Everything before the deallocation call. Only rel8 branches inside, so these bytes are
|
|
||||||
// identical on 1.6.1179, 1.7.99 and 1.7.104.
|
|
||||||
constexpr std::uint8_t DOUBLE_RELEASE_FORM[]{
|
|
||||||
0x40, 0x53, // push rbx
|
|
||||||
0x48, 0x83, 0xEC, 0x30, // sub rsp,0x30
|
|
||||||
0x48, 0xC7, 0x44, 0x24, 0x20, 0xFE, 0xFF, 0xFF, 0xFF, // mov [rsp+0x20],-2
|
|
||||||
0x48, 0x8B, 0xDA, // mov rbx,rdx
|
|
||||||
0xB8, 0xFF, 0xFF, 0xFF, 0xFF, // mov eax,-1
|
|
||||||
0xF0, 0x0F, 0xC1, 0x42, 0x20, // lock xadd [rdx+0x20],eax
|
|
||||||
0x83, 0xF8, 0x01, // cmp eax,1
|
|
||||||
0x75, 0x6C, // jne +0x8D (return)
|
|
||||||
0x48, 0x8B, 0x4A, 0x10, 0x48, 0x85, 0xC9, 0x74, 0x06, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // Release [rdx+0x10]
|
|
||||||
0x48, 0x8B, 0x0B, 0x48, 0x85, 0xC9, 0x74, 0x06, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // Release [rbx]
|
|
||||||
0x48, 0x8B, 0x4B, 0x08, 0x48, 0x85, 0xC9, 0x74, 0x07, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // Release [rbx+0x08]
|
|
||||||
0x90, // nop
|
|
||||||
0x48, 0x8B, 0x0B, 0x48, 0x85, 0xC9, 0x74, 0x06, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // +0x4E: Release [rbx] again
|
|
||||||
0x48, 0x8B, 0x4B, 0x08, 0x48, 0x85, 0xC9, 0x74, 0x06, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // Release [rbx+0x08] again
|
|
||||||
0x48, 0x8B, 0x4B, 0x10, 0x48, 0x85, 0xC9, 0x74, 0x07, 0x48, 0x8B, 0x01, 0xFF, 0x50, 0x10, // Release [rbx+0x10] again
|
|
||||||
0x90 // nop, +0x7B follows: mov edx,0x28
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1.6.1170 and older release each object once. Recognized so the log can say so.
|
|
||||||
constexpr std::uint8_t SINGLE_RELEASE_PROLOGUE[]{ 0x40, 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x8B, 0xDA };
|
|
||||||
|
|
||||||
constexpr std::size_t SECOND_BATCH = 0x4E;
|
|
||||||
constexpr std::size_t DEALLOCATE = 0x7B;
|
|
||||||
|
|
||||||
void Install()
|
|
||||||
{
|
|
||||||
if (!REL::Module::IsAE()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto target = REL::Relocation<std::uintptr_t>{ REL::ID(77322) }.address();
|
|
||||||
const auto bytes = reinterpret_cast<const std::uint8_t*>(target);
|
|
||||||
|
|
||||||
// jmp short +0x2B, from +0x50 to +0x7B
|
|
||||||
const std::uint8_t code[]{ 0xEB, static_cast<std::uint8_t>(DEALLOCATE - SECOND_BATCH - 2) };
|
|
||||||
|
|
||||||
if (std::memcmp(bytes, DOUBLE_RELEASE_FORM, sizeof(DOUBLE_RELEASE_FORM)) == 0) {
|
|
||||||
logger::info("Patching the double texture release...");
|
|
||||||
REL::safe_write(target + SECOND_BATCH, code, sizeof(code));
|
|
||||||
} else if (std::memcmp(bytes, DOUBLE_RELEASE_FORM, SECOND_BATCH) == 0 && std::memcmp(bytes + SECOND_BATCH, code, sizeof(code)) == 0) {
|
|
||||||
// Another plugin wrote the same jump first. Nothing left to do.
|
|
||||||
logger::info("Double texture release already patched by another plugin");
|
|
||||||
} else if (std::memcmp(bytes, SINGLE_RELEASE_PROLOGUE, sizeof(SINGLE_RELEASE_PROLOGUE)) == 0) {
|
|
||||||
logger::info("Texture release function releases once, no patch needed");
|
|
||||||
} else {
|
|
||||||
logger::error("Unexpected bytes in the texture release function, skipping the double release patch...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -36,12 +36,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
# Link-time optimization for release builds only (debug builds stay fast).
|
# Link-time optimization for release builds only (debug builds stay fast).
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||||
|
|
||||||
# Static CRT: the plugin must not depend on the user's VC Redist version. The MSVC STL
|
# CommonLibSSE-NG requires the dynamic CRT. Dependencies used to be supplied by
|
||||||
# requires msvcp140.dll to be at least as new as the toolset that built the plugin, so a
|
# vcpkg (which set this via a preset); now that they come from FetchContent we
|
||||||
# /MD build crashes on load against an outdated redistributable. All dependencies build
|
# set it here so both build.cmd and Visual Studio "Open Folder" pick it up.
|
||||||
# from source under this setting; CommonLibSSE-GG's prebuilt bundle is /MD only and is
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||||
# skipped automatically.
|
|
||||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
||||||
|
|
||||||
add_compile_definitions(NOMINMAX _USE_MATH_DEFINES WIN32_LEAN_AND_MEAN)
|
add_compile_definitions(NOMINMAX _USE_MATH_DEFINES WIN32_LEAN_AND_MEAN)
|
||||||
add_definitions(-DUNICODE -D_UNICODE)
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user