From 0891a6265a101c26156cc21555bbbc208b94d3c0 Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Sat, 20 Jun 2026 14:43:13 -0500 Subject: [PATCH] Probe GPS async route worker --- red4ext/EdgeWeightGPS/src/Main.cpp | 386 +++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index 21a5d12..e386a99 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -150,6 +150,12 @@ constexpr uintptr_t kGpsQuerySubmitRva = 0x070A42C; constexpr uintptr_t kGpsQueryDispatchRva = 0x070A570; constexpr uintptr_t kGpsQueryResultFetchRva = 0x07094B8; constexpr uintptr_t kGpsQueryStatusRva = 0x0AA5704; +constexpr uintptr_t kGpsDispatchAdvanceRva = 0x08D17D8; +constexpr uintptr_t kGpsAsyncWorkerTickRva = 0x0126B28; +constexpr uintptr_t kGpsRouteJobStartRva = 0x0818928; +constexpr uintptr_t kGpsRouteJobPollRva = 0x0883CD8; +constexpr uintptr_t kGpsRouteFinishRva = 0x11F5F60; +constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8; constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C; constexpr uintptr_t kGpsSearchRva = 0x044F054; constexpr uintptr_t kGpsEdgeCostRva = 0x044F838; @@ -213,11 +219,18 @@ uint32_t gGpsQuerySubmitLogCount = 0; uint32_t gGpsQueryDispatchLogCount = 0; uint32_t gGpsQueryResultFetchLogCount = 0; uint32_t gGpsQueryStatusLogCount = 0; +uint32_t gGpsDispatchAdvanceLogCount = 0; +uint32_t gGpsAsyncWorkerTickLogCount = 0; +uint32_t gGpsRouteJobStartLogCount = 0; +uint32_t gGpsRouteJobPollLogCount = 0; +uint32_t gGpsRouteFinishLogCount = 0; +uint32_t gGpsRouteJobBuildLogCount = 0; uint32_t gGpsRouteProducerLogCount = 0; uint32_t gGpsSearchLogCount = 0; uint32_t gGpsEdgeCostLogCount = 0; std::array gGpsEdgeCostClassLogCounts{}; std::mutex gGpsCostLogMutex; +std::mutex gGpsAsyncLogMutex; uint64_t gLogStartTick = 0; uint64_t gLogFrequency = 0; void* gMappinSetTrackedTarget = nullptr; @@ -312,6 +325,13 @@ using GpsQuerySubmitFunc = int32_t (*)(void* aManager, void* aQuery); using GpsQueryDispatchFunc = void (*)(void* aRuntimeQuery, void* aTargetPosition, void* aArg3, void* aArg4); using GpsQueryResultFetchFunc = bool (*)(void* aManager, uint32_t aQueryId, void* aOutPath); using GpsQueryStatusFunc = int32_t (*)(void* aManager, uint32_t aQueryId); +using GpsDispatchAdvanceFunc = void (*)(void* aRouteSystem, void* aRuntimeQuery, void* aOutTicket, + void* aTargetPosition, void* aSourcePosition); +using GpsAsyncWorkerTickFunc = void (*)(void* aRouteSystem); +using GpsRouteJobStartFunc = bool (*)(void* aRouteSystem, void* aActiveEntry, float aDelta, void* aOutLocal); +using GpsRouteJobPollFunc = bool (*)(void* aRouteSystem, void* aActiveEntry); +using GpsRouteFinishFunc = void* (*)(void* aResultQueue, void* aOutEntry, void* aActiveEntry, void* aLocalResult); +using GpsRouteJobBuildFunc = void* (*)(void* aJob, void* aOut, void* aRequest); using GpsRouteProducerFunc = int32_t (*)(void* aGraph, void* aQuery, void* aOutResult); using GpsSearchFunc = int32_t (*)(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHandle, void* aStartPoint, void* aTargetPoint, void* aCostProvider, void* aOutPath, uint32_t* aOutCount); @@ -370,6 +390,12 @@ GpsQuerySubmitFunc gOriginalGpsQuerySubmit = nullptr; GpsQueryDispatchFunc gOriginalGpsQueryDispatch = nullptr; GpsQueryResultFetchFunc gOriginalGpsQueryResultFetch = nullptr; GpsQueryStatusFunc gOriginalGpsQueryStatus = nullptr; +GpsDispatchAdvanceFunc gOriginalGpsDispatchAdvance = nullptr; +GpsAsyncWorkerTickFunc gOriginalGpsAsyncWorkerTick = nullptr; +GpsRouteJobStartFunc gOriginalGpsRouteJobStart = nullptr; +GpsRouteJobPollFunc gOriginalGpsRouteJobPoll = nullptr; +GpsRouteFinishFunc gOriginalGpsRouteFinish = nullptr; +GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr; GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr; GpsSearchFunc gOriginalGpsSearch = nullptr; GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr; @@ -1414,6 +1440,138 @@ void AppendGpsResultObjectSummary(std::ostringstream& aLine, const char* aPrefix AppendUint64Field(aLine, (std::string(aPrefix) + "_u50").c_str(), aResultObject, 0x50); } +uint32_t NextGpsAsyncLogCall(uint32_t& aCounter) +{ + std::lock_guard lock(gGpsAsyncLogMutex); + return aCounter++; +} + +bool TryReadUint32FieldValue(void* aObject, uintptr_t aOffset, uint32_t& aValue) +{ + return aObject && TryReadMemory(reinterpret_cast(reinterpret_cast(aObject) + aOffset), + aValue); +} + +void AppendRouteJobSummary(std::ostringstream& aLine, const char* aPrefix, void* aJob) +{ + const auto prefix = std::string(aPrefix); + AppendPointerWithRva(aLine, prefix.c_str(), aJob); + if (!aJob) + { + return; + } + + AppendUint32Field(aLine, (prefix + "_type10").c_str(), aJob, 0x10); + AppendPointerField(aLine, (prefix + "_result18").c_str(), aJob, 0x18); + AppendPointerField(aLine, (prefix + "_result30").c_str(), aJob, 0x30); + AppendUint32Field(aLine, (prefix + "_u4c").c_str(), aJob, 0x4C); + AppendUint32Field(aLine, (prefix + "_count50").c_str(), aJob, 0x50); + AppendUint32Field(aLine, (prefix + "_retry54").c_str(), aJob, 0x54); + AppendUint32Field(aLine, (prefix + "_flagsc0").c_str(), aJob, 0xC0); + AppendUint32Field(aLine, (prefix + "_statec4").c_str(), aJob, 0xC4); + AppendPointerField(aLine, (prefix + "_chunkse0").c_str(), aJob, 0xE0); + AppendUint32Field(aLine, (prefix + "_chunks_cap_e8").c_str(), aJob, 0xE8); + AppendUint32Field(aLine, (prefix + "_chunks_count_ec").c_str(), aJob, 0xEC); + AppendPointerField(aLine, (prefix + "_nodesf8").c_str(), aJob, 0xF8); +} + +void AppendRouteSystemSummary(std::ostringstream& aLine, const char* aPrefix, void* aRouteSystem) +{ + const auto prefix = std::string(aPrefix); + AppendPointerWithRva(aLine, prefix.c_str(), aRouteSystem); + if (!aRouteSystem) + { + return; + } + + AppendUint32Field(aLine, (prefix + "_queued8fc0").c_str(), aRouteSystem, 0x8FC0); + AppendUint32Field(aLine, (prefix + "_active8fd0").c_str(), aRouteSystem, 0x8FD0); + AppendPointerField(aLine, (prefix + "_job90d0").c_str(), aRouteSystem, 0x90D0); + + auto* job = ReadPointerField(aRouteSystem, 0x90D0); + if (!job) + { + return; + } + + AppendRouteJobSummary(aLine, (prefix + "_job").c_str(), job); +} + +bool HasPendingRouteWork(void* aRouteSystem) +{ + uint32_t queued = 0; + uint32_t active = 0; + TryReadUint32FieldValue(aRouteSystem, 0x8FC0, queued); + TryReadUint32FieldValue(aRouteSystem, 0x8FD0, active); + return queued != 0 || active != 0 || ReadPointerField(aRouteSystem, 0x90D0) != nullptr; +} + +void AppendRouteRequestFields(std::ostringstream& aLine, const char* aPrefix, void* aObject, uintptr_t aOffset) +{ + const auto prefix = std::string(aPrefix); + auto* base = aObject ? reinterpret_cast(reinterpret_cast(aObject) + aOffset) : nullptr; + AppendPointerWithRva(aLine, prefix.c_str(), base); + if (!base) + { + return; + } + + AppendBytesPointer(aLine, (prefix + "_raw40").c_str(), base, 0x40); + AppendVector4Field(aLine, (prefix + "_vec00").c_str(), base, 0x00); + AppendVector4Field(aLine, (prefix + "_vec10").c_str(), base, 0x10); + AppendUint32Field(aLine, (prefix + "_u00").c_str(), base, 0x00); + AppendUint32Field(aLine, (prefix + "_u08").c_str(), base, 0x08); + AppendUint32Field(aLine, (prefix + "_u1c").c_str(), base, 0x1C); + AppendUint16Field(aLine, (prefix + "_u1e").c_str(), base, 0x1E); + AppendUint32Field(aLine, (prefix + "_u20").c_str(), base, 0x20); + AppendUint16Field(aLine, (prefix + "_u20w").c_str(), base, 0x20); + AppendUint16Field(aLine, (prefix + "_u24").c_str(), base, 0x24); + AppendUint16Field(aLine, (prefix + "_u26").c_str(), base, 0x26); + AppendUint16Field(aLine, (prefix + "_u28").c_str(), base, 0x28); + AppendUint32Field(aLine, (prefix + "_u70").c_str(), base, 0x70); + AppendByteField(aLine, (prefix + "_b74").c_str(), base, 0x74); + AppendByteField(aLine, (prefix + "_b75").c_str(), base, 0x75); + AppendByteField(aLine, (prefix + "_b76").c_str(), base, 0x76); + AppendByteField(aLine, (prefix + "_b77").c_str(), base, 0x77); +} + +void AppendRouteQueueEntrySummary(std::ostringstream& aLine, const char* aPrefix, void* aEntry) +{ + const auto prefix = std::string(aPrefix); + AppendPointerWithRva(aLine, prefix.c_str(), aEntry); + if (!aEntry) + { + return; + } + + AppendUint32Field(aLine, (prefix + "_id00").c_str(), aEntry, 0x00); + AppendRouteRequestFields(aLine, (prefix + "_payload10").c_str(), aEntry, 0x10); + AppendPointerField(aLine, (prefix + "_vtable40").c_str(), aEntry, 0x40); + AppendPointerField(aLine, (prefix + "_self78").c_str(), aEntry, 0x78); + AppendUint32Field(aLine, (prefix + "_u80").c_str(), aEntry, 0x80); + AppendByteField(aLine, (prefix + "_b84").c_str(), aEntry, 0x84); + AppendByteField(aLine, (prefix + "_b85").c_str(), aEntry, 0x85); + AppendByteField(aLine, (prefix + "_b86").c_str(), aEntry, 0x86); + AppendByteField(aLine, (prefix + "_b87").c_str(), aEntry, 0x87); +} + +void AppendRouteOutputPointerSummary(std::ostringstream& aLine, const char* aPrefix, void* aOut) +{ + const auto prefix = std::string(aPrefix); + AppendPointerWithRva(aLine, prefix.c_str(), aOut); + if (!aOut) + { + return; + } + + auto* value = ReadPointerField(aOut, 0x00); + AppendPointerWithRva(aLine, (prefix + "_value").c_str(), value); + if (value && IsReadableMemory(value, 0x58)) + { + AppendGpsResultObjectSummary(aLine, (prefix + "_result").c_str(), value); + } +} + void TrackGpsQuery(uint32_t aQueryId, void* aManager, uintptr_t aSubmitReturnRva, uint32_t aSubmitCall) { if (aQueryId == UINT32_MAX) @@ -2841,6 +2999,216 @@ int32_t DetourGpsQueryStatus(void* aManager, uint32_t aQueryId) return result; } +void DetourGpsDispatchAdvance(void* aRouteSystem, void* aRuntimeQuery, void* aOutTicket, void* aTargetPosition, + void* aSourcePosition) +{ + constexpr uint32_t kMaxCalls = 128; + + const auto callCount = NextGpsAsyncLogCall(gGpsDispatchAdvanceLogCount); + const auto shouldLog = callCount < kMaxCalls; + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSDispatchAdvance 0x8d17d8 call=" << callCount; + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendPointerWithRva(line, "runtimeQuery", aRuntimeQuery); + AppendPointerWithRva(line, "outTicket", aOutTicket); + AppendUint32Field(line, "runtimeQuery_state138", aRuntimeQuery, 0x138); + AppendUint32Field(line, "runtimeQuery_bank13c", aRuntimeQuery, 0x13C); + AppendVector4Pointer(line, "target", aTargetPosition); + AppendVector4Pointer(line, "source", aSourcePosition); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + if (gOriginalGpsDispatchAdvance) + { + gOriginalGpsDispatchAdvance(aRouteSystem, aRuntimeQuery, aOutTicket, aTargetPosition, aSourcePosition); + } + + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSDispatchAdvance result call=" << callCount; + AppendUint32Field(line, "outTicket_value", aOutTicket, 0x00); + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + LogRed4ext(line.str()); + } +} + +void DetourGpsAsyncWorkerTick(void* aRouteSystem) +{ + constexpr uint32_t kMaxCalls = 192; + + const auto callCount = NextGpsAsyncLogCall(gGpsAsyncWorkerTickLogCount); + const auto shouldLogBefore = callCount < 16 || (callCount < kMaxCalls && HasPendingRouteWork(aRouteSystem)); + if (shouldLogBefore) + { + std::ostringstream line; + line << "hook GPSAsyncWorkerTick 0x126b28 call=" << callCount; + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + if (gOriginalGpsAsyncWorkerTick) + { + gOriginalGpsAsyncWorkerTick(aRouteSystem); + } + + const auto shouldLogAfter = shouldLogBefore || (callCount < kMaxCalls && HasPendingRouteWork(aRouteSystem)); + if (shouldLogAfter) + { + std::ostringstream line; + line << "hook GPSAsyncWorkerTick result call=" << callCount; + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + LogRed4ext(line.str()); + } +} + +bool DetourGpsRouteJobStart(void* aRouteSystem, void* aActiveEntry, float aDelta, void* aOutLocal) +{ + constexpr uint32_t kMaxCalls = 128; + + const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobStartLogCount); + const auto shouldLog = callCount < kMaxCalls; + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteJobStart 0x818928 call=" << callCount << " dt=" << aDelta; + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry); + AppendRouteOutputPointerSummary(line, "outLocal", aOutLocal); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + bool result = false; + if (gOriginalGpsRouteJobStart) + { + result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal); + } + + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteJobStart result call=" << callCount << " value=" << static_cast(result); + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry); + AppendRouteOutputPointerSummary(line, "outLocal", aOutLocal); + LogRed4ext(line.str()); + } + + return result; +} + +bool DetourGpsRouteJobPoll(void* aRouteSystem, void* aActiveEntry) +{ + constexpr uint32_t kMaxCalls = 128; + + const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobPollLogCount); + const auto shouldLog = callCount < kMaxCalls; + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteJobPoll 0x883cd8 call=" << callCount; + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + bool result = false; + if (gOriginalGpsRouteJobPoll) + { + result = gOriginalGpsRouteJobPoll(aRouteSystem, aActiveEntry); + } + + if (shouldLog || result) + { + std::ostringstream line; + line << "hook GPSRouteJobPoll result call=" << callCount << " value=" << static_cast(result); + AppendRouteSystemSummary(line, "routeSystem", aRouteSystem); + AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry); + LogRed4ext(line.str()); + } + + return result; +} + +void* DetourGpsRouteFinish(void* aResultQueue, void* aOutEntry, void* aActiveEntry, void* aLocalResult) +{ + constexpr uint32_t kMaxCalls = 128; + + const auto callCount = NextGpsAsyncLogCall(gGpsRouteFinishLogCount); + const auto shouldLog = callCount < kMaxCalls; + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteFinish 0x11f5f60 call=" << callCount; + AppendPointerWithRva(line, "resultQueue", aResultQueue); + AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry); + AppendRouteOutputPointerSummary(line, "localResult", aLocalResult); + AppendPointerWithRva(line, "outEntry", aOutEntry); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + void* result = nullptr; + if (gOriginalGpsRouteFinish) + { + result = gOriginalGpsRouteFinish(aResultQueue, aOutEntry, aActiveEntry, aLocalResult); + } + + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteFinish result call=" << callCount; + AppendPointerWithRva(line, "value", result); + AppendRouteOutputPointerSummary(line, "localResult", aLocalResult); + AppendPointerWithRva(line, "outEntry", aOutEntry); + LogRed4ext(line.str()); + } + + return result; +} + +void* DetourGpsRouteJobBuild(void* aJob, void* aOut, void* aRequest) +{ + constexpr uint32_t kMaxCalls = 128; + + const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobBuildLogCount); + const auto shouldLog = callCount < kMaxCalls; + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteJobBuild 0x818ba8 call=" << callCount; + AppendRouteJobSummary(line, "job", aJob); + AppendRouteOutputPointerSummary(line, "out", aOut); + AppendRouteRequestFields(line, "request", aRequest, 0x00); + AppendReturnRva(line, __builtin_return_address(0)); + LogRed4ext(line.str()); + } + + void* result = nullptr; + if (gOriginalGpsRouteJobBuild) + { + result = gOriginalGpsRouteJobBuild(aJob, aOut, aRequest); + } + + if (shouldLog) + { + std::ostringstream line; + line << "hook GPSRouteJobBuild result call=" << callCount; + AppendPointerWithRva(line, "value", result); + AppendRouteOutputPointerSummary(line, "out", aOut); + AppendRouteJobSummary(line, "job", aJob); + LogRed4ext(line.str()); + } + + return result; +} + int32_t DetourGpsRouteProducer(void* aGraph, void* aQuery, void* aOutResult) { constexpr uint32_t kMaxCalls = 512; @@ -3361,6 +3729,24 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e AttachProbeHook("GPSQueryStatus 0xaa5704", kGpsQueryStatusRva, reinterpret_cast(&DetourGpsQueryStatus), reinterpret_cast(&gOriginalGpsQueryStatus)); + AttachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva, + reinterpret_cast(&DetourGpsDispatchAdvance), + reinterpret_cast(&gOriginalGpsDispatchAdvance)); + AttachProbeHook("GPSAsyncWorkerTick 0x126b28", kGpsAsyncWorkerTickRva, + reinterpret_cast(&DetourGpsAsyncWorkerTick), + reinterpret_cast(&gOriginalGpsAsyncWorkerTick)); + AttachProbeHook("GPSRouteJobStart 0x818928", kGpsRouteJobStartRva, + reinterpret_cast(&DetourGpsRouteJobStart), + reinterpret_cast(&gOriginalGpsRouteJobStart)); + AttachProbeHook("GPSRouteJobPoll 0x883cd8", kGpsRouteJobPollRva, + reinterpret_cast(&DetourGpsRouteJobPoll), + reinterpret_cast(&gOriginalGpsRouteJobPoll)); + AttachProbeHook("GPSRouteFinish 0x11f5f60", kGpsRouteFinishRva, + reinterpret_cast(&DetourGpsRouteFinish), + reinterpret_cast(&gOriginalGpsRouteFinish)); + AttachProbeHook("GPSRouteJobBuild 0x818ba8", kGpsRouteJobBuildRva, + reinterpret_cast(&DetourGpsRouteJobBuild), + reinterpret_cast(&gOriginalGpsRouteJobBuild)); AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva, reinterpret_cast(&DetourGpsRouteProducer), reinterpret_cast(&gOriginalGpsRouteProducer));