#98: CORRECTION -- restore the byte-verified condenser lamp table; my previous mapping was a guess and was wrong
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0254d9ef34
commit
0b983703a4
@@ -163,8 +163,13 @@ static const int kBTQuadModeMask[12] = { 0x1,0x1,0x1,0x1, 0x20,0x20,0x20,0x20, 0
|
||||
static const int kBTEngModeMask[12] = { 0x2,0x4,0x8,0x10, 0x40,0x80,0x100,0x200, 0x800,0x1000,0x2000,0x4000 };
|
||||
static const int kBTQuadLamp[12] = { 0xF,0xD,0xB,0x9, 0x27,0x25,0x23,0x21, 0x7,0x5,0x3,0x1 };
|
||||
static const int kBTEngBankTop[12] = { 0xF,0xF,0xF,0xF, 0x27,0x27,0x27,0x27, 0x7,0x7,0x7,0x7 };
|
||||
// (the per-condenser lamp table that used to live here is retired: condenser N
|
||||
// resolves through BTFixedLampOf(N-1), the same six cooling-loop lamps. #98)
|
||||
// @0051d058 per-condenser lamps, BYTE-VERIFIED from the image (int32 each):
|
||||
// 07 2F 2E 2D 2B 2A 29 ... indexed 1-BASED by condenserNumber (+0x1D4),
|
||||
// so slot 0 is unused and condensers 1..6 -> 2F 2E 2D 2B 2A 29.
|
||||
// Deliberately NOT the coolingLoop set of FUN_004cc148 (which has 0x2C and
|
||||
// no 0x29) -- overlapping but distinct lamps. Slot 6 (0x29) is also
|
||||
// DAT_0051d070[0], the first per-placement lamp: the two tables abut.
|
||||
static const int kBTCondenserLamp[7] = { 0x7, 0x2F,0x2E,0x2D,0x2B,0x2A, 0x29 };
|
||||
static const int kBTPlacementLamp[5] = { 0x29, 0x1A,0x1B,0x1C,0x1D }; // [auxScreenPlacement]
|
||||
|
||||
//
|
||||
@@ -292,21 +297,30 @@ void
|
||||
// #98 -- "I'm getting leaks but no indicators" / "the display
|
||||
// buttons aren't ALWAYS flashing on leaking components".
|
||||
//
|
||||
// condenserNumber is 1-BASED (verified live: 'Condenser6' reports 6),
|
||||
// which the old table's own comment said -- but the guard was
|
||||
// `n >= 0 && n < 6`, a 0-based bound. Condenser 6 was therefore
|
||||
// REJECTED outright and could never annunciate, and the table it
|
||||
// indexed was missing 0x2C, so condensers 4 and 5 flashed the NEXT
|
||||
// loop's button instead of their own. Three of six correct, one
|
||||
// silent, two lying -- exactly the "sometimes" players reported.
|
||||
// FUN_004cc264 is literally:
|
||||
// return *(int *)(&DAT_0051d058 + sub[0x1d4] * 4);
|
||||
// -- indexed by condenserNumber with NO bounds check. The table
|
||||
// bytes at 0051d058 are, verified from the image:
|
||||
// 07 2F 2E 2D 2B 2A 29 ... (int32 each)
|
||||
// and condenserNumber is 1-BASED (live: 'Condenser6' reports 6), so
|
||||
// slot 0 (0x7) is unused and condensers 1..6 map to
|
||||
// 0x2F 0x2E 0x2D 0x2B 0x2A 0x29.
|
||||
// NOTE these are NOT the coolingLoop1..6 lamps of FUN_004cc148
|
||||
// (0x2F 0x2E 0x2D 0x2C 0x2B 0x2A) -- the condenser set skips 0x2C
|
||||
// and ends on 0x29. They overlap but are different lamps; do not
|
||||
// "correct" one into the other (I did, and it mis-mapped 4, 5 and 6).
|
||||
//
|
||||
// Condenser N drives cooling-loop N, whose six lamps are the same
|
||||
// ones the coolingLoop1..6 codes resolve to, so use that verified
|
||||
// map rather than a second, partly-wrong copy of it.
|
||||
// THE PORT BUG was only the bounds check: `n >= 0 && n < 6` is a
|
||||
// 0-based bound on a 1-based index, so condenser 6 was rejected and
|
||||
// annunciated NOTHING, while the binary resolves it to 0x29. Six
|
||||
// condensers, one permanently silent -- which is what players read as
|
||||
// "sometimes". The guard now admits 1..6 (kept, unlike the binary's
|
||||
// unchecked read, so a stray authored number cannot walk off the
|
||||
// table into the adjacent per-placement one).
|
||||
//
|
||||
int n = BTCondenserNumber(the_subsystem);
|
||||
if (n >= 1 && n <= 6)
|
||||
lamp_id = BTFixedLampOf(n - 1); // loop N -> its own button
|
||||
lamp_id = kBTCondenserLamp[n];
|
||||
}
|
||||
}
|
||||
else if (BTSubsystemIsGenerator(the_subsystem))
|
||||
|
||||
Reference in New Issue
Block a user