diff --git a/restoration/source410/MUNGA_L4/L4GREND.NOTES.md b/restoration/source410/MUNGA_L4/L4GREND.NOTES.md new file mode 100644 index 00000000..2e159476 --- /dev/null +++ b/restoration/source410/MUNGA_L4/L4GREND.NOTES.md @@ -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< 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.