0254d9e re-mapped condensers onto the coolingLoop1..6 lamps on the reasoning
that "six condensers <-> six loops" and that the table's missing 0x2C was a
transcription slip. It was not. Read the decomp -- which was exported the whole
time and which I should have read BEFORE touching the table:
FUN_004cc264(sub) { return *(int *)(&DAT_0051d058 + sub[0x1d4] * 4); }
indexed by condenserNumber, no bounds check. And the bytes at 0051d058 are:
07 00 00 00 2F 00 00 00 2E 00 00 00 2D 00 00 00
2B 00 00 00 2A 00 00 00 29 00 00 00 1A 00 00 00
So the ORIGINAL table was byte-accurate: the binary really does skip 0x2C. The
condenser lamps (2F 2E 2D 2B 2A 29) are a DIFFERENT set from the coolingLoop
lamps of FUN_004cc148 (2F 2E 2D 2C 2B 2A) -- overlapping but distinct. My
"fix" therefore mis-mapped condensers 4, 5 AND 6, replacing a correct mapping
with a plausible-looking wrong one.
What IS a real port defect, and all that should have changed: the bounds check.
`n >= 0 && n < 6` is a 0-based bound on a 1-based index (live: 'Condenser6'
reports 6), so condenser 6 was rejected and annunciated NOTHING, where the
binary resolves it to 0x29. The table is now 7 slots ending in 0x29 and the
guard admits 1..6 -- kept rather than dropped, so a stray authored number cannot
walk off the end into the adjacent per-placement table the way the binary's
unchecked read would.
Verified: Condenser6 leak -> [galarm] condition 2 -> lamp 0x29 FLASH, which is
what the binary computes for it.
Lesson, and it is the third time this week: I had a live-reproduced symptom and
inferred the cause from internal consistency instead of reading the available
decomp. The symptom was real; the explanation was invented. Gotcha 24 covers
over-generalising a verified fact -- this is its sibling: inventing a fact to
explain a verified symptom.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>