Patch GPS node multiplier inline
This commit is contained in:
@@ -99,9 +99,10 @@ constexpr uint32_t kGameStateRunning = 2;
|
||||
constexpr bool kEnableVerboseJournalListenerHooks = false;
|
||||
constexpr bool kEnableVerboseRouteObserverHooks = false;
|
||||
constexpr bool kEnableVerboseMappinRouteState = false;
|
||||
constexpr bool kEnableGpsMultiplierHooks = true;
|
||||
constexpr bool kEnableGpsMultiplierHooks = false;
|
||||
constexpr bool kEnableGpsAuxMultiplierHook = false;
|
||||
constexpr bool kEnableGpsMultiplierSignatureTrace = false;
|
||||
constexpr bool kEnableGpsNodeMultiplierInlinePatch = true;
|
||||
constexpr bool kEnableGpsCostTablePatch = false;
|
||||
constexpr bool kEnableGpsProviderClassPatch = false;
|
||||
constexpr bool kEnableGpsProviderClassPatchTrace = false;
|
||||
@@ -195,6 +196,7 @@ constexpr uintptr_t kGpsBaseCostProviderVtableRva = 0x2AE6120;
|
||||
constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8;
|
||||
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
|
||||
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
|
||||
constexpr uintptr_t kGpsNodeMultiplierRoadTailRva = 0x040BB98;
|
||||
constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28;
|
||||
constexpr uint64_t kGpsResolveTraceWindowMs = 1200;
|
||||
constexpr uint32_t kGpsResolveSelectedRetLogLimit = 24;
|
||||
@@ -214,6 +216,7 @@ constexpr uintptr_t kMinimumReadableAddress = 0x10000;
|
||||
constexpr float kGpsSolverHighwayMultiplier = 0.35f;
|
||||
constexpr float kGpsSolverGpsOnlyMultiplier = 1.10f;
|
||||
constexpr float kGpsSolverPavementMultiplier = 1.25f;
|
||||
constexpr size_t kGpsNodeMultiplierRoadTailPatchSize = 14;
|
||||
constexpr std::array<float, 8> kGpsSolverNonRoadMultipliers = {
|
||||
10.0f, 6.0f, 2.0f, 0.0f, 4.0f, 2.5f, 1.5f, 0.0f,
|
||||
};
|
||||
@@ -322,6 +325,9 @@ std::array<uint32_t, 4> gGpsResolveHandleRetLogCounts{};
|
||||
uint64_t gGpsResolveTraceWindowUntilMs = 0;
|
||||
uint32_t gGpsResolveTraceWindowSerial = 0;
|
||||
std::array<uint32_t, 64> gGpsEdgeCostClassLogCounts{};
|
||||
std::array<uint8_t, kGpsNodeMultiplierRoadTailPatchSize> gGpsNodeMultiplierRoadTailOriginal{};
|
||||
void* gGpsNodeMultiplierRoadTailCave = nullptr;
|
||||
bool gGpsNodeMultiplierRoadTailPatchApplied = false;
|
||||
std::mutex gGpsCostLogMutex;
|
||||
std::mutex gGpsAsyncLogMutex;
|
||||
std::mutex gGpsCostTablePatchMutex;
|
||||
@@ -568,6 +574,8 @@ std::atomic<uint64_t> gGpsSpatialWeightsNextReloadCheckMs = 0;
|
||||
std::filesystem::file_time_type gGpsSpatialWeightsLastWriteTime{};
|
||||
bool gGpsSpatialWeightsFileKnown = false;
|
||||
|
||||
uintptr_t GetImageBase();
|
||||
|
||||
std::filesystem::path GetPluginDirectory()
|
||||
{
|
||||
wchar_t modulePath[MAX_PATH]{};
|
||||
@@ -634,6 +642,149 @@ void LogRed4ext(std::string_view aMessage)
|
||||
}
|
||||
}
|
||||
|
||||
void WriteInt32(uint8_t* aTarget, int32_t aValue)
|
||||
{
|
||||
std::memcpy(aTarget, &aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
void WriteFloat(uint8_t* aTarget, float aValue)
|
||||
{
|
||||
std::memcpy(aTarget, &aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
bool InstallGpsNodeMultiplierInlinePatch()
|
||||
{
|
||||
if (!kEnableGpsNodeMultiplierInlinePatch || gGpsNodeMultiplierRoadTailPatchApplied)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr size_t kCaveSize = 64;
|
||||
auto* cave = static_cast<uint8_t*>(
|
||||
VirtualAlloc(nullptr, kCaveSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
|
||||
if (!cave)
|
||||
{
|
||||
LogRed4ext("gps-node-multiplier-inline-patch skipped reason=alloc-failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replaces the road-tail at 0x40bb98. This preserves the vanilla node93
|
||||
// multiplier, then applies the highway multiplier before returning.
|
||||
size_t offset = 0;
|
||||
const auto emit = [&](std::initializer_list<uint8_t> aBytes) {
|
||||
for (auto byte : aBytes)
|
||||
{
|
||||
cave[offset++] = byte;
|
||||
}
|
||||
};
|
||||
|
||||
emit({0x80, 0xBA, 0x93, 0x00, 0x00, 0x00, 0x01}); // cmp byte ptr [rdx+0x93], 1
|
||||
emit({0x75, 0x08}); // jne +8
|
||||
|
||||
const auto mulNode93Offset = offset;
|
||||
emit({0xF3, 0x0F, 0x59, 0x05, 0x00, 0x00, 0x00, 0x00}); // mulss xmm0, [rip+disp32]
|
||||
|
||||
emit({0x66, 0xF7, 0x82, 0x88, 0x00, 0x00, 0x00, 0x00, 0x40}); // test word ptr [rdx+0x88], 0x4000
|
||||
emit({0x74, 0x08}); // je +8
|
||||
|
||||
const auto mulHighwayOffset = offset;
|
||||
emit({0xF3, 0x0F, 0x59, 0x05, 0x00, 0x00, 0x00, 0x00}); // mulss xmm0, [rip+disp32]
|
||||
emit({0xC3}); // ret
|
||||
|
||||
while (offset < 40)
|
||||
{
|
||||
cave[offset++] = 0x90;
|
||||
}
|
||||
|
||||
const auto node93ConstantOffset = offset;
|
||||
WriteFloat(cave + offset, 1.1f);
|
||||
offset += sizeof(float);
|
||||
const auto highwayConstantOffset = offset;
|
||||
WriteFloat(cave + offset, kGpsSolverHighwayMultiplier);
|
||||
offset += sizeof(float);
|
||||
|
||||
const auto caveAddress = reinterpret_cast<uintptr_t>(cave);
|
||||
WriteInt32(cave + mulNode93Offset + 4,
|
||||
static_cast<int32_t>((caveAddress + node93ConstantOffset) - (caveAddress + mulNode93Offset + 8)));
|
||||
WriteInt32(cave + mulHighwayOffset + 4,
|
||||
static_cast<int32_t>((caveAddress + highwayConstantOffset) - (caveAddress + mulHighwayOffset + 8)));
|
||||
|
||||
auto* target = reinterpret_cast<uint8_t*>(GetImageBase() + kGpsNodeMultiplierRoadTailRva);
|
||||
std::memcpy(gGpsNodeMultiplierRoadTailOriginal.data(), target, gGpsNodeMultiplierRoadTailOriginal.size());
|
||||
|
||||
uint8_t patch[kGpsNodeMultiplierRoadTailPatchSize]{};
|
||||
size_t patchOffset = 0;
|
||||
patch[patchOffset++] = 0xFF; // jmp qword ptr [rip+0]
|
||||
patch[patchOffset++] = 0x25;
|
||||
patch[patchOffset++] = 0x00;
|
||||
patch[patchOffset++] = 0x00;
|
||||
patch[patchOffset++] = 0x00;
|
||||
patch[patchOffset++] = 0x00;
|
||||
const auto cavePointer = reinterpret_cast<uintptr_t>(cave);
|
||||
std::memcpy(patch + patchOffset, &cavePointer, sizeof(cavePointer));
|
||||
patchOffset += sizeof(cavePointer);
|
||||
while (patchOffset < sizeof(patch))
|
||||
{
|
||||
patch[patchOffset++] = 0x90;
|
||||
}
|
||||
|
||||
DWORD oldProtect = 0;
|
||||
if (!VirtualProtect(target, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect))
|
||||
{
|
||||
VirtualFree(cave, 0, MEM_RELEASE);
|
||||
LogRed4ext("gps-node-multiplier-inline-patch skipped reason=protect-failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(target, patch, sizeof(patch));
|
||||
FlushInstructionCache(GetCurrentProcess(), target, sizeof(patch));
|
||||
|
||||
DWORD ignoredProtect = 0;
|
||||
VirtualProtect(target, sizeof(patch), oldProtect, &ignoredProtect);
|
||||
|
||||
gGpsNodeMultiplierRoadTailCave = cave;
|
||||
gGpsNodeMultiplierRoadTailPatchApplied = true;
|
||||
|
||||
std::ostringstream line;
|
||||
line << "gps-node-multiplier-inline-patch applied target_rva=0x" << std::hex
|
||||
<< kGpsNodeMultiplierRoadTailRva << " cave=" << static_cast<void*>(cave)
|
||||
<< " highwayMultiplier=" << std::dec << kGpsSolverHighwayMultiplier;
|
||||
LogRed4ext(line.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveGpsNodeMultiplierInlinePatch()
|
||||
{
|
||||
if (!gGpsNodeMultiplierRoadTailPatchApplied)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* target = reinterpret_cast<uint8_t*>(GetImageBase() + kGpsNodeMultiplierRoadTailRva);
|
||||
DWORD oldProtect = 0;
|
||||
if (VirtualProtect(target, gGpsNodeMultiplierRoadTailOriginal.size(), PAGE_EXECUTE_READWRITE, &oldProtect))
|
||||
{
|
||||
std::memcpy(target, gGpsNodeMultiplierRoadTailOriginal.data(), gGpsNodeMultiplierRoadTailOriginal.size());
|
||||
FlushInstructionCache(GetCurrentProcess(), target, gGpsNodeMultiplierRoadTailOriginal.size());
|
||||
|
||||
DWORD ignoredProtect = 0;
|
||||
VirtualProtect(target, gGpsNodeMultiplierRoadTailOriginal.size(), oldProtect, &ignoredProtect);
|
||||
LogRed4ext("gps-node-multiplier-inline-patch restored");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRed4ext("gps-node-multiplier-inline-patch restore-failed reason=protect-failed");
|
||||
}
|
||||
|
||||
if (gGpsNodeMultiplierRoadTailCave)
|
||||
{
|
||||
VirtualFree(gGpsNodeMultiplierRoadTailCave, 0, MEM_RELEASE);
|
||||
gGpsNodeMultiplierRoadTailCave = nullptr;
|
||||
}
|
||||
|
||||
gGpsNodeMultiplierRoadTailPatchApplied = false;
|
||||
}
|
||||
|
||||
bool IsUsableSpatialMultiplier(float aValue)
|
||||
{
|
||||
return std::isfinite(aValue) && aValue > 0.0f && aValue < 20.0f;
|
||||
@@ -5269,6 +5420,8 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
|
||||
LogGpsSpatialMultipliers("spatial weights defaults", values);
|
||||
}
|
||||
|
||||
InstallGpsNodeMultiplierInlinePatch();
|
||||
|
||||
if (kEnableGpsQueryLifecycleHooks)
|
||||
{
|
||||
AttachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva,
|
||||
@@ -5497,6 +5650,8 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
|
||||
|
||||
if (aReason == RED4ext::v1::EMainReason::Unload)
|
||||
{
|
||||
RemoveGpsNodeMultiplierInlinePatch();
|
||||
|
||||
if (kEnableGpsQueryLifecycleHooks)
|
||||
{
|
||||
DetachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva);
|
||||
|
||||
Reference in New Issue
Block a user