Added MovementPathFollowerCrashFix
This commit is contained in:
commit
13ff172e61
@ -17,6 +17,7 @@
|
|||||||
#include "Patches/FormTypeCollisionDetector.h"
|
#include "Patches/FormTypeCollisionDetector.h"
|
||||||
#include "Patches/GogVramLeakFix.h"
|
#include "Patches/GogVramLeakFix.h"
|
||||||
#include "Patches/CrosshairPickDataCrashFix.h"
|
#include "Patches/CrosshairPickDataCrashFix.h"
|
||||||
|
#include "Patches/MovementPathFollowerCrashFix.h"
|
||||||
|
|
||||||
using namespace SKSE;
|
using namespace SKSE;
|
||||||
|
|
||||||
@ -86,6 +87,8 @@ namespace {
|
|||||||
logger::info("Installing crosshair pick data crash fix...");
|
logger::info("Installing crosshair pick data crash fix...");
|
||||||
CrosshairPickDataCrashFix::Install();
|
CrosshairPickDataCrashFix::Install();
|
||||||
}
|
}
|
||||||
|
logger::info("Installing movement path follower crash fix...");
|
||||||
|
MovementPathFollowerCrashFix::Install();
|
||||||
if (g_settings.at("StayAtSystemPage")) {
|
if (g_settings.at("StayAtSystemPage")) {
|
||||||
if (const auto pluginInfo = GetLoadInterface()->GetPluginInfo("StayAtSystemPage"); pluginInfo) {
|
if (const auto pluginInfo = GetLoadInterface()->GetPluginInfo("StayAtSystemPage"); pluginInfo) {
|
||||||
MessageBoxW(NULL, L"Stay At The System Page is already included in Enderal, please, disable it.", L"Enderal SE Error", MB_OK | MB_ICONERROR);
|
MessageBoxW(NULL, L"Stay At The System Page is already included in Enderal, please, disable it.", L"Enderal SE Error", MB_OK | MB_ICONERROR);
|
||||||
|
|||||||
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Fix crash in MovementAgentPathFollowerVirtual when [RDI+0x48] is NULL
|
||||||
|
// Crash occurs when MOVSS tries to read from [RAX+0x8C] with RAX=NULL
|
||||||
|
// Address Library ID: 92556 (MovementAgentPathFollowerVirtual::sub_1115090)
|
||||||
|
//
|
||||||
|
// Original crash flow:
|
||||||
|
// MOV RAX, [RDI+0x48] ; RAX can be NULL
|
||||||
|
// LEA RDX, [RAX+0x58]
|
||||||
|
// MOVSS XMM2, [RAX+0x8C] ; CRASH if RAX is NULL
|
||||||
|
//
|
||||||
|
// Fix: Add null check after loading RAX, skip to safe exit if NULL
|
||||||
|
namespace MovementPathFollowerCrashFix
|
||||||
|
{
|
||||||
|
// Address Library ID for MovementAgentPathFollowerVirtual::sub_1115090
|
||||||
|
constexpr REL::RelocationID FuncID(92556, 92556);
|
||||||
|
|
||||||
|
// Offsets from function start to hook point (MOV RAX, [RDI+0x48])
|
||||||
|
// SE: 0x428, AE: 0x41F, VR: 0x428
|
||||||
|
constexpr REL::VariantOffset HookOffset(0x428, 0x41F, 0x428);
|
||||||
|
|
||||||
|
// Offsets from function start to skip point (safe exit label)
|
||||||
|
// SE: 0x518, AE: 0x50F, VR: 0x518
|
||||||
|
constexpr REL::VariantOffset SkipOffset(0x518, 0x50F, 0x518);
|
||||||
|
|
||||||
|
// Hook size: MOV (4 bytes) + LEA (4 bytes) = 8 bytes
|
||||||
|
constexpr std::size_t HookSize = 8;
|
||||||
|
|
||||||
|
inline void Install()
|
||||||
|
{
|
||||||
|
const REL::Relocation<std::uintptr_t> funcBase{ FuncID };
|
||||||
|
const std::uintptr_t hookAddr = funcBase.address() + HookOffset.offset();
|
||||||
|
const std::uintptr_t skipAddr = funcBase.address() + SkipOffset.offset();
|
||||||
|
const std::uintptr_t returnAddr = hookAddr + HookSize;
|
||||||
|
|
||||||
|
// Generate the patch code using xbyak
|
||||||
|
struct PatchCode : Xbyak::CodeGenerator
|
||||||
|
{
|
||||||
|
PatchCode(std::uintptr_t a_skipAddr, std::uintptr_t a_returnAddr)
|
||||||
|
{
|
||||||
|
// Original: MOV RAX, [RDI+0x48]
|
||||||
|
mov(rax, qword[rdi + 0x48]);
|
||||||
|
|
||||||
|
// Add null check
|
||||||
|
test(rax, rax);
|
||||||
|
jz("skip_label");
|
||||||
|
|
||||||
|
// Original: LEA RDX, [RAX+0x58]
|
||||||
|
lea(rdx, qword[rax + 0x58]);
|
||||||
|
|
||||||
|
// Jump back to original code after LEA (continue with MOVSS)
|
||||||
|
jmp(ptr[rip]);
|
||||||
|
dq(a_returnAddr);
|
||||||
|
|
||||||
|
// Skip label - jump to safe exit point
|
||||||
|
L("skip_label");
|
||||||
|
jmp(ptr[rip]);
|
||||||
|
dq(a_skipAddr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PatchCode patchCode(skipAddr, returnAddr);
|
||||||
|
patchCode.ready();
|
||||||
|
|
||||||
|
// Allocate trampoline space and copy our patch code
|
||||||
|
SKSE::AllocTrampoline(patchCode.getSize() + 14);
|
||||||
|
auto& trampoline = SKSE::GetTrampoline();
|
||||||
|
|
||||||
|
void* codeCave = trampoline.allocate(patchCode.getSize());
|
||||||
|
std::memcpy(codeCave, patchCode.getCode(), patchCode.getSize());
|
||||||
|
|
||||||
|
// Write jump from original location to our patch code
|
||||||
|
trampoline.write_branch<5>(hookAddr, reinterpret_cast<std::uintptr_t>(codeCave));
|
||||||
|
|
||||||
|
// NOP remaining bytes
|
||||||
|
if (HookSize > 5) {
|
||||||
|
REL::safe_fill(hookAddr + 5, REL::NOP, HookSize - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* version = REL::Module::IsVR() ? "VR" :
|
||||||
|
(REL::Module::get().version() <= REL::Version(1, 5, 97, 0) ? "SE" : "AE");
|
||||||
|
logger::info("MovementPathFollowerCrashFix: Patched {} at 0x{:X}, code cave at 0x{:X}",
|
||||||
|
version, hookAddr, reinterpret_cast<std::uintptr_t>(codeCave));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user