The page listed 34 of the 38 vehicles in the resource file. The four missing were community variants, and I had written down that they shared another vehicle's mapping streams. They do not - they have their own, of 17 to 33 records. The decoder just could not find them. Streams are stored as resources named plainly L4 and Thrustmaster, so nothing in a stream says whose it is. The old decoder guessed by taking the nearest preceding vehicle name in the file, which works while vehicles are laid out one after another and fails quietly when they are not: it lost vole outright and mis-attributed four blkr variants. The file answers exactly if asked properly. A vehicle's ControlsMappings List holds the resource ids of its two streams. Ids are not quite positional - this file leaves 53 and 56 unassigned - so the directory walk is aligned against RPL4TOOL -l, skipping the ids the listing marks Not Used. That gives 1077 ids with zero size mismatches, and every stream lands on its owner. Subsystem names now come from each vehicle's own Stream of N Subsystems, checked against the count in its header instead of being pattern-matched out of the bytes. Nothing already verified moved: lepton, dark, blkspk and neut decode exactly as they did when checked against the 4.10 retail file, blkspk still putting its third booster on the thumb-high in preset 4 and dark still spending preset 5's HORN slot on its second demo pack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
401 lines
21 KiB
Markdown
401 lines
21 KiB
Markdown
# Red Planet — the six control presets on the map screen
|
||
|
||
The six amber buttons down the right flank of the map display, labelled
|
||
PRESET 1 … PRESET 6 on the glass itself. They are not a display option and
|
||
they do not touch the map: **each one is a complete factory layout for the
|
||
four mappable joystick buttons**, authored per vehicle and shipped in
|
||
`RPL4.RES`. Pressing one swaps the whole stick over, live, mid-race.
|
||
|
||
```
|
||
map glass, right flank (vRIO 0x18..0x1D, amber)
|
||
┌──────────────┐
|
||
│ PRESET 1 ● │ Secondary7 press
|
||
│ PRESET 2 ○ │ Secondary8 │
|
||
│ PRESET 3 ○ │ Secondary9 ▼
|
||
│ PRESET 4 ○ │ Secondary10 VTVRIOMapper::SelectPresetMessageHandler
|
||
│ PRESET 5 ○ │ Secondary11 │ (keyboard 1-6 joins here)
|
||
│ PRESET 6 ○ │ Secondary12 ▼
|
||
└──────────────┘ L4VTVControlsMapper::PresetEnable(n)
|
||
├─ remove old ModePresetN from the
|
||
│ mode manager, add the new one
|
||
│ │
|
||
│ ▼
|
||
│ every control mapping whose modeMask
|
||
│ carries that bit goes live; the other
|
||
│ five presets' mappings go dead
|
||
│
|
||
└─ NotifyOfPresetChange(old, new)
|
||
▼
|
||
lamp old→dim, new→on
|
||
```
|
||
|
||
## 1. What the player sees
|
||
|
||
The map is the pod's portrait-mounted secondary monitor. Its gauge group
|
||
paints the button legends itself — `tags` in
|
||
`assets/RP411/GAUGE/L4GAUGE.CFG:1611`, six 16×102 strips at x=464:
|
||
|
||
| legend | file | cell |
|
||
|---|---|---|
|
||
| PRESET 1 … PRESET 6 | `butpres1.pcc` … `butpres6.pcc` | one per legend cell down the right edge |
|
||
| ZOOM + / ZOOM − / reticle / HORN | `butzmin`, `butzmout`, `butcross`, `buthorn` | left edge, top four cells |
|
||
|
||
The legend grid is not `height/6`: the first cell starts 13 rows down and
|
||
the six cells are 102 tall on a 105 pitch (13 + 6×102 + 5×3 = 640). vRIO's
|
||
`MFDSplitView::LayoutButtons` scales exactly that grid so each on-screen
|
||
button lands against its own painted label
|
||
(`MUNGA_L4/L4MFDVIEW.cpp:683`, constants at `:103`).
|
||
|
||
Addresses run **down** each column from the anchor — the map view is
|
||
created with `SideColumns, 0x10, 0x18` (`MUNGA_L4/L4VB16.cpp:4450`), so the
|
||
left column is Secondary1…6 top-to-bottom and the right column is
|
||
Secondary7…12, i.e. **PRESET 1 is the top button, PRESET 6 the bottom**.
|
||
That ordering is confirmed by the left column, where the code binds
|
||
Secondary1→zoom in, 2→zoom out, 3→reticle, 4→horn
|
||
(`RP_L4/RPL4MPPR.cpp:1857`) and the artwork paints ZOOM+/ZOOM−/cross/HORN
|
||
in exactly that order, leaving the bottom two cells blank — the pod wires
|
||
only 6 of each column's 8 addresses; 0x16/0x17 and 0x1E/0x1F are Tesla
|
||
relays (`MUNGA_L4/L4CTRL.cpp:1901`).
|
||
|
||
Preset 1 is lit at construction, the other five dim
|
||
(`RP_L4/RPL4MPPR.cpp:1962`). These six lamps are driven explicitly by
|
||
`NotifyOfPresetChange`; unlike most panel lamps they are not linked to
|
||
their button's mapping state (`SetAutomaticOperation(False)` in
|
||
`CreateControlledLamp`, `RP_L4/RPL4MPPR.cpp:47`).
|
||
|
||
## 2. The mechanism — a preset is a mode-mask bit
|
||
|
||
The engine gates every control mapping behind a `ModeMask`. RP adds six
|
||
bits purely for this (`RP_L4/RPL4MODE.h:30`, `nextModeBit` = 0 so the
|
||
values are literal):
|
||
|
||
| mode | bit | value |
|
||
|---|---|---|
|
||
| ModeNonConfig / ConfigReady / ConfigOn | 0–2 | 0x001 / 0x002 / 0x004 |
|
||
| **ModePreset1 … ModePreset6** | **3–8** | **0x008 … 0x100** |
|
||
| ModeBasic / ModeStandard / ModeIntercom | 9–11 | 0x200 / 0x400 / 0x800 |
|
||
|
||
`PresetEnable` (`RP_L4/RPL4MPPR.cpp:685`) does one thing: removes the
|
||
outgoing preset's bit from the application mode manager and adds the
|
||
incoming one. `ControlsUpdateManager::Update` then only dispatches
|
||
instances whose `modeMask` intersects the live mask
|
||
(`MUNGA/CONTROLS.h:599` shows the same test in `GetMapState`), so five
|
||
sixths of the authored stick mappings are simply invisible at any moment.
|
||
|
||
Consequences that fall out of that design:
|
||
|
||
- Presets cost nothing to switch — no rebuild, no allocation, one mask word.
|
||
- They are exactly six because there are six switches: `presetCount = 6 //
|
||
limited to number of switches!` (`RP_L4/RPL4MPPR.h:119`).
|
||
- Nothing *but* control mappings is preset-scoped. No gauge, bitmap or
|
||
strip in `L4GAUGE.CFG` references `ModePreset*` — the mode names exist
|
||
in the lookup table (`RP_L4/RPL4MODE.cpp:23`) only so the resource
|
||
compiler can resolve them in the control-mapping source.
|
||
- Entering configuration state drops all six (`ModeAllNonConfig`) and
|
||
restores the previously selected one on exit
|
||
(`RP_L4/RPL4MPPR.cpp:483`).
|
||
|
||
Keyboard `1`–`6` calls `PresetEnable` directly
|
||
(`RP_L4/RPL4MPPR.cpp:1048`), the same entry point the flank switches use.
|
||
Both paths therefore move the lamps, because `PresetEnable` announces the
|
||
change itself through the virtual `NotifyOfPresetChange` (`:733`) rather
|
||
than leaving it to the switch handler — see §7.
|
||
|
||
### One mask, two consumers
|
||
|
||
The live mask is a single 32-bit word on the `ModeManager`
|
||
(`MUNGA/MODE.h:65`), seeded once with `ModeInitial` — `ModeNonConfig |
|
||
ModeBasic`, **0x201** — at `RP_L4/RPL4APP.cpp:539`, and changed only by
|
||
`AddModeMask` / `RemoveModeMask` / `ReplaceModeMask` (`MUNGA/MODE.h:26`).
|
||
Two subsystems read it, and both gate the same way — a bitwise AND, never
|
||
an equality, so any shared bit passes and `ModeAlwaysActive` (`-1L`, all
|
||
bits) always matches:
|
||
|
||
| consumer | object | gate |
|
||
|---|---|---|
|
||
| controls | `ControlsInstance::modeMask` | `MUNGA/CONTROLS.cpp:132` in `ControlsMappingGroup::Update` — decides whether a mapping dispatches at all |
|
||
| gauges | `GaugeBase::modeMask` | `MUNGA/GAUGREND.cpp:3625`, `:3782`, `:3852` — decides whether a drawable sits in the active list |
|
||
|
||
The gauge half runs in `GaugeRenderer::ExecuteForeground`
|
||
(`MUNGA/GAUGREND.cpp:3581`), which reads the mask once per pass, derives
|
||
`change_mode_mask = current ^ previous` (`:3597`), files newly created
|
||
gauges into the active or inactive chain (`:3625`), and — only when the
|
||
mask actually moved (`:3661`) — shuffles objects between them
|
||
(`ActivateGaugeBases` `:3782`, `DeactivateGaugeBases` `:3852`). Only the
|
||
active chain is executed, so an unmatched drawable costs one list entry
|
||
and nothing else. The lamp manager is handed the same word a few lines
|
||
earlier (`:3604`), which is how the preset and mode lamps are filtered.
|
||
|
||
A drawable whose bit is never set therefore never draws, and never
|
||
complains — it is simply parked in `inactiveList` for the life of the
|
||
process. That is exactly what happened to the intercom art (§3): its
|
||
gauges are authored, resolved and loaded, and then sit in the inactive
|
||
chain forever because nothing ever ORs `ModeIntercom` into the mask.
|
||
|
||
## 3. What is actually in a preset
|
||
|
||
Only the four mappable stick buttons are ever preset-scoped —
|
||
`FirstMappableButton`…`LastMappableButton` minus the hat
|
||
(`MUNGA_L4/L4CTRL.h:497`):
|
||
|
||
| element | button |
|
||
|---|---|
|
||
| 0x40 | trigger |
|
||
| 0x45 | pinky |
|
||
| 0x46 | thumb low |
|
||
| 0x47 | thumb high (by the hat) |
|
||
|
||
Everything else — throttle, pedals, stick axes, hat, the whole aux panel —
|
||
is bound `ModeAlwaysActive` or `ModeNonConfig` and is identical in all six
|
||
presets. The functions that can land on a stick button are the ones that
|
||
also have a panel button and a "configure" partner: booster fire, chute,
|
||
weapon trigger, LIFT CUT, SIDESLIP, HORN, and the intercom PTT — the last
|
||
of which never works on a shipped pod, see below
|
||
(`RP/VTVMPPR.cpp:144`, `RP/BOOSTER.cpp:50`, `RP/CHUTE.cpp:48`,
|
||
`RP/WEAPSYS.cpp:103`).
|
||
|
||
### The intercom PTT binding is dead on shipped hardware
|
||
|
||
The intercom press-to-talk needed a headset and a comms panel that never
|
||
went past prototype cockpits. `ActivatePTTMessageID` is still bound in the
|
||
authored data and still handled in the code, but no production pod can
|
||
trigger it, so **the buttons it occupies are dead, and are reported as
|
||
unbound in the tables here and on the roster page**. Anyone re-decoding
|
||
`RPL4.RES` will find message ID 13 sitting on the pinky and wonder why the
|
||
tables show it empty, hence this section.
|
||
|
||
PTT occupies 26 preset rows across 13 vehicles — always the pinky, always
|
||
presets 1 and 3, and only on lightly armed or unarmed vehicles, where the
|
||
pinky was free. Two independent things in the shipped assets confirm the
|
||
hardware never arrived:
|
||
|
||
1. **Its panel label was never drawn.** The tool panel (`auxUL2`) has four
|
||
button-pair quadrants and `common_setup` fills three of them — LIFT CUT
|
||
at (22,240), SIDE SLIP at (327,240), HORN at (327,54)
|
||
(`assets/RP411/GAUGE/L4GAUGE.CFG:767`). The fourth, (22,54), is blank —
|
||
and that is exactly the `AuxUpperLeft5/6` pair the PTT is bound to
|
||
(`RP_L4/RPL4MPPR.cpp:2000`, between the LIFT CUT/SIDESLIP pairs at
|
||
`:1978` and the HORN pair at `:2011`). Every other configurable system
|
||
has a legend on the glass; the PTT has bare panel.
|
||
2. **Its display art never reaches the glass.** `GAUGE/RPAICOM.PCX` and
|
||
`GAUGE/RPACOMM.PCX` are finished 640×480 intercom station screens that
|
||
the gauge config never references at all. The edge strips
|
||
`strpcom1/2.pcc` *are* declared — top and bottom of the centre aux
|
||
display, `port = auxC` (`assets/RP411/GAUGE/L4GAUGE.CFG:706`) — but
|
||
gated on `ModeIntercom`, and **nothing ever sets that bit**: there is no
|
||
`AddModeMask(ModeIntercom)` anywhere in the tree, and the config emits
|
||
no `enable` command for the data-driven path
|
||
(`MUNGA/GAUGREND.cpp:1791`) to pick up either. The one block that would
|
||
have wired the intercom buttons sits inside `#if 0`
|
||
(`MUNGA_L4/L4ICOM.cpp:903`) and still names `L4ModeManager`, a class
|
||
that no longer exists — so it was cut before the `RPL4ModeManager`
|
||
rename and never revisited. None of this art has ever been on screen.
|
||
|
||
Dropping it costs nothing on 12 of the 13 vehicles — presets 1 and 3 stay
|
||
distinct because their thumb-low binding differs. The exception is the
|
||
**quark**, whose presets 3 and 4 become identical, since the PTT was the
|
||
only thing separating them. Four other vehicles ship preset pairs that were
|
||
already identical before any of this: dragon, roach and spitter (3 ≡ 4) and
|
||
puck (2 ≡ 5).
|
||
|
||
**The roster page never mentions the PTT at all.** `docs/vtv-presets.html`
|
||
is the player-facing artifact; naming a control that cannot be pressed
|
||
would raise more questions than it answers, so those cells are simply
|
||
blank there.
|
||
|
||
## 4. The shipped tables
|
||
|
||
The mappings are streamed from the vehicle resource at mission start —
|
||
`LBE4ControlsManager::CreateStreamedMappings`
|
||
(`MUNGA_L4/L4CTRL.cpp:2153`), fed from the entity's ControlMappings list
|
||
resource by name: `"L4"` for the pod RIO, `"Thrustmaster"` for the stick
|
||
(`RP_L4/RPL4APP.cpp:761`). Each record is a `ControlsMapping`
|
||
(`MUNGA/CONTROLS.h:783`) carrying its own mode mask, so a single button can
|
||
appear six times with six different targets.
|
||
|
||
**Every VTV in `assets/RP411/RPL4.RES` ships a full six-preset table, for
|
||
both the RIO and the Thrustmaster** — 38 of them as of the resource file
|
||
promoted from the airlock archive (see §8). Decoded, the *lepton* (two
|
||
boosters, one chute, unarmed) reads:
|
||
|
||
| | trigger | thumb high | thumb low | pinky |
|
||
|---|---|---|---|---|
|
||
| PRESET 1 | booster 1 | booster 2 | chute | — |
|
||
| PRESET 2 | booster 1 | booster 2 | chute | **LIFT CUT** |
|
||
| PRESET 3 | booster 1 | booster 2 | **LIFT CUT** | — |
|
||
| PRESET 4 | booster 1 | booster 2 | LIFT CUT | chute |
|
||
| PRESET 5 | booster 1 | booster 2 | **HORN** | LIFT CUT |
|
||
| PRESET 6 | **LIFT CUT** | booster 1 | booster 2 | chute |
|
||
|
||
An armed vehicle substitutes the weapon trigger for the primary booster.
|
||
The *puck* (one booster, laser):
|
||
|
||
| | trigger | thumb high | thumb low | pinky |
|
||
|---|---|---|---|---|
|
||
| PRESET 1 | laser | booster | HORN | — |
|
||
| PRESET 2 | laser | booster | HORN | LIFT CUT |
|
||
| PRESET 3 | laser | booster | LIFT CUT | — |
|
||
| PRESET 4 | laser | booster | LIFT CUT | HORN |
|
||
| PRESET 5 | laser | booster | HORN | LIFT CUT |
|
||
| PRESET 6 | **LIFT CUT** | booster | — | laser |
|
||
|
||
The authored intent is consistent across every vehicle in the file:
|
||
|
||
1. **Presets 1–5 keep the primary weapon (or lead booster) on the
|
||
trigger** and shuffle the *secondary* duties — where LIFT CUT lives,
|
||
whether the stick carries HORN, and whether the chute/third system
|
||
stays on the stick at all.
|
||
2. **Preset 2 always puts LIFT CUT on the pinky** and **preset 6 always
|
||
puts LIFT CUT on the trigger** — verified true for all 26 tables.
|
||
Preset 6 is the outlier layout: everything shifts up a finger and the
|
||
trigger becomes a handling control rather than a fire control.
|
||
3. Vehicles carrying fewer systems leave cells empty, and some presets then
|
||
collapse into duplicates — on the puck, presets 2 and 5 are identical.
|
||
4. Heavily armed vehicles spend the freed cells on second and third
|
||
weapons instead of HORN (bttlbrg, gator, roadblk).
|
||
|
||
## 5. Editing the active preset in flight
|
||
|
||
The presets are also the storage for in-mission rebinding. With the CFG
|
||
button on the upper-right MFD (`AuxUpperRight1/2`, registered under
|
||
`ModeStandard` — `RP_L4/RPL4MPPR.cpp:1826`), the pod enters configuration
|
||
state: the aux strips swap their legends from tool art to SET art
|
||
(`strptol1/2.pcc` → `strpset1/2.pcc`, `L4GAUGE.CFG:715`), and every
|
||
mappable button gets a temporary mapping to the chosen system's "choose"
|
||
message (`RP_L4/RPL4MPPR.cpp:604`). Pressing a panel button arms a
|
||
function; pressing a stick button toggles it via `AddOrErase`, which
|
||
writes against **`previousPresetModeMask`** — the currently selected
|
||
preset, and only that one (`RP_L4/RPL4MPPR.cpp:1538`).
|
||
|
||
The `configMap` gauge on that display (`RPL4GAUG.cpp:3199`, placed at
|
||
`L4GAUGE.CFG:749`) shows the state of all four stick buttons against the
|
||
armed function, one icon each, using the same preset mask:
|
||
`cfgNone` / `cfgOther` / `cfgMe` / `cfgBoth` for
|
||
`unmapped` / `mappedByOthers` / `mappedByMe` / both
|
||
(`MUNGA/CONTROLS.h:315`).
|
||
|
||
Configuration is unavailable in Basic control mode — the CFG button and the
|
||
`configMap` gauge are both `ModeStandard`, which is set by Standard,
|
||
Veteran and Master but cleared by Basic (`RP_L4/RPL4MPPR.cpp:374`).
|
||
|
||
**Nothing is persisted.** Edits live in the `ControlsMappingGroup` chains
|
||
for the mission only; no code writes mappings back to disk, and the next
|
||
mission re-streams the authored table from `RPL4.RES`. (Porting a real
|
||
bindings file is still open — see the roadmap's Workstream A.)
|
||
|
||
## 6. The preset number goes out on the wire
|
||
|
||
`PresetEnable` sets `mustMatch = preset_number` unconditionally, before the
|
||
early-out, tagged `HACK - for backward watcher compatibility`
|
||
(`RP_L4/RPL4MPPR.cpp:700`). `mustMatch` is a replicated attribute of the
|
||
mapper (`RP/VTVMPPR.cpp:384`), so a watcher/spectator station sees which
|
||
preset a pilot is on. It is the only preset state that leaves the machine.
|
||
|
||
## 7. Defects found while reading
|
||
|
||
**Fixed (2026-08-06):** both lamp defects below.
|
||
|
||
1. **The preset loop wrote past `modeLamp`.** `modeLampCount` is 4, but the
|
||
preset pass stored six lamps into `modeLamp[i]`, i = 0…5. The members
|
||
are declared `configLamp[2], modeLamp[4], presetLamp[6]` in that order
|
||
(`RP_L4/RPL4MPPR.h:312`), so indices 4 and 5 landed in
|
||
`presetLamp[0..1]` and the whole thing was self-consistent by memory
|
||
layout — the preset press handler read the same out-of-range slots.
|
||
Two real consequences: an out-of-bounds write, and it **destroyed the
|
||
four control-mode lamps** created immediately above (`:1921`), so
|
||
`NotifyOfControlModeChange` drove PRESET 1–4's lamps on the map flank
|
||
and the Basic/Standard/Veteran/Master lamps on the upper-right MFD were
|
||
never lit at all. `presetLamp[]` was meanwhile never populated or read.
|
||
The preset pass now fills `presetLamp[]` (`:1960`), which is what the
|
||
array was always for.
|
||
2. **Keyboard preset switching desynchronized the lamps.** The lamp work
|
||
lived in `SelectPresetMessageHandler`, so only the flank switches moved
|
||
the lamps and keyboard `1`–`6` left the wrong one lit. It now lives in
|
||
the virtual `NotifyOfPresetChange`, announced by `PresetEnable` itself
|
||
(`:733`) — one place, every path. `VTVRIOMapper` overrides it to move
|
||
the six flank lamps (`:1651`); the base mapper and the Thrustmaster
|
||
mapper have no preset lamps and inherit the no-op (`:746`).
|
||
|
||
Verified by reading the commanded RIO lamp states out of the running
|
||
game (`PadRIO::lampState`, PadRIO + `TEST.EGG`, at rest in Basic mode).
|
||
Before and after are byte-identical except for lamp 0x33:
|
||
|
||
| lamps | before | after |
|
||
|---|---|---|
|
||
| 0x18 PRESET 1 / 0x19–0x1D PRESET 2–6 | 3c / 14 | 3c / 14 |
|
||
| 0x30–0x32 MASTER/VETERAN/STANDARD | 14 | 14 |
|
||
| **0x33 BASIC** | **14 (dim — never lit)** | **3c (lit)** |
|
||
|
||
Still open, by design rather than by accident: `PresetEnable` early-outs on
|
||
`preset_number == previousPresetNumber`, so re-pressing the lit switch is a
|
||
no-op. Correct for switching, but it also means a preset can never be used
|
||
as a "reset to authored bindings" after the player has edited it in
|
||
configuration mode.
|
||
|
||
## 8. The promoted resource file, and the vehicles that came back
|
||
|
||
`assets/RP411/RPL4.RES` is no longer the file RP412 inherited. It is the
|
||
1.25 MB resource from `assets/airlock_RP411/`, a 2014 community build, which
|
||
verification showed to be a strict superset of ours:
|
||
|
||
* nothing is lost — the only resources ours has and it does not are two
|
||
unnamed "Not Used" placeholders;
|
||
* same format version (`v1.3.0.2`);
|
||
* all 26 base vehicles' `L4` and `Thrustmaster` mapping streams are
|
||
**byte-identical** to the ones we shipped;
|
||
* `vole` matches resource-for-resource, id for id and size for size;
|
||
* it boots and runs against our own `GAUGE`/`VIDEO`/`AUDIO`, with a log
|
||
identical to the baseline.
|
||
|
||
Its `GAUGE/L4GAUGE.CFG` was promoted with it. That file is ours plus the new
|
||
vehicles' blocks, plus one fix: `dragonInit` gains `twoBoosterInit`, so the
|
||
dragon's two boosters finally have gauges — it always had them in its
|
||
subsystem list and the panel never drew them.
|
||
|
||
**What the roster gained.** The three vehicles cut after 4.10 are back —
|
||
`dark` (*Blacker Puck*), `blkspk` (*Black Speck*) and `blktrn` (*Black
|
||
Tarantula*) — with the same tables they shipped with: `blkspk`'s preset 4
|
||
still puts its third booster on the thumb-high, `dark`'s preset 5 still
|
||
spends the HORN slot on its second demo pack. Beyond them the archive adds
|
||
`neut` (*Neutrino*, four boosters and an `Eject` subsystem) and eight
|
||
community "Blacker" variants, plus seven maps.
|
||
|
||
**The `black` mystery was a renaming bug.** RP411's gauge config carried a
|
||
`blackInit` block that nothing could ever select, because `GetGameModel()`
|
||
returns a model name and the lookup is `<model>Init`. The airlock config
|
||
calls the same block — byte-identical body — `blktrnInit`, which is the
|
||
model's real name. There was never a vehicle called "black"; the block had
|
||
simply been renamed out of reach. `disk` remains the one genuine orphan: a
|
||
panel layout with no vehicle behind it in any resource file, and no entry in
|
||
the console's own config either.
|
||
|
||
### Reading the file by id rather than by proximity
|
||
|
||
Every vehicle in the file has its own mapping streams — 38 of them, all on
|
||
the roster page. An earlier pass claimed four of the community variants
|
||
shared another vehicle's streams; that was wrong, and worth recording
|
||
because of *why*.
|
||
|
||
Streams are stored as resources named plainly `L4` and `Thrustmaster`, so
|
||
nothing in the stream itself says which vehicle it belongs to. The first
|
||
decoder guessed: it took the nearest preceding vehicle name in the file.
|
||
That works while vehicles are laid out one after another, and quietly fails
|
||
when they are not — it lost `vole` outright and mis-attributed four of the
|
||
`blkr` variants, whose streams simply do not sit where the guess expected.
|
||
|
||
The file answers the question exactly, if asked properly. A vehicle's
|
||
`ControlsMappings List of 2 elements` holds the **resource ids** of its two
|
||
streams. Resolving an id needs the id table, and ids are not quite
|
||
positional — this file leaves 53 and 56 unassigned, and the listing marks
|
||
them `Not Used`, so a plain walk of the directory drifts by two from there
|
||
on. Aligning the walk against `RPL4TOOL -l`, skipping the unassigned ids,
|
||
gives an exact id→resource map: 1077 ids with **zero** size mismatches.
|
||
|
||
Subsystem names come the same way, from each vehicle's own
|
||
`Stream of N Subsystems`, checked against the count in its header rather
|
||
than pattern-matched out of the bytes.
|
||
|
||
The lesson is the ordinary one: the format had an explicit answer, and the
|
||
first decoder inferred one from layout instead. Everything the guess got
|
||
right, it got right by luck of ordering.
|