Drawing what the map screen draws never was going to give a map. Nearly every placement in every track is one piece, cn3, and its gauge image is two 25x5 bars at x 19.5..44.5 and -44.5..-19.5 - not a wall along the route but a wall across it with a 39 unit gate in the middle. The collision solid agrees exactly. A few hundred of those is a row of ticks. The gate is the point: cn3's origin sits in the opening, so every placement marks somewhere the race passes through. Walking the gates nearest to nearest, from the end furthest out, draws the track itself - Brewer's Bane comes out as its L with the junction chambers, Zaxxis as a circuit, and the small arena as the maze it always was. Guarded, because chaining nearest neighbours across a regular grid invents a maze-like path out of nothing but visit order. Each track is tested first on how many neighbours a gate has within 1.6x the typical spacing: a corridor gives 2, a floor of obstacles gives 4 or more. The separation is not close - seventeen tracks score 1 or 2, the demolition arena scores 8 on an exact 100 unit grid and keeps its wall blocks. Most of the arcade tracks really are near-straight canyon runs, a few hundred units wide and several thousand long. The plans say so now rather than implying otherwise.
Regenerating the reference pages
docs/vtv-presets.html and docs/tracks.html are generated from
assets/RP411/RPL4.RES, the gauge config, and the console's RPConfig.xml.
Both are committed, so this is only needed when the resource file changes.
Both need a resource listing from the original tool, which is the authority for resource ids — the directory can be walked in file order, but the ids have holes (this file leaves 53 and 56 unassigned) so the walk has to be aligned against the listing:
Release\RPL4TOOL.exe -l assets\RP411\RPL4.RES > listing.txt # ends in _getch(); close it
python tools\pages\extract_presets.py vtv.json listing.txt
python tools\pages\build_page.py vtv.json docs\vtv-presets.html
python tools\pages\build_tracks.py listing.txt docs\tracks.html
RPL4TOOL finishes with _getch() (RP_L4/RPL4TOOL.cpp), so with stdout
redirected it writes the whole listing and then waits for a keypress —
close it once the file stops growing.
What each reads
| script | reads | writes |
|---|---|---|
extract_presets.py |
RES, L4GAUGE.CFG, tools/console-config/RPConfig.xml, listing |
vtv.json |
build_page.py |
vtv.json, GAUGE/s*.pcc silhouettes |
docs/vtv-presets.html |
build_tracks.py |
RES, listing, RPConfig.xml, RPL4FE.cpp catalogs, tracks.css/js/tpl |
docs/tracks.html |
build_tracks.py reads the front end's own kMaps / kFootballMaps arrays
so the page cannot claim a track is offered when the menu does not offer it.
Two formats worth knowing
Control mappings. A vehicle's ControlsMappings List holds the resource
ids of its L4 and Thrustmaster streams. Streams are named plainly, so
there is nothing in a stream saying whose it is — resolve by id, never by
which vehicle name sits nearest in the file. Records are 24 bytes and carry
their own mode mask; bits 3–8 are ModePreset1..6.
Map instances. 76-byte records: class id at +0, position at +48, unit quaternion at +60. A few records are longer, so resync on an unexpected class id rather than trusting the stride. The quaternion doubles as a checksum — a mis-read almost never produces a unit one.