Probe GPS packed handle resolver

This commit is contained in:
2026-06-20 21:45:09 -05:00
parent bf72dcf6e3
commit 407c44b1ad
3 changed files with 134 additions and 9 deletions
+34 -7
View File
@@ -45,6 +45,7 @@ Active hooks:
- `GPSQueryStatus 0xaa5704` - `GPSQueryStatus 0xaa5704`
- `GPSSearch 0x44f054` - `GPSSearch 0x44f054`
- `GPSRouteProducer 0x44cc7c` - `GPSRouteProducer 0x44cc7c`
- `GPSResolveHandle 0x44e1a8`
- `GPSEdgeCost 0x44f838` - `GPSEdgeCost 0x44f838`
- filtered provider filter `0x44ff68` - filtered provider filter `0x44ff68`
- base provider filter `0x450b08` - base provider filter `0x450b08`
@@ -85,6 +86,7 @@ GPS pipeline landmarks:
- query dispatch: `0x70a570` - query dispatch: `0x70a570`
- route producer: `0x44cc7c` - route producer: `0x44cc7c`
- local search loop: `0x44f054` - local search loop: `0x44f054`
- packed-handle resolver: `0x44e1a8`
- edge cost: `0x44f838` - edge cost: `0x44f838`
- query result fetch: `0x7094b8` - query result fetch: `0x7094b8`
@@ -117,6 +119,26 @@ Route result records:
- The log's human wall timestamp has a millisecond rollover bug around second - The log's human wall timestamp has a millisecond rollover bug around second
boundaries. Use the `+Nms` elapsed field for ordering and durations. boundaries. Use the `+Nms` elapsed field for ordering and durations.
Packed-handle resolver:
- `0x44e1a8` decodes the packed route handle used by the search/materialization
side:
- low 20 bits: point index
- bits 20..47: segment index
- high bits: generation/check value
- It resolves the handle into a 0x68-byte segment record and a 0x14-byte point
record. The point record's `+0x13 & 0x3f` is the search-side class consumed by
`0x44f838`.
- The current probe logs selected resolver calls at return RVAs `0x44d26c`,
`0x44d304`, `0x44d99f`, `0x44d9cd`, `0x44db50`, `0x44db88`, and `0x44dd92`,
plus the first 64 successful calls as a guard against a bad static address
assumption. Logs are capped at 512 calls.
- Each resolver line includes the decoded handle, segment/point pointers,
segment pointer fields `+0x08` through `+0x38`, raw segment bytes, and raw
point bytes. The goal is to determine whether the selected-path segment still
carries, or points to, the traffic lane hash/resource flags before the final
0x28 route records are emitted.
## Latest 23:08 Test Result ## Latest 23:08 Test Result
The user clicked: The user clicked:
@@ -182,13 +204,18 @@ Inference:
## Next Work ## Next Work
- Static disassembly around result-drain/producer RVAs `0x520783`, - Install the resolver probe and run the usual map route routine.
`0x41be2c`, `0x41be94`, `0x70a42c`, and `0x8d20d4`. - Archive the plugin log as `logs/EdgeWeightGPS_resolve_handle_probe_<time>.log`.
- The plausible patch point is upstream of result copy: find where search - Compare `GPSResolveHandle` handles/segment bytes against the final
output is converted to 0x28 route records or where candidate lanes are ranked `GPSQueryResultFetch` route-record `h00` values joined to
with enough lane-hash/resource context to prefer highway/major-road lanes. `work/raw-segment-json/all.traffic_persistent.json`.
- Post-`0x7094b8` rewriting would likely only change displayed path records and - If the resolver's 0x68 segment record or its pointer fields expose
risks desynchronizing instructions from the real planner. `worldTrafficLanePersistent.nodeRefHash` or equivalent road/highway flags,
use that data inside `DetourGpsEdgeCost` to weight true resource-side
highways/major roads. If not, follow the segment pointer fields statically.
- Avoid post-`0x7094b8` rewriting unless everything upstream fails; that would
likely only change displayed path records and risks desynchronizing
instructions from the real planner.
## Useful Commands ## Useful Commands
+7 -2
View File
@@ -74,12 +74,17 @@ Active build note:
`0x70a42c` submissions, `0x7094b8` result records, and local `0x70a42c` submissions, `0x7094b8` result records, and local
search/provider-cost stats. search/provider-cost stats.
- It hooks `GPSSearch` (`0x44f054`), `GPSRouteProducer` (`0x44cc7c`), - It hooks `GPSSearch` (`0x44f054`), `GPSRouteProducer` (`0x44cc7c`),
`GPSEdgeCost` (`0x44f838`), and the two cost-provider filters `GPSResolveHandle` (`0x44e1a8`), `GPSEdgeCost` (`0x44f838`), and the two
`0x44ff68`/`0x450b08`. cost-provider filters `0x44ff68`/`0x450b08`.
- The filter probe records pass/fail counts by point class and by the - The filter probe records pass/fail counts by point class and by the
`point + 0x10` mask bits tested against provider masks at `+0x108`/`+0x10a`. `point + 0x10` mask bits tested against provider masks at `+0x108`/`+0x10a`.
This is meant to identify whether class/mask filtering, rather than the edge This is meant to identify whether class/mask filtering, rather than the edge
multiplier table, is dominating route choice. multiplier table, is dominating route choice.
- The resolver probe logs selected packed-handle resolutions from the
search/materialization corridor. It decodes point index, segment index, and
generation bits, then dumps the resolved 0x68-byte segment record and
0x14-byte point record. The purpose is to see whether the edge-cost stage can
reach final lane hashes/resource-side road classes before a path is chosen.
Current route-probe focus: Current route-probe focus:
+93
View File
@@ -168,6 +168,7 @@ constexpr uintptr_t kGpsRouteFinishRva = 0x11F5F60;
constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8; constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8;
constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C; constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C;
constexpr uintptr_t kGpsSearchRva = 0x044F054; constexpr uintptr_t kGpsSearchRva = 0x044F054;
constexpr uintptr_t kGpsResolveHandleRva = 0x044E1A8;
constexpr uintptr_t kGpsEdgeCostRva = 0x044F838; constexpr uintptr_t kGpsEdgeCostRva = 0x044F838;
constexpr uintptr_t kGpsFilteredProviderFilterRva = 0x044FF68; constexpr uintptr_t kGpsFilteredProviderFilterRva = 0x044FF68;
constexpr uintptr_t kGpsBaseProviderFilterRva = 0x0450B08; constexpr uintptr_t kGpsBaseProviderFilterRva = 0x0450B08;
@@ -264,6 +265,7 @@ uint32_t gGpsRouteFinishLogCount = 0;
uint32_t gGpsRouteJobBuildLogCount = 0; uint32_t gGpsRouteJobBuildLogCount = 0;
uint32_t gGpsRouteProducerLogCount = 0; uint32_t gGpsRouteProducerLogCount = 0;
uint32_t gGpsSearchLogCount = 0; uint32_t gGpsSearchLogCount = 0;
uint32_t gGpsResolveHandleLogCount = 0;
uint32_t gGpsEdgeCostLogCount = 0; uint32_t gGpsEdgeCostLogCount = 0;
uint32_t gGpsAuxMultiplierLogCount = 0; uint32_t gGpsAuxMultiplierLogCount = 0;
uint32_t gGpsNodeMultiplierLogCount = 0; uint32_t gGpsNodeMultiplierLogCount = 0;
@@ -413,6 +415,7 @@ using GpsRouteJobBuildFunc = void* (*)(void* aJob, void* aOut, void* aRequest);
using GpsRouteProducerFunc = int32_t (*)(void* aGraph, void* aQuery, void* aOutResult); using GpsRouteProducerFunc = int32_t (*)(void* aGraph, void* aQuery, void* aOutResult);
using GpsSearchFunc = int32_t (*)(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHandle, void* aStartPoint, using GpsSearchFunc = int32_t (*)(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHandle, void* aStartPoint,
void* aTargetPoint, void* aCostProvider, void* aOutPath, uint32_t* aOutCount); void* aTargetPoint, void* aCostProvider, void* aOutPath, uint32_t* aOutCount);
using GpsResolveHandleFunc = int32_t (*)(void* aGraph, uint64_t aHandle, void* aOutSegment, void* aOutPoint);
using GpsEdgeCostFunc = float (*)(void* aCostProvider, void* aCurrentState, void* aNeighborState, using GpsEdgeCostFunc = float (*)(void* aCostProvider, void* aCurrentState, void* aNeighborState,
uint64_t aPreviousHandle, void* aPreviousSegment, void* aPreviousPoint, uint64_t aPreviousHandle, void* aPreviousSegment, void* aPreviousPoint,
uint64_t aCurrentHandle, void* aCurrentSegment, void* aCurrentPoint, uint64_t aCurrentHandle, void* aCurrentSegment, void* aCurrentPoint,
@@ -478,6 +481,7 @@ GpsRouteFinishFunc gOriginalGpsRouteFinish = nullptr;
GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr; GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr;
GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr; GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr;
GpsSearchFunc gOriginalGpsSearch = nullptr; GpsSearchFunc gOriginalGpsSearch = nullptr;
GpsResolveHandleFunc gOriginalGpsResolveHandle = nullptr;
GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr; GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr;
GpsProviderFilterFunc gOriginalGpsFilteredProviderFilter = nullptr; GpsProviderFilterFunc gOriginalGpsFilteredProviderFilter = nullptr;
GpsProviderFilterFunc gOriginalGpsBaseProviderFilter = nullptr; GpsProviderFilterFunc gOriginalGpsBaseProviderFilter = nullptr;
@@ -1055,6 +1059,33 @@ void AppendGpsPointSummary(std::ostringstream& aLine, const char* aPrefix, void*
aLine << " " << prefix << "_raw13=0x" << std::hex << static_cast<uint32_t>(byte13) << std::dec; aLine << " " << prefix << "_raw13=0x" << std::hex << static_cast<uint32_t>(byte13) << std::dec;
} }
void AppendGpsPackedHandleSummary(std::ostringstream& aLine, const char* aPrefix, uint64_t aHandle)
{
const auto pointIndex = aHandle & 0xFFFFF;
const auto segmentIndex = (aHandle >> 20) & 0x0FFFFFFF;
const auto generation = aHandle >> 48;
aLine << " " << aPrefix << "_pointIndex=" << pointIndex;
aLine << " " << aPrefix << "_segmentIndex=" << segmentIndex;
aLine << " " << aPrefix << "_generation=" << generation;
}
bool IsSelectedGpsResolveReturnRva(uintptr_t aReturnRva)
{
constexpr std::array<uintptr_t, 7> kSelectedReturnRvas = {
0x44D26C,
0x44D304,
0x44D99F,
0x44D9CD,
0x44DB50,
0x44DB88,
0x44DD92,
};
return std::find(kSelectedReturnRvas.begin(), kSelectedReturnRvas.end(), aReturnRva) !=
kSelectedReturnRvas.end();
}
void AppendGpsCostProviderSummary(std::ostringstream& aLine, const char* aPrefix, void* aProvider) void AppendGpsCostProviderSummary(std::ostringstream& aLine, const char* aPrefix, void* aProvider)
{ {
const auto prefix = std::string(aPrefix); const auto prefix = std::string(aPrefix);
@@ -3904,6 +3935,64 @@ bool DetourGpsBaseProviderFilter(void* aCostProvider, uint64_t aHandle, void* aS
return result; return result;
} }
int32_t DetourGpsResolveHandle(void* aGraph, uint64_t aHandle, void* aOutSegment, void* aOutPoint)
{
const auto returnAddress = __builtin_return_address(0);
uintptr_t returnRva = 0;
TryGetImageRva(returnAddress, returnRva);
int32_t result = 0x80000008;
if (gOriginalGpsResolveHandle)
{
result = gOriginalGpsResolveHandle(aGraph, aHandle, aOutSegment, aOutPoint);
}
void* segment = nullptr;
void* point = nullptr;
const auto hasSegment = aOutSegment && TryReadMemory(aOutSegment, segment);
const auto hasPoint = aOutPoint && TryReadMemory(aOutPoint, point);
uint32_t callCount = 0;
bool shouldLog = false;
{
std::lock_guard lock(gGpsCostLogMutex);
callCount = gGpsResolveHandleLogCount++;
shouldLog = result >= 0 && hasSegment && hasPoint &&
(IsSelectedGpsResolveReturnRva(returnRva) || callCount < 64) && callCount < 512;
}
if (shouldLog)
{
std::ostringstream line;
line << "hook GPSResolveHandle 0x44e1a8 call=" << callCount << " value=0x" << std::hex
<< static_cast<uint32_t>(result) << " handle=0x" << aHandle << std::dec;
AppendGpsPackedHandleSummary(line, "handle", aHandle);
AppendPointerWithRva(line, "graph", aGraph);
AppendPointerWithRva(line, "outSegment", aOutSegment);
AppendPointerWithRva(line, "outPoint", aOutPoint);
AppendPointerWithRva(line, "segment", segment);
AppendPointerWithRva(line, "point", point);
AppendPointerField(line, "segment_p08", segment, 0x08);
AppendPointerField(line, "segment_p10", segment, 0x10);
AppendPointerField(line, "segment_p18", segment, 0x18);
AppendPointerField(line, "segment_p20", segment, 0x20);
AppendPointerField(line, "segment_p28", segment, 0x28);
AppendPointerField(line, "segment_p30", segment, 0x30);
AppendPointerField(line, "segment_p38", segment, 0x38);
AppendGpsPointSummary(line, "pointSummary", point);
AppendBytesPointer(line, "segment_raw68", segment, 0x68);
AppendBytesPointer(line, "point_raw14", point, 0x14);
if (gActiveGpsRouteProducerCall != UINT32_MAX)
{
line << " routeProducerCall=" << gActiveGpsRouteProducerCall;
}
AppendReturnRva(line, returnAddress);
LogRed4ext(line.str());
}
return result;
}
float DetourGpsEdgeCost(void* aCostProvider, void* aCurrentState, void* aNeighborState, uint64_t aPreviousHandle, float DetourGpsEdgeCost(void* aCostProvider, void* aCurrentState, void* aNeighborState, uint64_t aPreviousHandle,
void* aPreviousSegment, void* aPreviousPoint, uint64_t aCurrentHandle, void* aCurrentSegment, void* aPreviousSegment, void* aPreviousPoint, uint64_t aCurrentHandle, void* aCurrentSegment,
void* aCurrentPoint, uint64_t aNeighborHandle, void* aNeighborSegment, void* aNeighborPoint) void* aCurrentPoint, uint64_t aNeighborHandle, void* aNeighborSegment, void* aNeighborPoint)
@@ -4398,6 +4487,9 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva, AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva,
reinterpret_cast<void*>(&DetourGpsRouteProducer), reinterpret_cast<void*>(&DetourGpsRouteProducer),
reinterpret_cast<void**>(&gOriginalGpsRouteProducer)); reinterpret_cast<void**>(&gOriginalGpsRouteProducer));
AttachProbeHook("GPSResolveHandle 0x44e1a8", kGpsResolveHandleRva,
reinterpret_cast<void*>(&DetourGpsResolveHandle),
reinterpret_cast<void**>(&gOriginalGpsResolveHandle));
AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast<void*>(&DetourGpsEdgeCost), AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast<void*>(&DetourGpsEdgeCost),
reinterpret_cast<void**>(&gOriginalGpsEdgeCost)); reinterpret_cast<void**>(&gOriginalGpsEdgeCost));
AttachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva, AttachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva,
@@ -4560,6 +4652,7 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks) if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks)
{ {
DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva); DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva);
DetachProbeHook("GPSResolveHandle 0x44e1a8", kGpsResolveHandleRva);
DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva); DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva);
DetachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva); DetachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva);
DetachProbeHook("GPSBaseProviderFilter 0x450b08", kGpsBaseProviderFilterRva); DetachProbeHook("GPSBaseProviderFilter 0x450b08", kGpsBaseProviderFilterRva);