enderalse/source/Steam DLL/cmake/patch_commonlibsse.cmake

81 lines
3.6 KiB
CMake

# Patch applied to CommonLibSSE-NG via FetchContent PATCH_COMMAND.
#
# Skyrim AE 1.7.99 inserts two event sink bases into RE::SkyrimVM at 0x180 and
# 0x188, so every member from 0x180 onwards moves up by 0x10. CommonLibSSE-NG
# 6.5.0 modelled most of that shift (handlePolicy, the runtime data blocks and
# the tail block all get version aware accessors), but it left the pointer block
# that starts at "impl" declared at its pre-1.7.99 offsets.
#
# "impl" holds the BSScript::IVirtualMachine the whole Papyrus layer runs on:
#
# RE::BSScript::Internal::VirtualMachine::GetSingleton()
# -> SkyrimVM::GetSingleton()->impl.get()
#
# On 1.7.99 that reads offset 0x200, which now lands inside the preceding
# BSTEventSource, so the singleton is null or garbage. The visible effects are
# "Failed to get vm type id for class <n>!" for every object typed Papyrus
# parameter (RE::BSScript::GetRawTypeFromVMType null checks the pointer) and a
# crash in any caller that does not (Papyrus argument marshalling in
# PackUnpack.cpp, and this plugin's own script checks).
#
# The patch adds a version aware GetImpl() and routes the two in tree consumers
# through it. It deliberately leaves the member declarations alone, so every
# layout static_assert still holds.
#
# Verification: 61 call sites in SkyrimSE.exe 1.6.1170 read the field at
# [SkyrimVM + 0x200], and the matching 61 sites in 1.7.99 read [SkyrimVM + 0x210].
#
# Remove this file, its two PATCH_COMMAND entries and this note once upstream
# fixes the offset. The script is idempotent, and it fails the configure step if
# any of the three anchors is missing, so a bump that changes or fixes this code
# is reported instead of silently producing an unpatched build.
set(_clib_header "include/RE/S/SkyrimVM.h")
set(_clib_vm "src/RE/V/VirtualMachine.cpp")
set(_clib_skvm "src/RE/S/SkyrimVM.cpp")
set(_clib_marker "GetImpl")
set(_clib_accessor
" // AE 1.7.99 shifts every member from 0x180 onwards by +0x10, and impl is
// declared at its pre-1.7.99 offset. Read it through here, never directly.
[[nodiscard]] inline BSTSmartPointer<BSScript::IVirtualMachine>& GetImpl() noexcept
{
// Only AE reaches 1.7.99, so the version test alone selects the runtime.
return REL::RelocateMemberIfNewer<BSTSmartPointer<BSScript::IVirtualMachine>>(
SKSE::RUNTIME_SSE_1_7_99, this, 0x200, 0x210);
}
[[nodiscard]] inline const BSTSmartPointer<BSScript::IVirtualMachine>& GetImpl() const noexcept
{
return const_cast<SkyrimVM*>(this)->GetImpl();
}
static SkyrimVM* GetSingleton();")
function(_clib_patch_file path from to)
if(NOT EXISTS "${path}")
message(FATAL_ERROR "CommonLibSSE patch: ${path} is missing.")
endif()
file(READ "${path}" _content)
string(FIND "${_content}" "${_clib_marker}" _found)
if(_found GREATER_EQUAL 0)
return() # already patched
endif()
string(FIND "${_content}" "${from}" _anchor)
if(_anchor LESS 0)
message(FATAL_ERROR
"CommonLibSSE patch: anchor not found in ${path}.\n"
"Check whether upstream fixed SkyrimVM::impl for AE 1.7.99. If it did, "
"delete cmake/patch_commonlibsse.cmake and its PATCH_COMMAND entries.")
endif()
string(REPLACE "${from}" "${to}" _content "${_content}")
file(WRITE "${path}" "${_content}")
endfunction()
_clib_patch_file("${_clib_header}" " static SkyrimVM* GetSingleton();" "${_clib_accessor}")
_clib_patch_file("${_clib_vm}" "vm->impl.get()" "vm->GetImpl().get()")
_clib_patch_file("${_clib_skvm}" "impl.get()->SendEvent" "GetImpl().get()->SendEvent")
message(STATUS "Patched CommonLibSSE-NG SkyrimVM::impl for AE 1.7.99.")