From 4dde0b85797128d1e8dd93e47f877e5765a1417c Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Sat, 20 Jun 2026 15:10:18 -0500 Subject: [PATCH] Patch GPS route cost table during jobs --- red4ext/EdgeWeightGPS/src/Main.cpp | 138 ++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index 05e8832..eeb3d4a 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -96,6 +96,7 @@ constexpr bool kEnableVerboseJournalListenerHooks = false; constexpr bool kEnableVerboseRouteObserverHooks = false; constexpr bool kEnableVerboseMappinRouteState = false; constexpr bool kEnableGpsMultiplierHooks = false; +constexpr bool kEnableGpsCostTablePatch = true; constexpr uintptr_t kGpsSystemTickRva = 0x0E6B62C; constexpr uintptr_t kJournalTrackEntryImplRva = 0x05944FC; constexpr uintptr_t kJournalListenerArrayOffset = 0x210; @@ -162,6 +163,10 @@ constexpr uintptr_t kGpsSearchRva = 0x044F054; constexpr uintptr_t kGpsEdgeCostRva = 0x044F838; constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00; constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40; +constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28; +constexpr std::array kGpsPatchedCostMultipliers = { + 25.0f, 18.0f, 8.0f, 0.0f, 0.35f, 0.50f, 0.75f, +}; HMODULE gModule = nullptr; std::mutex gLogMutex; @@ -233,9 +238,11 @@ uint32_t gGpsSearchLogCount = 0; uint32_t gGpsEdgeCostLogCount = 0; uint32_t gGpsAuxMultiplierLogCount = 0; uint32_t gGpsNodeMultiplierLogCount = 0; +uint32_t gGpsCostTablePatchLogCount = 0; std::array gGpsEdgeCostClassLogCounts{}; std::mutex gGpsCostLogMutex; std::mutex gGpsAsyncLogMutex; +std::mutex gGpsCostTablePatchMutex; uint64_t gLogStartTick = 0; uint64_t gLogFrequency = 0; void* gMappinSetTrackedTarget = nullptr; @@ -1577,6 +1584,126 @@ bool TryReadUint32FieldValue(void* aObject, uintptr_t aOffset, uint32_t& aValue) aValue); } +bool IsDrivingRouteJob(void* aRouteSystem, void* aActiveEntry) +{ + uint32_t mode = 0; + if (TryReadUint32FieldValue(aActiveEntry, 0x80, mode) && mode == 2) + { + return true; + } + + auto* job = ReadPointerField(aRouteSystem, 0x90D0); + return TryReadUint32FieldValue(job, 0xC4, mode) && mode == 2; +} + +void AppendFloatArray(std::ostringstream& aLine, const char* aLabel, const float* aValues, size_t aCount) +{ + const auto flags = aLine.flags(); + const auto precision = aLine.precision(); + aLine << " " << aLabel << "=["; + for (size_t index = 0; index < aCount; ++index) + { + if (index > 0) + { + aLine << ","; + } + aLine << std::fixed << std::setprecision(2) << aValues[index]; + } + aLine << "]"; + aLine.flags(flags); + aLine.precision(precision); +} + +class ScopedGpsCostTablePatch +{ +public: + ScopedGpsCostTablePatch(bool aShouldPatch, uint32_t aRouteJobStartCall) + : m_routeJobStartCall(aRouteJobStartCall) + { + if (!kEnableGpsCostTablePatch || !aShouldPatch) + { + return; + } + + m_table = reinterpret_cast(GetImageBase() + kGpsCostMultiplierTableRva); + if (!IsReadableMemory(m_table, sizeof(m_original))) + { + LogPatch("gps-cost-table-patch skipped", "reason=unreadable-table"); + return; + } + + std::memcpy(m_original.data(), m_table, sizeof(m_original)); + + DWORD oldProtect = 0; + if (!VirtualProtect(m_table, sizeof(m_original), PAGE_EXECUTE_READWRITE, &oldProtect)) + { + LogPatch("gps-cost-table-patch skipped", "reason=protect-failed"); + return; + } + + std::memcpy(m_table, kGpsPatchedCostMultipliers.data(), sizeof(m_original)); + + DWORD ignoredProtect = 0; + VirtualProtect(m_table, sizeof(m_original), oldProtect, &ignoredProtect); + FlushInstructionCache(GetCurrentProcess(), m_table, sizeof(m_original)); + + m_active = true; + LogPatch("gps-cost-table-patch applied", nullptr); + } + + ~ScopedGpsCostTablePatch() + { + if (!m_active || !m_table) + { + return; + } + + DWORD oldProtect = 0; + if (!VirtualProtect(m_table, sizeof(m_original), PAGE_EXECUTE_READWRITE, &oldProtect)) + { + LogPatch("gps-cost-table-patch restore-failed", "reason=protect-failed"); + return; + } + + std::memcpy(m_table, m_original.data(), sizeof(m_original)); + + DWORD ignoredProtect = 0; + VirtualProtect(m_table, sizeof(m_original), oldProtect, &ignoredProtect); + FlushInstructionCache(GetCurrentProcess(), m_table, sizeof(m_original)); + LogPatch("gps-cost-table-patch restored", nullptr); + } + +private: + void LogPatch(const char* aMessage, const char* aReason) + { + const auto patchLogCall = NextGpsAsyncLogCall(gGpsCostTablePatchLogCount); + if (patchLogCall >= 64) + { + return; + } + + std::ostringstream line; + line << aMessage << " patchCall=" << patchLogCall << " routeJobStartCall=" << m_routeJobStartCall; + AppendPointerWithRva(line, "table", m_table); + if (aReason) + { + line << " " << aReason; + } + if (m_table && IsReadableMemory(m_table, sizeof(m_original))) + { + AppendFloatArray(line, "current", m_table, m_original.size()); + } + AppendFloatArray(line, "original", m_original.data(), m_original.size()); + AppendFloatArray(line, "patched", kGpsPatchedCostMultipliers.data(), kGpsPatchedCostMultipliers.size()); + LogRed4ext(line.str()); + } + + uint32_t m_routeJobStartCall = 0; + bool m_active = false; + float* m_table = nullptr; + std::array m_original{}; +}; + void AppendRouteJobSummary(std::ostringstream& aLine, const char* aPrefix, void* aJob) { const auto prefix = std::string(aPrefix); @@ -3211,7 +3338,16 @@ bool DetourGpsRouteJobStart(void* aRouteSystem, void* aActiveEntry, float aDelta bool result = false; if (gOriginalGpsRouteJobStart) { - result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal); + if (kEnableGpsCostTablePatch && IsDrivingRouteJob(aRouteSystem, aActiveEntry)) + { + std::lock_guard patchLock(gGpsCostTablePatchMutex); + ScopedGpsCostTablePatch patch(true, callCount); + result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal); + } + else + { + result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal); + } } if (shouldLog)