Compare commits
No commits in common. "7a6d154d3e74bd4d6b7db6a69d6babd70361432d" and "d263cf9e84d8ab861f975b365aec525705bd5a35" have entirely different histories.
7a6d154d3e
...
d263cf9e84
@ -9,4 +9,3 @@ AttachLightHitEffectCrashFix = true
|
||||
AutoScaleHeroMenu = true
|
||||
WarnFormTypeCollisions = true
|
||||
GogVramLeakFix = true
|
||||
ExperimentalCrashFixes = false
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
#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));
|
||||
}
|
||||
}
|
||||
@ -16,8 +16,7 @@
|
||||
#include "Patches/PluginsTxtPatch.h"
|
||||
#include "Patches/FormTypeCollisionDetector.h"
|
||||
#include "Patches/GogVramLeakFix.h"
|
||||
#include "CrashFixes/CrosshairPickDataCrashFix.h"
|
||||
#include "CrashFixes/MovementPathFollowerCrashFix.h"
|
||||
#include "Patches/CrosshairPickDataCrashFix.h"
|
||||
|
||||
using namespace SKSE;
|
||||
|
||||
@ -33,7 +32,7 @@ static std::map<std::string, bool> g_settings{
|
||||
{ "AttachLightHitEffectCrashFix", true },
|
||||
{ "AutoScaleHeroMenu", true },
|
||||
{ "GogVramLeakFix", true },
|
||||
{ "ExperimentalCrashFixes", false }
|
||||
{ "CrosshairPickDataCrashFix", true }
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -83,11 +82,9 @@ namespace {
|
||||
logger::info("Installing light attach crash fix...");
|
||||
AttachLightHitEffectCrash::Install();
|
||||
}
|
||||
if (g_settings.at("ExperimentalCrashFixes")) {
|
||||
if (g_settings.at("CrosshairPickDataCrashFix")) {
|
||||
logger::info("Installing crosshair pick data crash fix...");
|
||||
CrosshairPickDataCrashFix::Install();
|
||||
logger::info("Installing movement path follower crash fix...");
|
||||
MovementPathFollowerCrashFix::Install();
|
||||
}
|
||||
if (g_settings.at("StayAtSystemPage")) {
|
||||
if (const auto pluginInfo = GetLoadInterface()->GetPluginInfo("StayAtSystemPage"); pluginInfo) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user