# Ambient traffic preview: driving and texture boundaries

This note separates the recovered retail traffic path from the WebGL preview's
current motion and records which body materials actually have source textures.

## Retail traffic movement

Retail ambient cars are driven through the AI action/controller path, not by
playing a racing spline at a fixed speed. `AIActionDriveTrafficLane` updates a
FuturePoint queue from candidate route records. The queue holds up to 20 points;
`0x0041d160` removes targets behind the actor using the actor-forward dot
product, trims the queue to a lookahead distance, and `0x0041d530` refills it
from eligible route candidates. The normal horizon is `max(actor[+0x430] *
0.5, 30)`; a global mode uses `actor[+0x430] * 0.25`. The class callback also
selects between controller modes and can query a candidate route before
copying a successful candidate into persistent route state. The target queue
is consumed by the driver/controller path; it is not itself a steering
command.

Two other traffic actions change the path in response to the world:
`AIActionObeyTrafficIntersections` updates route state around intersections,
and `AIActionAvoidAvoidableAsTraffic` queries nearby traffic candidates and
refreshes the lane lookahead after avoidance. These callbacks are documented
in [traffic-lane-action.md](traffic-lane-action.md),
[traffic-intersections.md](traffic-intersections.md), and
[traffic-car-avoidance-action.md](traffic-car-avoidance-action.md).

The viewer does not run that controller. It now uses a stateful kinematic
follower: a future route sample is handed to the follower as a target point,
the visible heading turns toward its bearing, and position advances along
that heading. Route progress increments by the accepted movement distance.
The first target uses the recovered `clamp(15 + 0.75*abs(q) + 0.04*speed,
15, 30)` formula. Since static exports omit the live FuturePoint queue, the
viewer estimates `abs(q)` as the distance from the simulated car to the first
4 m segment of its decoded static route queue. This responds to cross-track
error, but does not claim to reproduce the runtime queue's segment selection.
The viewer also uses preview speed in place of retail actor speed and samples
the recovered second point at 12 m.
The preview caps its kinematic heading slew at `2π` radians/second, borrowing
the numeric rate used by helper `0x00405990` to slew an APsychoDriver-internal
heading field. The disassembly does not establish that field as actor yaw or
that rate as a physical turn limit. Each proposed movement step must hit an
exported drivable road triangle. When the full 15–30 m target would steer a
step off the road, the preview retries with shorter points on that same route
before holding.
The route sampler measures cumulative distance in 3D, matching retail
`0x00415630`'s Euclidean FuturePoint segment-length metric. Its target
interpolation remains a static polyline through exported `0x34121` waypoint
positions; it does not recreate candidate-resolved FuturePoints or their
runtime corridor offsets.
Rendering, player collision checks, and the forward-view roster check now
read and advance the same stored driver pose/progress. Route refresh prunes
states for retired slots, and disabling traffic or loading another route set
clears those states so a reused route key cannot inherit stale motion.

The speed remains a preview constant (`7 + (routeKey % 6)` m/s), and applying
the driver's internal heading-rate helper directly to visible yaw is an
explicit approximation. Retail routes heading and speed through stateful
steering, throttle/brake/e-brake, and vehicle physics. The viewer still does
not simulate per-car route-candidate refresh, lane changes, intersection
right-of-way, or obstacle avoidance. At a route endpoint it can follow the
`transition_match_record_index` selected by retail walker `0x005d8760`, even
when the endpoint node has two linked `0x34122` records. It still requires a
reciprocal endpoint, samples within 2 m in 3D, and aligned travel tangents.
L4RA has 852 two-record nodes among 3,289 nodes; those nodes were previously
excluded from preview continuity. The matched row is a useful static candidate,
not proof of the live AI's per-car branch choice.

The reason is now explicit in the call path. `0x00420ed0` selects an available
route phase (logical selectors 2–5) from route-header bytes and a corridor
metric, then tries that cursor through `0x00420d40`; it is not a route-object
edge selector. `0x00420d40` refreshes the live FuturePoint queue through
`0x0041d530`. Queue population scans the runtime per-car candidate array,
applies selector/phase-pair and interval checks, then resolves and appends
candidate points. The static exports do not contain those candidate objects,
their per-car weights, or the successful cursor state. The current viewer
therefore uses the decoded route basis for direction within a route but does
not infer a turn at its end. Every exported route's waypoint order has
negative mean dot agreement with its decoded basis (all 855 L4RA routes;
mean `-0.923`, and all routes in L4RB/L4RC/L4RD/L4RF/L4RG as well), so the
viewer reverses point order to follow that basis. That basis establishes
forward ordering within a route, not a selected edge between routes. Cars
whose route sample reaches its end retire from the preview until another
nearby route is assigned.

The remaining gap to a faithful driving loop is runtime state: the live
FuturePoint candidate values and weights, the selected action/controller
mode, actor speed/force state, and the driver-output-to-body-physics mapping.
The static route export contains route samples and availability bytes, but not
those per-car runtime values. The primary queue-sample formula and 12-unit
second sample are decoded; the viewer uses an estimated route cross-track
`abs(q)` and preview speed. Its `2π/s` yaw cap is a kinematic presentation choice borrowed from a
different internal heading field, not a recovered physical turn bound.

## Traffic body texture coverage

The viewer's upload and draw path is already wired for authored GLB textures:
`loadTrafficBodyVariant` uploads the GLB images, each batch retains that
texture array, and the draw path looks up the material's
`pbrMetallicRoughness.baseColorTexture` index. The exporter also preserves
`TEXCOORD_0` and the source vertex color/prelight. For P8 sources the shader
applies the recovered red/blue channel correction. No missing viewer-side
texture binding was found.

The 15 exported BODY_A assets embed available shared `GLOBALB.BUN` maps:

- `CARBOTTOM` (`0xbd1f0e0a`), `WINDOW_TRAFFIC` (`0x008a6835`), and
  `TIREBACK` (`0xc8c5a8a4`) are present across the roster.
- `DRIVER` (`0x5799e60b`) is present on most models; HATCHBACK instead uses
  `TRAFFIC_DRIVERFRONT` (`0x6af267d2`) and `TRAFFIC_DRIVERSIDE`
  (`0x9e6b68ee`). BUS and FIRETRUCK have no driver map in their exported
  material lists.
- `HATCHBACK02` has two material slots referencing `CARBOTTOM`; the duplicate
  does not indicate a missing texture upload.

The body paint material is the exception. In the current traffic GLBs, the
untextured body material is named `paint_body` and carries source geometry
material hash `0x19e0ecbe`; it has no `baseColorTexture` reference. Every
per-car ambient `TEXTURES.BIN` in the installed image is zero bytes. Retail
traffic paint is a runtime color/material choice; the exact traffic paint
selection table has not been recovered. The preview preserves the available
source maps and vertex prelight and does not fabricate a paint texture. The
older export's `0x0bab0358` label is superseded by the current source-material
metadata; the retail draw-time replacement hash `0xd6d6080a` is a separate
identifier in the executable path.

## Recheck of the full exported roster

I checked all 39 packaged traffic body variants (15 definitions, with the
shorter BUS/FIRETRUCK inventories) and the stock player-car GLBs. BODY_A and
BODY_B retain the shared `CARBOTTOM`, `WINDOW_TRAFFIC`, and `TIREBACK` maps
where authored; BODY_C has fewer source slices and therefore fewer embedded
images. HATCHBACK's extra driver maps are present, and HATCHBACK02's duplicate
`CARBOTTOM` material references share the same image. The player-car GLBs also
retain their resolved per-car texture references. In `viewer.js`, both loaders
upload the GLB's own image array and attach it to each batch; `drawVehicle`
binds the material's `baseColorTexture` index. The browser capture
[`244-r153-traffic-texture-frame-check.png`](../webgl-city/review/images/244-r153-traffic-texture-frame-check.png)
shows the glass tint and front/rear source slices, while the broad gray body
panels and nearly black wheels remain visually flat.

That flatness is an asset/runtime-data gap, not an omitted WebGL texture
binding: the ambient body paint slice is untextured in every inspected variant,
and the body material color comes from the fallback. `traffic-resource-streaming.md`
records the retail paint path: runtime render-owner state `+0x384` resolves
material hash `0xd6d6080a`, and an optional descriptor selector looks up entry
key `0x6ba02c05`. The `0x34605` child contains 99 matching key/value rows;
98 values resolve to named paint or vehicle-state resources. Their raw
payloads are exported in `traffic-paint-states.json`, but the payload fields
and class-`0x3f` descriptor selector remain unknown. Reusing player-car maps
or assigning an arbitrary named state would not recover the ambient choice.
This audit does not
establish that retail glass reflection, wheel assembly, or traffic BODY_A/B/C
selection is reproduced.

A second executable trace narrows the paint handoff. Constructor `0x00638d7f`
reads an optional descriptor at parent `+0x68c`; helper `0x0061f700` reads its
selector at `+0x0a`, looks up table key `0x6ba02c05`, and returns a state handle.
Resolver `0x0048f6a0` turns that handle into the state stored at render-owner
`+0x384`. During body drawing, `0x00617494` passes that state with material
hash `0xd6d6080a` to `0x0048d860`, which can replace a matching material-run
record before rendering. This confirms a selector-to-material-state override;
it does not establish the class-2 selector values or whether the state supplies
a solid color, texture, or finish. No installed source texture for the body
paint hash was found. The XP `GLOBALB.BUN` type-2 definitions carry the same
descriptor lookup key `0x253570af` in words `+0x84c` and `+0x850` across all 15
ambient models. In the class-2 setup path, `0x0041ab55` passes argument zero
to `0x00637040`, which uses `+0x84c` to request descriptor key `0x3f` and stores
the result at live `+0x68c`. The descriptor's selector at `+0x0a` is in the
resolved class-`0x3f` resource, not in the extracted definition fields; its
value and downstream paint state remain unresolved. See the exact fields and
next extraction target in `traffic-resource-streaming.md`.

The dark tire appearance also has a source boundary. The ambient GLB assigns
the circumferential tire slices to `rubber_vehicle`, whose exporter fallback
factor is `[0.12, 0.13, 0.14, 1]` and has no texture. `TIREBACK` is a separate
alpha-mask material on only six indices per wheel mesh; its 128×128 image is
mostly opaque black or transparent black. It is not the missing rubber tread
map. The near-black visible tires therefore follow the available fallback
material and vertex prelight; binding `TIREBACK` across the rubber slices
would be unsupported. A close r153 capture shows the gray paint, brown windows,
and black tire result at the same time; no retail-backed material change is
currently available.

## Ambient action/controller boundary

The executable does connect the ambient descriptor path to its AI controller.
In `0x006065b0`, the six-entry descriptor loop checks the descriptor class at
`+4`; for class `2`, it pushes `3` as the first explicit argument and `0` as
the second to `0x00606480`. That initializer passes its first argument as the
controller mode to `0x005eba30`. The factory stores that mode at live-object
`+0x24`; mode `3` allocates the `0x7b4`-byte outer controller and calls
`0x004260b0`. This constructor stores the host actor through the base
`AICarController` constructor and branches on host `+0x24 == 3`. In the normal
global-state branch it allocates the `0x460`-byte child and calls
`0x00425ab0`, whose reflected class is `APsychoDriver`; under the alternate
global-state branch it attaches the `0x250`-byte implementation constructed
by `0x00425e40`. Thus the streamed class-2 traffic descriptor path does
instantiate the AI controller/driver family, with the precise child
implementation gated by global state. The object is then linked into the live
traffic collection and updated by `0x00609ce0`.

The executable has a concrete serializer for this action slot. The common
`AICarController` serializer at `0x00428260` delegates to `CarController`, then
serializes controller `+0x6fc` through `0x00413960` and `+0x6f8` through
`0x004270e0`. The latter dispatches serialized action kind 6 to constructor
`0x00424290`; that constructor installs the class record at `0x00785820`, whose
callback is `0x004242d0`. So the action is a registered, deserializable member
of the controller's action family.

The ambient construction call does not itself deserialize that slot. Its
mode-3 path reaches `0x004260b0`, which explicitly zeros controller `+0x6f8`
and the action-list/config pointer at `+0x6fc`. The `0x00606480` ambient
initializer then invokes factory setup (`0x005f4400` / `0x005f46a0`) and links
the actor into the live collection; there is no call to `0x00428260` in this
path. There is, however, a runtime request path: the live-object manager at
`0x00609ce0` ticks the child controller through vtable slot `+0x0c`; mode-3
`AICarController` uses `0x0042c0c0`, which calls base update `0x0042abd0`.
For a class-2 host descriptor, a gated branch in the base update requests
action kind `0x16` through controller vtable slot `+0xac` (`0x00426230`),
passing the same controller instance and its `+0x6f8` current-action slot.
This branch requires global `0x0081cb88` to be nonzero and an existing action
to pass the controller's action-state checks; it is therefore a runtime
transition request, not proof that every newly spawned car starts with this
action. It closes the missing caller edge for ambient controllers that enter
that branch.

On a standard base update, a controller with empty config reaches
`0x0042aed6`, which calls its vtable slot `+0x88`. For mode 3 that slot is
`0x0042c060`. This method first checks globals `0x0089e7d8`, `0x00890108`,
`0x0089e7a0`, `0x0086b320`, and `0x0086b328`. If both first flags are zero
and the special `(0x0089e7a0 == 0x44b && 0x0086b320 != 0 &&
0x0086b328 == 4)` case is false, it sends event 1 and returns before reading
the host descriptor. Otherwise, it sends event 3 for descriptor class 1 and
event 2 for other descriptor classes, including class 2. So the disassembly
does not prove that event 2 runs for every streamed class-2 actor; that depends
on the global state at this setup point.

When event 2 is selected, `0x0042aa20` looks up the mode-2 config and stores
it at `+0x6fc`. Even if that registry lookup misses, its fallback config has
the same `+0x3c` method. The base update then sees `+0x6f8` empty and calls
config vtable slot `+0x3c`; mode-2 config vtable `0x007847d0` maps this slot to
`0x00426ec0`. That function constructs the `0x00785820` action record
(`AIActionDriveTrafficLane`, callback `0x004242d0`) and returns it for storage
at controller `+0x6f8`. Thus the event-2 path attaches the lane action as its
initial action. The class name for config vtable `0x007847d0` remains
unresolved, and the standard update's global-state gate determines whether a
class-2 actor takes this path.

Later action transitions are a separate path. The gated kind-22 request goes
through `0x00426230`; with a mode-2 config it calls config vtable `+0x38`,
which is `0x0042d470`. That selector conditionally constructs several action
classes, including `GetUnstuck` (`0x00785784`), “Pick route to avoid
animation” (`0x007859d4`), and “I just hit a wall (in a drag race)” (`0x007858d4`).
The runtime route and action fields choose among its branches, so I cannot
identify which transition result a particular ambient actor receives from
static control flow alone. The live `FuturePoint` queue is also still unjoined
to the ambient instance.

`AIActionDriveTrafficLane` itself updates route state via `0x00420d40` or
candidate/fallback transition helper `0x00420ed0`, while the lookahead queue
is pruned/filled through `0x0041d160` and `0x0041d530`. The initial ambient
lane-action attachment is now joined to the controller update path, but the
live `FuturePoint` queue has not been recovered from ambient runtime state.
The viewer now applies the recovered queue-sample distances and heading target
to a stateful kinematic follower: each frame turns toward the lookahead
bearing, advances at the preview speed, constrains each step to exported road
triangles, and increments route progress by accepted travel distance. The
decoded route samples still provide its targets, and speed plus the steering
and force response remain approximations. This is not the retail controller
and vehicle-physics loop.

## Evidence and validation

- Route orientation was calculated from `route_point_vector` (or
  `route_basis_xy` fallback) against each adjacent waypoint displacement in
  `freeroam-traffic-routes.json`; all listed route records had negative mean
  agreement before the viewer's point-order reversal.
- Retail lookahead details are in `traffic-lane-action.md`; endpoint-link
  limits are in `traffic-transition-pseudocode.md`.
- Material/image and GLB attribute inspection confirmed the texture index and
  UV path for the exported traffic assets. A recursive scan of 67 installed
  `.BUN` archives and car texture packs found no raw/key match for
  `0x0bab0358`; the existing extraction inventory records empty local traffic
  `TEXTURES.BIN` files. No matching source was found among the installed
  archives or alternate car variants, so this remains a runtime paint/material
  choice rather than a recoverable texture resource.
- The full-variant material audit confirmed the same missing ambient paint
  texture on BODY_A/B/C and source image references on the player-car GLBs.
- The runtime paint selector resolves a state handle from optional parent
  descriptor `+0x68c` and overrides material run `0xd6d6080a`; ambient selector
  values and the state's color/texture semantics remain unknown. The tire's
  main rubber slices have no source texture; `TIREBACK` is a separate masked
  back-face map.
- `traffic-resource-streaming.md` documents ambient object creation and frame
  updates; `traffic-lane-action.md` documents the AI route action and driver
  handoff. The ambient type-2 descriptor to mode-3 `AICarController` path,
  its class-2 initial `AIActionDriveTrafficLane` attachment through config
  vtable `+0x3c`, its conditional later kind-22 request through `0x00426230`,
  and the controller serializer's kind-6 mapping to `AIActionDriveTrafficLane`
  are confirmed here. The configured transition selector's selected class and
  runtime FuturePoint state remain open. `0x00426230` is the derived controller's vtable slot
  `+0xac` (`0x00785370`); the generic virtual-dispatch sites found at
  `0x0065b9a3` and `0x006dc0d7` call slot `+0x16c` instead and are unrelated
  to this action request.

## Traffic roster: route samples are not car instances

The preview previously duplicated route candidates with increasing `slot`
values when fewer distinct routes were visible than the requested traffic
count. This manufactured same-route packs and was a direct cause of the convoy
appearance. The viewer now assigns at most one preview actor to each selected
route and leaves the roster smaller when there are not enough distinct paths.

Model selection also keeps each ambient definition to at most one visible
actor at a time. This follows the manager structure: each definition has one
manager slot, candidate assignment increments that slot's matched-candidate
count, and `0x00412db0` creates one stream handle for each positively counted
slot. Quota values are candidate assignment targets, not permission to spawn
multiple handles for one definition. The retail manager chooses uniformly
among slots still under their quota; quota bytes control eligibility, not the
selection weight. The preview now makes an even deterministic choice among
unused definitions with positive quota in the player's density column. Static
route rows omit live candidate-to-definition counts and the retail PRNG state,
so this does not reproduce the manager's changing under-quota pool or its
per-slot refresh and retirement timing.

This is a conservative presentation rule, not a recovered retail limit. The
ambient manager at `0x00412be0` assigns eligible class-2 candidates to vehicle
definition slots and the streaming path creates live actors from those slots;
the normal controller path then gives each actor its own mode-3
`AICarController` / driver and may enter `AIActionDriveTrafficLane`
(`0x004242d0`). The executable therefore has per-actor runtime state that the
viewer does not export. The static route/node files do not identify which
candidate actor occupies which route or which lane/intersection/avoidance
action is active. Until that join is recovered, repeating one route to meet a
UI count has no source basis. Intersection and traffic-avoidance actions are
documented separately; their candidate and phase state remains unresolved.

## Route-source identity and the remaining AI runtime dependency

The viewer's traffic geometry comes from the retail `RoutesFreeRoam*.bin`
files, specifically chunk `0x34121`; it does not follow the event-race line
network. The loader at `0x005deb80` dispatches that chunk to parser
`0x005de1c0`, which registers route objects by ID and reads ordered 56-byte
waypoints. The traffic action then consumes route/FuturePoint candidates from
that route table. The separate event `Paths*.bin` race-line network is chunk
`0x34148`, with 24-byte points and `0x34149` route records. None of the eight
installed `PathsFreeRoam*.bin` files contains a `0x34148` chunk. Their
`0x3414D` records are barrier segments, identified by the retail `BARRIERS_*`
resource names and the executable's line-segment collision path; they are not
traffic lanes. See the [route-source index](README.md#free-roam-routes-file)
and its [race-line distinction](README.md#world-bundle-topology).

There is no static authored traffic-lane graph that can replace the current
route-fragment preview faithfully. The static `0x34122` records and
`0x005d8760` walker establish endpoint-compatible route connections, but the
walker is not the car's next-edge selector. Each actor's lane action fills a
bounded FuturePoint queue at `0x0041d530` from its live candidate-pointer
array, applying that actor's phase, subtype, stored weights, and interval
checks. `0x00416120` samples that queue for the driver target and turn
response. The 38 `0x34123` intersection anchors exist only in L4RA; their
candidate banks are empty in the authored records and are populated at
runtime. The 1,036 type-16 polygons select density quotas, not traffic paths.
Therefore the WebGL follower correctly starts from retail free-roam route
samples, but its phase, speed, turn response, lane side, and fragment-end
behavior are still a preview. It is not retail-equivalent. No static endpoint
match should be turned into an unconditional route join.

### Runtime capture needed to close the gap

The next useful artifact is one short live snapshot series from the installed
XP game while ordinary free-roam traffic is moving through a junction. The
existing XP QEMU GDB stub can be used from the host after the game is launched
in the guest. Connect an i386-capable GDB to that stub, set breakpoints at
`0x004242d0` (registered `AIActionDriveTrafficLane` callback), `0x0041d530`
(FuturePoint queue population), `0x00416120` (queue-to-driver target update),
and `0x00408820` (`TrafficMover` update). At each stop, save the register file,
stack words, and the pointed-to blocks below; resume for several frames so the
same actor can be matched across queue refreshes. These are fixed executable
VAs in the installed non-PIE image:

```gdb
set architecture i386
set pagination off
target remote 127.0.0.1:1234
break *0x004242d0
break *0x0041d530
break *0x00416120
break *0x00408820
continue
info registers
x/32wx $esp
```

Replace port `1234` with the QEMU GDB-stub port configured for the running XP
VM. At each stop, record the PC, timer, registers, and stack dump first. Follow
the callback's actual entry argument to the context and verify the actor
pointer; do not assume the context is in one fixed register across these
different x86 calling conventions. Then dump the following fields from the
verified pointers:

1. At `0x004242d0`, identify its runtime context from the callback entry and
   follow the first context field to the actor. Save actor position/heading
   (`+0x60..+0x68`, `+0x40c`), route state (`+0x2c`, including route mode and
   byte `+0x10`), and phase at callback context `+0x30c`.
2. Save the actor controller's route queue/candidate block at `+0x31c` and
   phase record selected by context `+0x308`: the signed queue count, candidate
   count, all candidate pointers, candidate vtables, route/waypoint refs
   (`+0x5c/+0x5e`), scalar weights (`+0x54/+0x58`), phase arrays, and the
   queue's XYZ entries at `+0x10` with route refs/flags. Record the selected
   cursor before and after `0x0041d530`.
3. At `0x00416120`, save the live queue, actor speed (`+0x430`) and motion
   vector (`+0x70/+0x74`), driver target (`+0x224/+0x228`), driver state
   (`+0x1e4`, `+0x22c`, `+0x230`, `+0x238`, `+0x23c`), and command slots
   (`+0x20c/+0x210/+0x214`). This reveals the actual lookahead and turn
   response for that same traffic actor.
4. At `0x00408820`, save the mover object fields `+0x0c..+0x18`, its owner
   pointer at `+0x08`, owner steering/command inputs, and the post-update actor
   position/heading/speed. This ties driver outputs to actual movement.

Keep the actor pointer, timer, route/waypoint refs, and phase/cursor beside each
snapshot. That lets the route export resolve every live candidate back to its
authored `0x34121` waypoint and lets the browser reproduce observed queue
selection and motion, rather than inventing graph joins. An executable dump
with these values is the concrete missing input; a screenshot alone cannot
recover it.

## Installed texture-key recheck, 2026-10-16

I re-read all 40 packaged traffic BODY_A/B/C GLBs and compared their source
texture keys with the installed `GLOBALB.BUN` TPK and other extracted shared
track bundles. The verified shared keys still present in `GLOBALB.BUN` are
`CARBOTTOM` (`0xbd1f0e0a`), `WINDOW_TRAFFIC` (`0x008a6835`), `TIREBACK`
(`0xc8c5a8a4`), `DRIVER` (`0x5799e60b`), and the hatchback driver maps
`TRAFFIC_DRIVERFRONT` (`0x6af267d2`) and `TRAFFIC_DRIVERSIDE`
(`0x9e6b68ee`). The package has `CARBOTTOM` and `TIREBACK` in all 40 variants;
`WINDOW_TRAFFIC` is embedded in 27 variants, where authored. Those maps are
already resolved and assigned by the exporter; no texture restoration was
needed. `TIREBACK` remains its own narrow mask material, not a map for the
rubber surface.

The exact still-untextured material slots in the 40 GLBs are:

- `paint_body`, source material hash `0x19e0ecbe`: 40/40 variants, no
  `baseColorTexture` and no source texture key (`texture_key=0` in the
  exported source slice). Per-car ambient `TEXTURES.BIN` files are empty.
- `rubber_vehicle`, source material hash `0x78743ca1`: 40/40 variants, no
  `baseColorTexture` and no source texture key. The available `TIREBACK` key
  belongs to a different material slice, so it cannot be assigned to this
  slot from the recovered data.
- `glass_vehicle`, source material hash `0x1ff8c329`: 11 BODY_C variants, no
  `baseColorTexture` and no source texture key. The other authored window
  slices use the resolved `WINDOW_TRAFFIC` key; the BODY_C slot has none.
- Auxiliary untextured body slices: hash `0x12c9453c` on 11 variants and hash
  `0x0fedee40` on the FIRETRUCK BODY_A variant. These also have no source
  texture keys, and their material payloads remain unresolved.

Because these slices carry no source texture key, the installed catalogs
cannot be joined to them by exact key. No additional map can be restored from
the current exports; no body color, tire texture, or glass opacity was
inferred or changed.
