Files
2077-gps-mod/docs/red4ext-logging-shim.md
T
2026-06-20 13:00:35 -05:00

8.2 KiB

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:

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.

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, then returned query IDs 3, 4, 5, and 6 for the three quest routes plus the 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 was repeated 0x7094b8 result polling for startup query IDs 0/1, matching the HUD / minimap route initialization delay. The next probe tracks submitted IDs so route-click completion can be captured without being drowned by startup polling.
  • Static disassembly confirms 0xaa5704 takes (manager, queryId) and returns a small status code; one caller checks for value 1 as the completed-query state.

Build and install from the Fedora toolbox:

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.