From d6d0ed97660ae89639181c59cf0afad7638e6c6d Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Sat, 20 Jun 2026 16:05:03 -0500 Subject: [PATCH] Patch GPS search provider class costs --- red4ext/EdgeWeightGPS/src/Main.cpp | 128 +++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 5 deletions(-) diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index bdef5ba..845f6bd 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -96,7 +96,9 @@ constexpr bool kEnableVerboseJournalListenerHooks = false; constexpr bool kEnableVerboseRouteObserverHooks = false; constexpr bool kEnableVerboseMappinRouteState = false; constexpr bool kEnableGpsMultiplierHooks = false; -constexpr bool kEnableGpsCostTablePatch = true; +constexpr bool kEnableGpsCostTablePatch = false; +constexpr bool kEnableGpsProviderClassPatch = true; +constexpr bool kEnableGpsProviderClassPatchTrace = true; constexpr bool kEnableGpsTraceHooks = false; constexpr bool kEnableGpsRouteJobStartTrace = false; constexpr bool kEnableGpsLocalSearchHooks = false; @@ -165,12 +167,26 @@ constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8; constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C; constexpr uintptr_t kGpsSearchRva = 0x044F054; constexpr uintptr_t kGpsEdgeCostRva = 0x044F838; +constexpr uintptr_t kGpsBaseCostProviderVtableRva = 0x2AE6120; +constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8; constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00; constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40; constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28; constexpr std::array kGpsPatchedCostMultipliers = { 12.0f, 7.0f, 2.5f, 0.0f, 3.0f, 2.0f, 1.25f, }; +constexpr std::array kGpsPatchedProviderClassMultipliers = [] { + std::array values{}; + values.fill(1.0f); + + values[1] = 0.70f; + values[3] = 1.45f; + values[4] = 1.25f; + values[5] = 1.75f; + values[15] = 0.80f; + + return values; +}(); HMODULE gModule = nullptr; std::mutex gLogMutex; @@ -243,6 +259,7 @@ uint32_t gGpsEdgeCostLogCount = 0; uint32_t gGpsAuxMultiplierLogCount = 0; uint32_t gGpsNodeMultiplierLogCount = 0; uint32_t gGpsCostTablePatchLogCount = 0; +uint32_t gGpsProviderClassPatchLogCount = 0; std::array gGpsEdgeCostClassLogCounts{}; std::mutex gGpsCostLogMutex; std::mutex gGpsAsyncLogMutex; @@ -1708,6 +1725,100 @@ private: std::array m_original{}; }; +bool IsKnownGpsCostProvider(void* aProvider, uintptr_t& aVtableRva) +{ + aVtableRva = 0; + auto* vtable = ReadPointerField(aProvider, 0x00); + if (!TryGetImageRva(vtable, aVtableRva)) + { + return false; + } + + return aVtableRva == kGpsBaseCostProviderVtableRva || aVtableRva == kGpsFilteredCostProviderVtableRva; +} + +class ScopedGpsProviderClassPatch +{ +public: + ScopedGpsProviderClassPatch(void* aProvider, uint32_t aSearchCall) + : m_provider(aProvider) + , m_searchCall(aSearchCall) + { + if (!kEnableGpsProviderClassPatch || !m_provider) + { + return; + } + + if (!IsKnownGpsCostProvider(m_provider, m_vtableRva)) + { + LogPatch("gps-provider-class-patch skipped", "reason=unknown-provider"); + return; + } + + m_table = reinterpret_cast(reinterpret_cast(m_provider) + 0x08); + if (!IsReadableMemory(m_table, sizeof(m_original))) + { + LogPatch("gps-provider-class-patch skipped", "reason=unreadable-table"); + return; + } + + std::memcpy(m_original.data(), m_table, sizeof(m_original)); + std::memcpy(m_table, kGpsPatchedProviderClassMultipliers.data(), sizeof(m_original)); + m_active = true; + LogPatch("gps-provider-class-patch applied", nullptr); + } + + ~ScopedGpsProviderClassPatch() + { + if (!m_active || !m_table) + { + return; + } + + std::memcpy(m_table, m_original.data(), sizeof(m_original)); + LogPatch("gps-provider-class-patch restored", nullptr); + } + +private: + void LogPatch(const char* aMessage, const char* aReason) + { + if (!kEnableGpsProviderClassPatchTrace) + { + return; + } + + const auto patchLogCall = NextGpsAsyncLogCall(gGpsProviderClassPatchLogCount); + if (patchLogCall >= 48) + { + return; + } + + std::ostringstream line; + line << aMessage << " patchCall=" << patchLogCall << " searchCall=" << m_searchCall; + AppendPointerWithRva(line, "provider", m_provider); + line << " provider_vtable_rva=0x" << std::hex << m_vtableRva << std::dec; + 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", kGpsPatchedProviderClassMultipliers.data(), + kGpsPatchedProviderClassMultipliers.size()); + LogRed4ext(line.str()); + } + + void* m_provider = nullptr; + float* m_table = nullptr; + uint32_t m_searchCall = 0; + uintptr_t m_vtableRva = 0; + bool m_active = false; + std::array m_original{}; +}; + void AppendRouteJobSummary(std::ostringstream& aLine, const char* aPrefix, void* aJob) { const auto prefix = std::string(aPrefix); @@ -3544,7 +3655,7 @@ int32_t DetourGpsSearch(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHan { std::lock_guard lock(gGpsCostLogMutex); callCount = gGpsSearchLogCount++; - shouldLog = callCount < kMaxCalls; + shouldLog = kEnableGpsTraceHooks && callCount < kMaxCalls; } if (shouldLog) @@ -3569,6 +3680,7 @@ int32_t DetourGpsSearch(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHan int32_t result = 0x80000008; if (gOriginalGpsSearch) { + ScopedGpsProviderClassPatch providerPatch(aCostProvider, callCount); result = gOriginalGpsSearch(aGraph, aStartHandle, aTargetHandle, aStartPoint, aTargetPoint, aCostProvider, aOutPath, aOutCount); } @@ -4072,13 +4184,16 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e reinterpret_cast(&DetourGpsRouteJobStart), reinterpret_cast(&gOriginalGpsRouteJobStart)); } + if (kEnableGpsProviderClassPatch || (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks)) + { + AttachProbeHook("GPSSearch 0x44f054", kGpsSearchRva, reinterpret_cast(&DetourGpsSearch), + reinterpret_cast(&gOriginalGpsSearch)); + } if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks) { AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva, reinterpret_cast(&DetourGpsRouteProducer), reinterpret_cast(&gOriginalGpsRouteProducer)); - AttachProbeHook("GPSSearch 0x44f054", kGpsSearchRva, reinterpret_cast(&DetourGpsSearch), - reinterpret_cast(&gOriginalGpsSearch)); AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast(&DetourGpsEdgeCost), reinterpret_cast(&gOriginalGpsEdgeCost)); } @@ -4225,10 +4340,13 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e { DetachProbeHook("GPSRouteJobStart 0x818928", kGpsRouteJobStartRva); } + if (kEnableGpsProviderClassPatch || (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks)) + { + DetachProbeHook("GPSSearch 0x44f054", kGpsSearchRva); + } if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks) { DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva); - DetachProbeHook("GPSSearch 0x44f054", kGpsSearchRva); DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva); } if (kEnableGpsMultiplierHooks)