# 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 !" 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& GetImpl() noexcept { // Only AE reaches 1.7.99, so the version test alone selects the runtime. return REL::RelocateMemberIfNewer>( SKSE::RUNTIME_SSE_1_7_99, this, 0x200, 0x210); } [[nodiscard]] inline const BSTSmartPointer& GetImpl() const noexcept { return const_cast(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.")