Patch GPS node multiplier inline
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
2026-06-27 05:03:11.000 +0ms EdgeWeightGPS Main reason=Load handle=0x6ffff0570000 sdk=0x7f9fcf48 runtime=2.3.1
|
||||
2026-06-27 05:03:11.001 +1ms registered Running game-state callbacks
|
||||
2026-06-27 05:03:11.008 +8ms hook attach GPSQuerySubmit 0x70a42c target=0x14070a42c rva=0x70a42c result=ok original=0x1018c0480
|
||||
2026-06-27 05:03:11.015 +15ms hook attach GPSQueryResultFetch 0x7094b8 target=0x1407094b8 rva=0x7094b8 result=ok original=0x1018c04e0
|
||||
2026-06-27 05:03:11.029 +29ms hook attach GPSNodeMultiplier 0x40bb40 target=0x14040bb40 rva=0x40bb40 result=ok original=0x1018c0540
|
||||
2026-06-27 05:03:28.861 +16861ms GameState Running OnEnter app=0x31fb80
|
||||
2026-06-27 05:03:28.887 +16887ms GameState Running OnUpdate app=0x31fb80 count=0
|
||||
2026-06-27 05:03:28.894 +16894ms GameState Running OnUpdate app=0x31fb80 count=1
|
||||
2026-06-27 05:03:28.901 +16901ms GameState Running OnUpdate app=0x31fb80 count=2
|
||||
2026-06-27 05:03:28.907 +16907ms GameState Running OnUpdate app=0x31fb80 count=3
|
||||
2026-06-27 05:03:28.914 +16914ms GameState Running OnUpdate app=0x31fb80 count=4
|
||||
2026-06-27 05:04:00.752 +48752ms hook GPSQuerySubmit 0x70a42c call=3 manager=0x1a2e157d0 query=0x79a3fa60 query_f00=0/0x0 query_f04=65535/0xffff query_f08=2052/0x804 query_f0c=0/0x0 query_fcc=5/0x5 query_pos58=(-1569.68,1213.26,17.3737,0) resolveTraceWindowMs=1200 ret_rva=0x8d20d4
|
||||
2026-06-27 05:04:00.752 +48752ms hook GPSQuerySubmit result call=3 queryId=3 manager_nextIdD4=4/0x4
|
||||
2026-06-27 05:04:00.752 +48752ms hook GPSQuerySubmit 0x70a42c call=4 manager=0x1a2e157d0 query=0x79a3fa60 query_f00=0/0x0 query_f04=65535/0xffff query_f08=2052/0x804 query_f0c=0/0x0 query_fcc=5/0x5 query_pos58=(-1569.68,1213.26,17.3737,0) resolveTraceWindowMs=1200 ret_rva=0x8d20d4
|
||||
2026-06-27 05:04:00.752 +48752ms hook GPSQuerySubmit result call=4 queryId=4 manager_nextIdD4=5/0x5
|
||||
@@ -0,0 +1,145 @@
|
||||
Registered crash info file...
|
||||
InternalVersion: 3.0.5294808 P4CL: 9778208 Stream: //R6.Root/R6.Release
|
||||
!!!CRASHED!!!
|
||||
Error Reason: Unhandled exception
|
||||
Expression: EXCEPTION_ACCESS_VIOLATION (0xC0000005)
|
||||
Message: The thread attempted to read inaccessible data at 0x8C.
|
||||
File: <Unknown>
|
||||
Line: 0
|
||||
|
||||
"uptimeSeconds":48
|
||||
"stopThreadID":448
|
||||
"exceptionCode":0xC0000005
|
||||
"processID":368
|
||||
"Engine/CommitFailedErrorCode@1#TID=0":"0"
|
||||
"ResourceLoaderThrottler/PreviousSpeed@908858#TID=0":"Flood"
|
||||
"ResourceLoaderThrottler/CurrentSpeed@908859#TID=0":"Stream"
|
||||
"ResourceLoaderThrottler/NumThrottleTokensAcquired@916696#TID=0":"1"
|
||||
"ResourceLoaderThrottler/NumThrottleTokensSoftLimit@916695#TID=0":"2"
|
||||
"Gpu/RayTracing/Enabled@256#TID=0":"false"
|
||||
"Configuration/Name@2#TID=0":"FINAL"
|
||||
"Configuration/Name2@3#TID=0":"FINAL"
|
||||
"Game/Patch@7#TID=0":"2.31"
|
||||
"Game/TransformAnimator/Components@916123#TID=0":"315"
|
||||
"Game/TransformAnimator/RunningComponents@916122#TID=0":"271"
|
||||
"Game/TransformAnimator/AddedRunningComponents@916120#TID=0":"0"
|
||||
"Game/TransformAnimator/RemovedRunningAnimations@916121#TID=0":"0"
|
||||
"Game/TransformAnimator/RotatingMeshNodes@916126#TID=0":"120"
|
||||
"Game/TransformAnimator/AddedRotatingMeshNodes@916124#TID=0":"0"
|
||||
"Game/TransformAnimator/RemovedRotatingMeshNodes@916125#TID=0":"0"
|
||||
"Game/SessionDesc/WorldName@276998#TID=0":"03_night_city"
|
||||
"Game/SessionDesc/IsValid@276999#TID=0":"true"
|
||||
"Game/SessionDesc/IsLoadingSavedSession@277000#TID=0":"true"
|
||||
"Game/SessionDesc/IsJoiningRemoteSession@277001#TID=0":"false"
|
||||
"Game/SessionDesc/HostName@277002#TID=0":""
|
||||
"Game/SessionDesc/GameRulesConfig@277003#TID=0":"None"
|
||||
"Game/SessionDesc/SpawnLocalPlayer@277004#TID=0":"false"
|
||||
"Game/SessionDesc/SpawnPoint@277005#TID=0":"Position=[0, 0, 0, 0] Rotation=[Roll:0, Pitch:0, Yaw:0]"
|
||||
"Game/SessionDesc/SpawnRecordId@277006#TID=0":"<0x0000001C15982ADF>"
|
||||
"Game/SessionDesc/Gender@277007#TID=0":"None"
|
||||
"GameSessionDesc/UseSpecifiedStartPoint@277008#TID=0":"false"
|
||||
"GameSessionDesc/IsSpectator@277009#TID=0":"false"
|
||||
"GameSessionDesc/SpawnTags@277010#TID=0":""
|
||||
"Game/SessionDesc/IsStreamingTestRequested@277011#TID=0":"false"
|
||||
"Game/SessionDesc/IsBenchmark@277012#TID=0":"false"
|
||||
"Game/GameEffects/RunningEffects@916520#TID=0":"1"
|
||||
"Game/GameEffects/AddedEffects@916518#TID=0":"1"
|
||||
"Game/GameEffects/TerminatingEffects@916521#TID=0":"0"
|
||||
"Game/Interactions/DialogVisualizerTotalCount@916461#TID=0":"0"
|
||||
"Game/Interactions/DeviceVisualizerTotalCount@916462#TID=0":"0"
|
||||
"Game/Interactions/LootVisualizerTotalCount@916463#TID=0":"0"
|
||||
"Game/Interactions/HotSpots/CurrentCount@916468#TID=0":"405"
|
||||
"Game/Interactions/HotSpots/MaxCount@916469#TID=0":"528"
|
||||
"Game/Interactions/HotSpots/LastFrameAddedCount@916464#TID=0":"0"
|
||||
"Game/Interactions/HotSpots/MaxAddedCount@916465#TID=0":"566"
|
||||
"Game/Interactions/HotSpots/LastFrameRemovedCount@916466#TID=0":"0"
|
||||
"Game/Interactions/HotSpots/MaxRemovedCount@916467#TID=0":"128"
|
||||
"Game/Interactions/UniqueLayersCurrentCount@916470#TID=0":"3310"
|
||||
"Game/Interactions/UniqueLayersMaxCount@916471#TID=0":"4539"
|
||||
"Game/Interactions/EventsLastFrameCount@916459#TID=0":"0"
|
||||
"Game/Interactions/EventsMaxCount@916460#TID=0":"25"
|
||||
"Game/PlayerStateMachine/numberOfStateMachines@827197#TID=0":"10"
|
||||
"Game/TransactionSystem/NumberOfItemsInPlayersInventory@422220#TID=0":"620"
|
||||
"Game/TransactionSystem/NumberOfItemsInPlayersStash@621662#TID=0":"813"
|
||||
"Game/StatsSystem/NumberOfStatsBundles@915530#TID=0":"2353"
|
||||
"Game/StatPoolsSystem/MaxNumberOfStatPoolListenersOnObject@822465#TID=0":"10"
|
||||
"Game/StatsSystem/MaxNumberOfStatModifiersOfStatTypeOnObject@823059#TID=0":"99"
|
||||
"Game/StatsSystem/NameOfMaxNumberOfStatModifiersOnObjectStatType@823060#TID=0":"StaminaRatio"
|
||||
"Game/mounting/MountingFacility/numberOfGameplayMounts@826118#TID=0":"1"
|
||||
"Game/mounting/MountingFacility/numberOfMountEvents@825990#TID=0":"0"
|
||||
"Game/ItemFactorySystemPool/numberOfPrePooledOwners@422304#TID=0":"1"
|
||||
"Game/ItemFactorySystemPool/numberOfPrePooledItems@276804#TID=0":"0"
|
||||
"Game/ItemFactorySystemPool/numberOfPostPooledItems@276802#TID=0":"0"
|
||||
"Game/ItemFactorySystem/numberOfOwnerDatas@422305#TID=0":"1"
|
||||
"Game/ItemFactorySystem/numberOfItemsWithNoOwner@276801#TID=0":"0"
|
||||
"Game/ItemFactorySystem/numberOfCachedOwners@422303#TID=0":"1"
|
||||
"Game/ItemFactorySystem/numberOfCachedItems@696725#TID=0":"7"
|
||||
"Game/ItemFactorySystem/numberOfUncachedOwners@913876#TID=0":"106"
|
||||
"Game/ItemFactorySystem/numberOfUncachedItems@914747#TID=0":"128"
|
||||
"Game/StatPoolsSystem/MaxNumberOfUnassignedStatPoolListeners@825246#TID=0":"5172"
|
||||
"Game/StatsSystem/MaxNumberOfStatsListenersOnObject@824938#TID=0":"207"
|
||||
"Game/StatsSystem/MaxNumberOfStatsCallbackData@817039#TID=0":"267"
|
||||
"Game/mounting/LowLevelSystem/numberOfAssociations@825781#TID=0":"14"
|
||||
"Game/ItemFactorySystemLowLevel/numberOfSpawnRequests@914748#TID=0":"0"
|
||||
"Game/ItemFactorySystemLowLevel/numberOfReleaseRequests@825658#TID=0":"0"
|
||||
"Game/ItemFactorySystemLowLevel/numberOfCancelRequests@276836#TID=0":"0"
|
||||
"Game/ItemFactorySystemLowLevel/numberOfAppearanceChangeRequests@840034#TID=0":"0"
|
||||
"Game/Population/AlwaysSpawned@916330#TID=0":"18"
|
||||
"Game/Population/Attached@916331#TID=0":"220"
|
||||
"Game/Population/Registered@916332#TID=0":"4459"
|
||||
"Game/Population/InBucket@916333#TID=0":"997"
|
||||
"Game/Population/VisQueries@916334#TID=0":"1994"
|
||||
"Rendering/AsyncCompute@916665#TID=0":"true"
|
||||
"Game/LoadingStage@908857#TID=0":"Finished"
|
||||
"Session/GameDefinitionName@276791#TID=0":""
|
||||
"Engine/OOM@4#TID=0":"false"
|
||||
"GlobalMode/IsClosing@5#TID=0":"false"
|
||||
"GlobalMode/IsGame@17#TID=0":"true"
|
||||
"Jobs/InitParam/MaxLatentJobs@8#TID=0":"131072"
|
||||
"Jobs/InitParam/MaxCriticalPathJobs@9#TID=0":"65536"
|
||||
"Jobs/InitParam/MaxImmediateJobs@10#TID=0":"2048"
|
||||
"Jobs/InitParam/WorkerThreadStackSizeKB@11#TID=0":"1024"
|
||||
"Jobs/InitParam/MaxThreads@12#TID=0":"27"
|
||||
"Jobs/InitParam/AllJobsCriticalPath@14#TID=0":"false"
|
||||
"Jobs/InitParam/UseJobDebugger@13#TID=0":"false"
|
||||
"Jobs/Dispatcher/NumDispatcherThreads@15#TID=0":"11"
|
||||
"Jobs/Dispatcher/MaxHWConcurrency@16#TID=0":"12"
|
||||
"Gpu/Device/Name@251#TID=0":"AMD Radeon RX 7900 XT (RADV NAVI31)"
|
||||
"Gpu/Device/Vendor@252#TID=0":"AMD"
|
||||
"Gpu/Device/DriverVersion@253#TID=0":"Radeon software 99.10.2"
|
||||
"Gpu/Device/TotalMemoryMB@250#TID=0":"20464"
|
||||
"Gpu/Device/UsedMemoryMB@916697#TID=0":"5608"
|
||||
"Engine/SafeInit@47935#TID=0":"Finished"
|
||||
"Engine/IsHeadless@228#TID=0":"false"
|
||||
"Engine/VersionWatermark@227#TID=0":"3.0.5294808 P4CL: 9778208 Stream: //R6.Root/R6.Release Aug 27 2025 17:30:27 (FINAL) Total memory: 11711623168 bytes [EP1]"
|
||||
"Engine/InitParams/InteropStartingPort@213#TID=0":"0"
|
||||
"Engine/InitParams/RenderPreset@214#TID=0":"1"
|
||||
"Engine/InitParams/QualityLevelName@215#TID=0":"None"
|
||||
"Engine/InitParams/TextureQualityLevelName@216#TID=0":"High"
|
||||
"Engine/InitParams/WatchdogTimeoutSecondsOverride@217#TID=0":"0"
|
||||
"Engine/InitParams/ScriptsSilentCompilation@218#TID=0":"false"
|
||||
"Engine/InitParams/ScriptsSilentValidation@219#TID=0":"false"
|
||||
"Engine/InitParams/TweakDBSilentValidation@220#TID=0":"false"
|
||||
"Engine/InitParams/PvdDumpToFile@221#TID=0":"false"
|
||||
"Engine/InitParams/ProfilerEnabled@222#TID=0":"false"
|
||||
"Engine/InitParams/ShowVersionWatermark@223#TID=0":"true"
|
||||
"Engine/InitParams/WindowCaption@224#TID=0":""
|
||||
"Engine/PC/SystemName@225#TID=0":"Windows 10 Pro"
|
||||
"Engine/PC/SystemVersion@226#TID=0":"10.0.19045"
|
||||
"Engine/Scripts/PrimaryBlobModifiedDate@235#TID=0":"134020895866810433"
|
||||
"Engine/Scripts/FallbackBlobModifiedDate@236#TID=0":"0"
|
||||
"Engine/Scripts/BlobPathUsed@237#TID=0":"Loaded"
|
||||
"Engine/Scripts/ShouldCompileScripts@233#TID=0":"true"
|
||||
"Engine/Scripts/CompileScriptsSuccess@234#TID=0":"false"
|
||||
"Engine/Scripts/Loaded@238#TID=0":"true"
|
||||
"Telemetry/PlaythroughID@393001#TID=0":"69e857a3ebf988d1"
|
||||
"Telemetry/VisitID@49233#TID=0":"16cb284d-6d0f-4c08-966d-6e23557e7428"
|
||||
"Streaming/LastObserverPosition@916499#TID=0":"[-1569, 1213, 18]"
|
||||
"Streaming/IsLastObserverPositionValid@916500#TID=0":"true"
|
||||
"Streaming/IsTeleporting@916501#TID=0":"false"
|
||||
"Streaming/DebugLastTeleportDistance@916502#TID=0":"0.000000"
|
||||
"Streaming/MountState@392989#TID=0":"Mounted"
|
||||
"Streaming/Observer/0/LastObserverPosition@916496#TID=0":"[-1569, 1213, 18]"
|
||||
"Streaming/Observer/0/LastObserverVelocityOffsetPosition@916497#TID=0":"[-1569, 1213, 18]"
|
||||
"Streaming/Observer/0/UseStreamingOcclusion@916498#TID=0":"false"
|
||||
"##CrashDump##/DumpCrashDataSeconds":"0"
|
||||
@@ -0,0 +1,4 @@
|
||||
Error reason: Unhandled exception
|
||||
Expression: EXCEPTION_ACCESS_VIOLATION (0xC0000005)
|
||||
Message: The thread attempted to read inaccessible data at 0x8C.
|
||||
File: <Unknown>(0)
|
||||
@@ -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