diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index 98935db..cf027ac 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -102,6 +102,7 @@ constexpr bool kEnableGpsProviderClassPatchTrace = false; constexpr bool kEnableGpsTraceHooks = true; constexpr bool kEnableGpsQueryLifecycleHooks = true; constexpr bool kEnableGpsAsyncTrafficProbe = true; +constexpr bool kEnableGpsTrafficEdgeWeightPatch = true; constexpr bool kEnableGpsRouteJobLifecycleHooks = false; constexpr bool kEnableGpsRouteJobStartTrace = false; constexpr bool kEnableGpsLocalSearchHooks = false; @@ -165,6 +166,7 @@ constexpr uintptr_t kGpsMapRouteSubmitReturnRva = 0x08D20D4; constexpr uintptr_t kGpsDispatchAdvanceRva = 0x08D17D8; constexpr uintptr_t kGpsTrafficResultTakeRva = 0x0709D5C; constexpr uintptr_t kGpsRoutePostprocessRva = 0x044830C; +constexpr uintptr_t kGpsTrafficEdgeScoreRva = 0x08D46CC; constexpr uintptr_t kGpsAsyncWorkerTickRva = 0x0126B28; constexpr uintptr_t kGpsRouteJobStartRva = 0x0818928; constexpr uintptr_t kGpsRouteJobPollRva = 0x0883CD8; @@ -189,6 +191,12 @@ constexpr std::array kGpsPatchedCostMultipliers = { 12.0f, 7.0f, 2.5f, 0.0f, 3.0f, 2.0f, 1.25f, }; constexpr size_t kGpsProviderClassCount = 64; +constexpr uint16_t kTrafficLaneFlagPavement = 0x0008; +constexpr uint16_t kTrafficLaneFlagRoad = 0x0010; +constexpr uint16_t kTrafficLaneFlagGpsOnly = 0x0200; +constexpr uint16_t kTrafficLaneFlagNoAiDriving = 0x2000; +constexpr uint16_t kTrafficLaneFlagHighway = 0x4000; +constexpr uint16_t kTrafficLaneFlagNoAutodrive = 0x8000; struct GpsProviderClassOverride { @@ -268,6 +276,7 @@ uint32_t gGpsQueryStatusLogCount = 0; uint32_t gGpsDispatchAdvanceLogCount = 0; uint32_t gGpsTrafficResultTakeLogCount = 0; uint32_t gGpsRoutePostprocessLogCount = 0; +LONG gGpsTrafficEdgeScoreLogCount = 0; uint32_t gGpsAsyncWorkerTickLogCount = 0; uint32_t gGpsRouteJobStartLogCount = 0; uint32_t gGpsRouteJobPollLogCount = 0; @@ -423,6 +432,7 @@ using GpsDispatchAdvanceFunc = void (*)(void* aRouteSystem, void* aRuntimeQuery, using GpsTrafficResultTakeFunc = void* (*)(void* aTrafficService, void* aOutResult, uint32_t aTicket, void* aOutMeta); using GpsRoutePostprocessFunc = void (*)(void* aOutPath, void* aTrafficResult, void* aRouteRecords, float aScale); +using GpsTrafficEdgeScoreFunc = float (*)(void* aSearchContext, void* aCandidate, void* aReferencePoint); using GpsAsyncWorkerTickFunc = void (*)(void* aRouteSystem); using GpsRouteJobStartFunc = bool (*)(void* aRouteSystem, void* aActiveEntry, float aDelta, void* aOutLocal); using GpsRouteJobPollFunc = bool (*)(void* aRouteSystem, void* aActiveEntry); @@ -492,6 +502,7 @@ GpsQueryStatusFunc gOriginalGpsQueryStatus = nullptr; GpsDispatchAdvanceFunc gOriginalGpsDispatchAdvance = nullptr; GpsTrafficResultTakeFunc gOriginalGpsTrafficResultTake = nullptr; GpsRoutePostprocessFunc gOriginalGpsRoutePostprocess = nullptr; +GpsTrafficEdgeScoreFunc gOriginalGpsTrafficEdgeScore = nullptr; GpsAsyncWorkerTickFunc gOriginalGpsAsyncWorkerTick = nullptr; GpsRouteJobStartFunc gOriginalGpsRouteJobStart = nullptr; GpsRouteJobPollFunc gOriginalGpsRouteJobPoll = nullptr; @@ -3759,6 +3770,122 @@ void DetourGpsRoutePostprocess(void* aOutPath, void* aTrafficResult, void* aRout } } +const char* TrafficLaneCategory(uint16_t aFlags) +{ + if ((aFlags & kTrafficLaneFlagHighway) != 0) + { + return "highway"; + } + if ((aFlags & kTrafficLaneFlagRoad) != 0) + { + return "road"; + } + if ((aFlags & kTrafficLaneFlagPavement) != 0) + { + return "pavement"; + } + if ((aFlags & kTrafficLaneFlagGpsOnly) != 0) + { + return "gpsonly"; + } + + return "other"; +} + +float TrafficEdgeScoreMultiplier(uint16_t aFlags, bool aDriving) +{ + if (!aDriving) + { + return 1.0f; + } + + if ((aFlags & kTrafficLaneFlagHighway) != 0) + { + return 0.35f; + } + if ((aFlags & kTrafficLaneFlagRoad) != 0) + { + return 0.70f; + } + if ((aFlags & kTrafficLaneFlagGpsOnly) != 0) + { + return 1.35f; + } + if ((aFlags & kTrafficLaneFlagPavement) != 0) + { + return 2.75f; + } + + return 1.50f; +} + +float DetourGpsTrafficEdgeScore(void* aSearchContext, void* aCandidate, void* aReferencePoint) +{ + float baseScore = 0.0f; + if (gOriginalGpsTrafficEdgeScore) + { + baseScore = gOriginalGpsTrafficEdgeScore(aSearchContext, aCandidate, aReferencePoint); + } + + void* query = nullptr; + void* lane = nullptr; + uint8_t queryMode = 0xFF; + uint16_t laneFlags = 0; + + if (aSearchContext) + { + query = *reinterpret_cast(reinterpret_cast(aSearchContext) + 0x10); + } + if (query) + { + queryMode = *reinterpret_cast(reinterpret_cast(query) + 0x41); + } + if (aCandidate) + { + lane = *reinterpret_cast(aCandidate); + } + if (lane) + { + laneFlags = *reinterpret_cast(reinterpret_cast(lane) + 0x88); + } + + const auto driving = queryMode == 0; + const auto multiplier = TrafficEdgeScoreMultiplier(laneFlags, driving); + const auto patchedScore = baseScore * multiplier; + + constexpr LONG kMaxLoggedCalls = 256; + LONG callCount = -1; + if (gGpsTrafficEdgeScoreLogCount < kMaxLoggedCalls) + { + callCount = InterlockedIncrement(&gGpsTrafficEdgeScoreLogCount) - 1; + } + + if (callCount >= 0 && callCount < kMaxLoggedCalls) + { + std::ostringstream line; + const auto flags = line.flags(); + const auto precision = line.precision(); + line << "hook GPSTrafficEdgeScore 0x8d46cc call=" << callCount + << " category=" << TrafficLaneCategory(laneFlags) + << " driving=" << static_cast(driving) + << " query41=" << static_cast(queryMode) + << " laneFlags88=0x" << std::hex << laneFlags << std::dec + << " base=" << std::fixed << std::setprecision(3) << baseScore + << " multiplier=" << multiplier + << " result=" << patchedScore; + line.flags(flags); + line.precision(precision); + AppendPointerWithRva(line, "ctx", aSearchContext); + AppendPointerWithRva(line, "query", query); + AppendPointerWithRva(line, "candidate", aCandidate); + AppendPointerWithRva(line, "lane", lane); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + return patchedScore; +} + void DetourGpsAsyncWorkerTick(void* aRouteSystem) { constexpr uint32_t kMaxCalls = 192; @@ -4667,6 +4794,12 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e reinterpret_cast(&DetourGpsRoutePostprocess), reinterpret_cast(&gOriginalGpsRoutePostprocess)); } + if (kEnableGpsTrafficEdgeWeightPatch) + { + AttachProbeHook("GPSTrafficEdgeScore 0x8d46cc", kGpsTrafficEdgeScoreRva, + reinterpret_cast(&DetourGpsTrafficEdgeScore), + reinterpret_cast(&gOriginalGpsTrafficEdgeScore)); + } if (kEnableGpsRouteJobLifecycleHooks) { AttachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva, @@ -4853,6 +4986,10 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e DetachProbeHook("GPSTrafficResultTake 0x709d5c", kGpsTrafficResultTakeRva); DetachProbeHook("GPSRoutePostprocess 0x44830c", kGpsRoutePostprocessRva); } + if (kEnableGpsTrafficEdgeWeightPatch) + { + DetachProbeHook("GPSTrafficEdgeScore 0x8d46cc", kGpsTrafficEdgeScoreRva); + } if (kEnableGpsRouteJobLifecycleHooks) { DetachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva);