# RED4ext logging shim `EdgeWeightGPS` is a RED4ext plugin used to probe native GPS/mappin code paths without changing gameplay data. It writes `EdgeWeightGPS.log` beside the installed DLL: ```text Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.log ``` Current behavior: - Logs plugin load/unload. - Logs the RED4ext plugin handle and SDK pointer value passed to `Main`. - Registers Running game-state callbacks so route tests can be timestamped. - Hooks selected native wrappers and cores around `GPSSystem`, mappin tracking, world-map mappin selection, and map path framing. - Hooks `JournalManager.TrackEntry` at RVA `0x5944fc`, which is the native handoff used by quest/objective world-map route plotting. - Dumps the `JournalManager` listener array at offsets `0x210`/`0x21c` and the listener vtable slots `0x28`, `0x30`, and `0x50`, matching the indirect calls made by the native `TrackEntry` implementation. - Hooks the non-default journal listener callbacks seen during route plotting: `0xea89a8`, `0xea8958`, `0xe63f80`, `0xe63e6c`, `0xe63f00`, `0x8d136c`, `0x431a34`, `0x55a4e4`, and `0x14de238`. - For those listener callbacks, logs the listener object, event pointer, common object fields, and vtable slots on candidate nested owner/service objects. - Hooks the route handoff found from the journal listener bridge: `0x598250`, `0x13763d8`, `0xaa62d0`, `0xaa6330`, `0x27abd7c`, and `0x5625a4`. - Logs mappin route-event fields, active/deactive route keys, the mappin active-route map at `system + 0x1a0`, and the route observer list at `system + 0x280`. - Hooks the route observer callbacks reached by route activation/deactivation: `0xaa6610`, `0xaa6628`, `0xaa63e0`, `0x27b10c0`, `0x295d4a0`, and `0x286a85c`. - Logs the route entry pointer and route object pointer passed to those observers, including route object vtable slots and common fields such as the active byte at offset `0x84`, the service lookup key at offset `0x8c`, and the runtime observer1 service-owner path used before its vtable `0x220` lookup. - Hooks observer1's generic service lookup at `0x287c44`, filters it to the runtime type pointer at `.data` RVA `0x342f6a8`, logs the returned service object and slot `0x220`, then dynamically hooks that service `0x220` route lookup to log its route-id output handle. - The observer1 service branch was later identified as a false positive: `0x287c44` returns the `JournalManager` object (`vtable 0x1430f0890`) and the dynamic slot `0x220` resolves route IDs to readable journal/UI metadata strings such as `internet`, `home`, `clubs`, and `arasaka`. It is now left in the source but no longer attached in the active probe. - Hooks the GPS query lifecycle found from the `RunGPSQuery`/`UpdateGPSQuery` native registration cluster: `0x29bd128` (`RunGPSQuery` body), `0x29bd254` (`UpdateGPSQuery` body), `0x70a42c` (shared query submitter), `0x70a570` (low-level query dispatch), `0x7094b8` (query result/path fetch), and `0xaa5704` (query status check). - Logs query endpoints, returned query IDs, shared submitter return RVAs, query state fields, status values, result-fetch success, and summarized path buffers/point counts. - The active probe now records query IDs returned from `0x70a42c` and always logs result/status polling for those tracked IDs. Startup polling of query IDs `0`/`1` is sampled only briefly so it cannot exhaust the route-click log budget. - Resolves the native mappin system when one of those paths fires, logs relevant vtable slot addresses, and temporarily hooks the route-adjacent slots. Active build note: - The currently installed build is read-only. It disables provider class multiplier overrides and enables GPS query lifecycle tracing plus local GPS search tracing. - The async route-job hooks are split behind `kEnableGpsRouteJobLifecycleHooks` and are disabled in the active probe; this keeps the log focused on `0x70a42c` submissions, `0x7094b8` result records, and local search/provider-cost stats. - It hooks `GPSSearch` (`0x44f054`), `GPSRouteProducer` (`0x44cc7c`), `GPSEdgeCost` (`0x44f838`), and the two cost-provider filters `0x44ff68`/`0x450b08`. - The filter probe records pass/fail counts by point class and by the `point + 0x10` mask bits tested against provider masks at `+0x108`/`+0x10a`. This is meant to identify whether class/mask filtering, rather than the edge multiplier table, is dominating route choice. Current route-probe focus: - `FrameMappinPath` wrapper/core: `0x27c4314`, `0x27bc1ec` - `SetSelectedMappin` wrappers/core: `0x27c4a38`, `0x27c4944`, `0x27c49c4`, `0x27c1684` - `TrackCustomPositionMappin` wrapper/core: `0x27c4aac`, `0x27c2318` - mappin-system slots `0x1f0`, `0x280`, and `0x2f0` - `JournalManager.TrackEntry` implementation: `0x5944fc` - non-default `TrackEntry` listener callbacks listed above - journal/mappin route bridge candidate: `0x598250` - mappin route-event enqueue: `0x13763d8` - mappin route-event handler: `0xaa62d0` - mappin route activate/deactivate: `0xaa6330`, `0x27abd7c` - mappin route observer callbacks: `0xaa6610`, `0xaa6628`, `0xaa63e0`, `0x27b10c0`, `0x295d4a0`, `0x286a85c` - observer1 service lookup and returned service route lookup: `0x287c44`, dynamic service vtable slot `0x220` (disabled after being classified as journal metadata) - route-build candidate called by the bridge: `0x5625a4` - GPS query lifecycle: `0x29bd128`, `0x29bd254`, `0x70a42c`, `0x70a570`, `0x7094b8`, `0xaa5704` Quest/objective pins did not fire the mappin tracking hooks in live tests. The REDscript decompile shows that those pins call `JournalManager.TrackEntry` instead, so the current useful runtime question is which native listener reacts to tracked-entry changes. Most recent controlled test: - `06:31:43`: user hit Continue / GPS tick logged. - `06:32:02`: automatic `TrackEntry` during load, no route input. - `06:32:27`: map opened. - `06:32:44`, `06:32:50`, `06:32:56`: deliberate quest/objective route plots produced `JournalManager::TrackEntry` calls from return RVA `0x26ac34e`. - After map open, the journal listener count rose to 254. Most listener entries dispatch to no-op `0x14a700`; the non-default callbacks above are the current drill-down targets. Newest controlled test: - User clock `01:05`: Continue on the main menu. The log saw `GPSSystem/Tick`. - User clock `01:27`: Space to continue. The log saw automatic `JournalManager.TrackEntry`, a dense `JournalRouteBridge` burst, and thirty `RouteBuildCandidate 0x5625a4` calls. About 5.5 seconds later the mappin route activation/deactivation functions fired. - User clock `01:52`: map open. Hovering icons produced the expected `SetSelectedMappin` bursts. - Deliberate quest route clicks produced `JournalManager.TrackEntry`, route event enqueue calls for old-route-off/new-route-on, and route event handler calls roughly 11-18 ms later. - Custom pin routing used the separate custom-position mappin path, then the shared route activate/deactivate helper. Latest observer1 probe result: - The `Observer1ServiceLookup` hook fired, but it proved observer1 is not route solving. It returned the same `JournalManager` vtable seen in earlier journal traces, and its slot `0x220` resolved numeric route IDs into journal/UI category strings. That branch is closed as marker metadata. Current static lead: - `RunGPSQuery` and `UpdateGPSQuery` are registered native thunks at `0x29bd5ac` and `0x29bd6c8`. Their deeper bodies are `0x29bd128` and `0x29bd254`. - `RunGPSQuery` submits a query through a subsystem reached via an object field at `+0x130`, returning a query ID or `-1`. - `UpdateGPSQuery` fetches a completed query result through `0x7094b8`, copies a path point buffer, and writes the resulting point array for script/UI consumption. - The shared submitter `0x70a42c` is also used by non-GPS navigation callers, so return RVAs and query IDs are important for separating map GPS from traffic or AI path requests. Latest GPS-query finding: - During a controlled route test, the normal world-map GPS path did not call the script-native `RunGPSQuery`/`UpdateGPSQuery` bodies. - Each deliberate quest/custom route click called the shared submitter `0x70a42c` from return RVA `0x8d20d4`. In the 2026-06-20 19:03 run, the deliberately plotted routes returned query IDs `17`, `18`, `19`, and `20` for the side job, Sinnerman, Claire's Garage, and a custom pin. - Each submitted route produced three `0x70a570` dispatches: current/player position, a nearby snapped/lane position, then the destination position. - The dense log stream immediately after Space-to-Continue is repeated `0x7094b8` result polling for startup/minimap route queries, matching the HUD route initialization delay. - `GPSQueryResultFetch 0x7094b8` returning `1` is the route-solve completion signal. The caller at return RVA `0x52069c` starts handling a completed result, then the caller at return RVA `0x520783` drains result records. The visible route record appears in the object passed as `outPath`: offset `0x30` is a packed point count (`N/N` on the first drain record, then `N`), and offset `0x28` looks like the route point array pointer. The deliberate routes produced the familiar `11`, `16`, `15`, and `14` point-like counts in the 2026-06-20 19:03 run, and `11`, `16`, `15`, `11` in the 19:16 map routine where the custom pin was different. - Static disassembly confirms `0xaa5704` takes `(manager, queryId)` and returns a small status code; one caller checks for value `1`. Runtime logging shows this is HUD/display lifecycle polling rather than solve completion: it polls the currently displayed query once per second and pauses when the minimap is not rendered. - The current installed probe adds a read-only dump of result-object fields around offsets `0x20` through `0x5f` on successful active-route fetches. The field at `outPath + 0x28` is the data pointer for an internal route element vector, but live data does not decode as plain `Vector3`/`Vector4` coordinates. Latest route-result findings: - User clock `26:38`: Continue on the main menu. - User clock `27:01`: Space to continue, with the car in a different position after a previous drive. - User clock `27:21`: map opened. - User clock `28:06`: normal route-click routine complete. - Same three quest destinations from the shifted start produced different result counts: side job `12`, Sinnerman `23`, Claire's Garage `18`, and the custom pin `17`. - Static disassembly of the `0x520783` result-drain caller shows `0x7094b8` drains one route result record at a time from an internal vector, then copies that record into `outPath`. - The copied route-result subobject contains a vector at `outPath + 0x28`. Its live count is the dword at `outPath + 0x34`; in the latest run this cleanly matched the visible routes: side job `12`, Sinnerman `23`, Claire's Garage `18`, and custom pin `17`. - The vector elements are 0x28-byte records, not pointers. `0x41be2c` calls `0x41be94` with `(data, data + count * 40)`. The active probe logs each route element as a compact 40-byte record tuple: `h00,u08hex,u0c,u10,u14,h18,h18lowFloat,h18highFloat,h20`. - A nearby helper at `0x41c508` does iterate an array of 8-byte pointers at offset `+0x28`, but that is a different route-adjacent object type, not the `0x7094b8` output record captured in `outPath`. Latest route-element decode: - User clock `45:37`: Continue on the main menu. - User clock `46:05`: Space to continue. - User clock `46:15`: map opened and the usual route-click routine was run. - The startup/minimap route and the later explicit Claire's Garage map route produced identical 18-record `ptr28` sequences, so the same solved route object feeds both HUD startup and deliberate map plotting. - The four deliberate map routes completed as route result IDs `2` through `5`: side job `12` records / final span end `68`, Sinnerman `23` records / final span end `188`, Claire's Garage `18` records / final span end `86`, custom pin `14` records / final span end `78`. - Within every route, `u10` and `u14` are monotonically increasing start/end indexes into a flattened point/span buffer. The final `u14` equals `gpsResult_u20` for every route. - `h00` is a stable per-segment handle: common route prefixes share identical `h00` sequences, and the custom pin and Claire's Garage routes share their first ten solved segments before diverging. - `u08` is packed metadata. Values appear byte-structured, for example `0x00020202`, `0x01020203`, `0x02020103`, and `0x03020003`, rather than a simple scalar weight. - `h18` splits into two 32-bit floats. The high half is consistently a sane road-scale float, often in the `20` to `230` range, and is likely segment length/cost-like output. The low half is usually zero or a tiny value, with non-zero values appearing around the start segment. - `h20` has been zero in all captured full-map GPS result records. Route-record/resource correlation: - The `h00` field in the 0x28-byte route-result records matches `worldTrafficLanePersistent.nodeRefHash`. In the current route logs, every unique route handle checked matched a lane hash in the raw `all.traffic_persistent` resource. - This lets us join emitted route segments back to resource-side lane metadata: `flags`, `maxSpeed`, `length`, and `playerGPSInfo`. - The packed `u08` metadata is not a clean copy of resource-side lane flags. Its low byte commonly appears as `2`, `3`, `4`, `5`, `8`, `9`, etc., while the resource categories are bitflag bundles such as `Road`, `Highway`, `GPSOnly`, `Intersection`, and `Pavement`. - A joined sample from `EdgeWeightGPS_cost_table_mild_2121.log` showed mixed resource categories per low-byte class: class `2` was mostly pavement but included highway lanes, class `8` was split between pavement and highway, and class `1` was mostly road. This means the final route record's low byte is useful metadata, but it is not by itself the class multiplier index from `0x44f838`. - The next better lever is still the search-side cost provider table keyed by `(*(routePoint + 0x13) & 0x3f)`. To tune it intelligently, we need either the provider initialization site or a way to join search-side route points back to `nodeRefHash`/lane flags before the edge cost is returned. Current static producer lead: - `0x70a908` packages the route endpoints and query settings, then calls `0x44cc7c`. - `0x44cc7c` is now the main producer corridor, not merely a generic unknown helper. It allocates large scratch state, projects the query endpoints, and calls deeper pathfinding routines that should decide route cost before the `0x7094b8` fetch/drain path copies final result records. - Direct xrefs confirm `0x41be94` has only the result-copy caller `0x41be69`; patching there would alter copied/displayed records after route choice. The cost patch needs to happen upstream of the `0x44cc7c` result construction path. - `0x44f054` is the strongest route-planning lead so far. It validates the packed start/target lane handles, initializes a search-state table and open list, pushes the start state, repeatedly pops the best state, checks for the destination handle, traverses adjacency lists, and writes predecessor, accumulated-cost, and priority fields for improved neighbor states. This is the native GPS graph search loop. - The search-state record is 0x20 bytes. Offsets `+0x00/+0x04/+0x08` are a world position, `+0x0c` is accumulated path cost, `+0x10` is priority (`cost + heuristic`), `+0x14` stores open/closed flags and the predecessor index, and `+0x18` stores the packed lane handle. - `0x44f7bc` is the heuristic helper. It returns world-space distance to the target scaled by the constant at `0x1431ef3a0` (`0.9990000129`), unless a route-shape helper supplies a shorter adjusted distance. - The search loop calls a strategy object's vtable slot `+0x08` as an edge admissibility predicate and slot `+0x10` as the edge-cost callback. For the base/class-filter strategies, slot `+0x10` resolves to `0x44f838`. - `0x44f838` computes the geometric edge distance and multiplies it by `provider[0x08 + classId * 4]`, where `classId` is `(*(routePoint + 0x13) & 0x3f)`. The GPS planner is therefore weighted, but by a compact baked road-class table, not by traffic `maxSpeed` or lane connection probabilities. - The active probe now hooks `0x44f054` and `0x44f838` read-only. It logs the live provider vtable, the 64 class multipliers, start/target handles, and a capped sample of edge-cost calls with point class IDs, masks, handles, and returned costs. Autodrive finding: - User clock `28:28` and `28:48`: enabling autodrive did not use the full world-map GPS caller. It submitted repeated `0x70a42c` queries from return RVA `0x8ed760`, with `query_fcc=4`, near-current vehicle positions, and result fetches from return RVA `0x8ed7df`. - These autodrive queries appear to be short local vehicle navigation requests using the same query service, separate from the full player GPS map route caller at return RVA `0x8d20d4` with `query_fcc=5`. Build and install from the Fedora toolbox: ```bash toolbox run -c 2077 ./tools/install_red4ext_shim.sh ``` This shim is read-only. It does not enable any of the archived traffic data patches.