BT410 5.3.85: the six-bit hazard surveyed -- draw site named, 94 art files inventoried, a 1996 config typo found
Follow-up survey to 5.3.84, written into L4GREND.NOTES.md rather than left as
conversation.
THE DRAW SITE, NAMED. This closes a question that had been open across
several sessions ("which of the three DrawBitMapOpaque sites draws
sentinel-bearing sec art"). Answer: none of them. It is a DrawPixelMap8 --
BTL4GAU2.CPP:1524, the bgPixelMap widget, MoveToAbsolute(0,0) then a
full-image OPAQUE blit. BT_VIS_LOG places it at port=0, the six-bit head.
Every pixel of the colour head's background therefore goes through the
64-entry translation table, sentinels included.
ALSO PROVEN: no drawing operation contains the damage. The opaque inner loop's
Replace case is
*dest = (Word)((*dest & bitmask) | color);
which clears THIS port's bits and then ORs the whole table entry back --
including garbage in other ports' bit positions. Replace leaks exactly like
Or does.
ART INVENTORY. Swept all 428 PCX/PCC under GAUGE (which is all the art there
is -- nothing lives outside it). 94 carry pixels > 63: index 231 in 53 files
(sparse, 4-7%, the 172x217 avatar CRIT/HEAT silhouettes), 255 in 27 (the *HT
heat backgrounds), 226/228/229 as 1-2px specks in QJAK*, and 254 in 2.
BTSEC1.PCX is the only one whose out-of-range region is a SOLID RECTANGLE and
the only one proven to reach the screen -- 676px at (199,526)-(250,538),
rotation 270 mapping screen = (src_y, 479 - src_x), landing exactly on the
measured mask.
WHAT IS STILL OPEN, recorded as open rather than smoothed over:
The 354px cluster. Of the provoke control's 1030 lit pixels, 676 are the
bar; the remaining 33x32 cluster at (67,3)-(99,34) is NOT file-borne -- no
gauge file has a count near 354. So it is generated at runtime. The
obvious suspect CreateMutantPixelmap8 is ruled out: it is an inert stub
returning NULL, so recoloredMech never blits here. Unattributed; the fix
covers it regardless.
A possible second exposure, in-mission. L4GAUGE.CFG carries 1345
colour-mapper entries (656 cmCrit, 662 cmHeat, 27 cmArmor), BT_VIS_LOG puts
ALL of them on port 0, and their mode is ModeSecondaryCritical. They name
adpal/adpal2/heatpal, three of which the sweep flags -- ADPAL and ADPAL2 are
2x5 and ENTIRELY index 99. Whether that matters turns on whether a mapper
blits those pixels or merely reads their VGA palette, which nobody has
checked. Written down as a hypothesis with that caveat attached, NOT as a
finding. If the pixels are used as indices it would dwarf the bar and would
fire in-mission, which is where the operator sees the artifact.
The boot cockpit lighting only 1030 pixels says the avatar/mapper art is not
drawn on THAT screen. It says nothing about a live mission.
A SHIPPED DATA BUG, found in passing and deliberately NOT fixed:
L4GAUGE.CFG:2513-2514 name adpa12.pcc -- digit one for lowercase L. The
file does not exist; adpal2.pcc does. Two of 1345 lines, so the GAUSS and
AFC25 ammo-bin criticals have been loading a missing palette since 1996.
It is the original's bug and the shipped binary lives with it; the archive
is sacred, so we live with it too. Noted so the next person who sees a
warning about adpa12.pcc does not go hunting for a reconstruction defect.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# L4GREND.CPP — reconstruction shadow notes
|
||||
|
||||
The shadow exists for ONE reason: to construct a `BTL4GraphicsPort` instead of
|
||||
an `L4GraphicsPort`, so the colour head's uninitialised translation-table tail
|
||||
gets zeroed. Everything else in the file is the archive's, verbatim.
|
||||
|
||||
The mechanism, the proof, and the honest verification status live in the file's
|
||||
own header comment. This sidecar carries the survey work that did not belong in
|
||||
a source comment.
|
||||
|
||||
## The hazard, in one line
|
||||
|
||||
The colour head is **six bits** (`sec`, mask `0x003F`, `L4GAUGE.CFG`), so
|
||||
`BuildSecondaryTranslation` (`L4VB16.CPP:5419`) fills only `1<<numberOfBits` =
|
||||
**64** of `int translationTable[256]`. Any source pixel `> 63` reads
|
||||
uninitialised memory, and `DrawPixelMap8` ORs the result into the shared
|
||||
plane-packed framebuffer **unmasked**.
|
||||
|
||||
Worth noting that even `Replace` leaks. The opaque inner loop is
|
||||
|
||||
```c
|
||||
color = (Word) translation_table[source_data];
|
||||
case GraphicsDisplay::Replace:
|
||||
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
||||
```
|
||||
|
||||
`& bitmask` clears *this* port's bits, then `| color` ORs the whole table entry
|
||||
back — including whatever garbage sits in other ports' bit positions. There is
|
||||
no drawing operation that contains the damage.
|
||||
|
||||
## The draw site — answers a long-open question
|
||||
|
||||
`restoration/source410/BT_L4/BTL4GAU2.CPP:1524`, the `bgPixelMap` widget:
|
||||
|
||||
```c
|
||||
localView.MoveToAbsolute(0, 0);
|
||||
localView.DrawPixelMap8(True, 0, bg, 0, 0, // opaque, full image
|
||||
bg->Data.Size.x - 1, bg->Data.Size.y - 1);
|
||||
```
|
||||
|
||||
`BT_VIS_LOG` places it at `[w] bgPixelMap port=0 at 0,0` — port 0 is `sec`.
|
||||
Full-image, opaque, on the six-bit head: every pixel of the background goes
|
||||
through the table, sentinels included. Of the three `DrawBitMapOpaque` sites
|
||||
that were under suspicion, none is the culprit; this `DrawPixelMap8` is.
|
||||
|
||||
## Art inventory — 94 of 428 gauge files carry out-of-range pixels
|
||||
|
||||
Swept every `.PCX`/`.PCC` under `ALPHA_1/REL410/BT/GAUGE` (all the art there is
|
||||
— nothing lives outside that directory). Distinct out-of-range values:
|
||||
|
||||
| index | files | shape | reading |
|
||||
|---|---|---|---|
|
||||
| 231 | 53 | sparse, ~4–7%, in a mech-silhouette region of 172×217 `*CRIT`/`*HEAT` art | avatar damage/heat art |
|
||||
| 255 | 27 | sparse, ~5–7%, `*HT` art (252×200, 229×199, 303×200) | heat backgrounds |
|
||||
| 254 | 2 | **solid 52×13 rect** in `BTSEC1.PCX` | the confirmed offender |
|
||||
| 226/228/229 | 8/8/7 | 1–2px specks in `QJAK*.PCC` | paint spatter, harmless |
|
||||
| 99 | 2 | `ADPAL.PCC`, `ADPAL2.PCC` — 2×5, **100%** at 99 | palette swatches, see below |
|
||||
| 100 | 1 | `HEATPAL.PCC` — 3×3, one pixel | ditto |
|
||||
| 120, 127 | 1 each | | |
|
||||
|
||||
`BTSEC1.PCX` — "BT SEC-ondary", the colour head's own background — is the only
|
||||
one whose out-of-range region is a **solid rectangle**, and the only one proven
|
||||
to reach the screen: 676 pixels of index 254 at source (199,526)-(250,538),
|
||||
which the port's **rotation 270** maps to screen (526,228)-(538,279) via
|
||||
`screen = (src_y, 479 - src_x)`. The provoke mask measured exactly that box.
|
||||
|
||||
## What is NOT yet resolved
|
||||
|
||||
**The 354px cluster.** The provoke control lights 1030 pixels on the boot
|
||||
cockpit: 676 for the bar, and a 33×32 cluster at screen (67,3)-(99,34) that is
|
||||
*not* file-borne — no gauge file has an out-of-range count anywhere near 354,
|
||||
and all art lives in that one directory. It is therefore generated at runtime.
|
||||
The obvious suspect, `CreateMutantPixelmap8` (`BTL4GAU3.CPP:1067`), is ruled
|
||||
out: it is an inert stub returning NULL, so `recoloredMech` never blits in our
|
||||
build. Unattributed. The fix covers it either way, since it zeroes the whole
|
||||
tail regardless of who indexes it.
|
||||
|
||||
**The colour mappers may be a second exposure, in-mission.** `L4GAUGE.CFG`
|
||||
carries **1345** colour-mapper entries — 656 `cmCrit`, 662 `cmHeat`, 27
|
||||
`cmArmor` — and `BT_VIS_LOG` puts all of them on **port 0**, the six-bit head,
|
||||
at 50,0. Their mode is literally `ModeSecondaryCritical`. They name
|
||||
`adpal.pcc`, `adpal2.pcc`, `heatpal.pcc`, `heatpal2.pcc`, three of which the
|
||||
sweep flags as out-of-range (`ADPAL`/`ADPAL2` are 2×5 and **entirely** index
|
||||
99; `HEATPAL` has one pixel at 100).
|
||||
|
||||
Whether that matters depends on something not yet established: these are tiny
|
||||
swatch files, and a colour mapper plausibly reads their **VGA palette** rather
|
||||
than blitting their pixels. Do not repeat this as a finding until someone has
|
||||
read the mapper's implementation. It is recorded because if the pixels *are*
|
||||
used as colour indices, every one of 1345 mappers writes an out-of-range index
|
||||
on the six-bit head — which would dwarf the 676px bar and would fire
|
||||
in-mission, matching where the operator sees the artifact.
|
||||
|
||||
That the boot cockpit lights only 1030 pixels says the avatar/mapper art is not
|
||||
being drawn on that screen. It does not say anything about a live mission.
|
||||
|
||||
## A shipped data bug, found in passing — do NOT fix
|
||||
|
||||
```
|
||||
L4GAUGE.CFG:2513: cmCrit(I,ModeSecondaryCritical,56,adpal.pcc,adpa12.pcc,AmmoBinGAUSS);
|
||||
L4GAUGE.CFG:2514: cmCrit(J,ModeSecondaryCritical,57,adpal.pcc,adpa12.pcc,AmmoBinAFC25);
|
||||
```
|
||||
|
||||
`adpa12.pcc` — digit one for lowercase L — does not exist. `adpal2.pcc` does.
|
||||
Two of 1345 lines, so the GAUSS and AFC25 ammo-bin criticals have been loading
|
||||
a missing palette since 1996. This is the original's bug, present in the
|
||||
shipped config; the archive is sacred and the shipped binary lives with it, so
|
||||
we live with it too. Noted only so the next person who sees a warning about
|
||||
`adpa12.pcc` does not go hunting for a reconstruction defect.
|
||||
|
||||
## Method note
|
||||
|
||||
Zero is the right fix precisely because it is invisible — and that is what made
|
||||
it unverifiable. The `BT_TRANS_PROVOKE` positive control (fill the tail with
|
||||
`0xFF00` instead) is what turned "I cannot see a difference" into "1030 pixels,
|
||||
here they are, here is the shape". See `emulator/render-bridge/gauge-ab/README.md`
|
||||
for the rig, `oormask.py` for the mask, and the caution about within-boot vs
|
||||
across-boot noise floors that nearly cost this fix.
|
||||
Reference in New Issue
Block a user