Files
Sylpheed/tools/re-capture/struct_layout_control.py
sylph-decoder 2bc20ec31d tools: a layout control for structural claims -- and the obvious version does not work
sylpheed-port named a gap in their own rule: the fourth aside of mine to reach their
authored data was a STRUCTURE, not a decoration, and 'the unchecked things carry no
weight' did not cover it because a wrong field order looks like a fact. It carried
no weight only by luck. The fix belongs at my end, so this is the control that
should have existed when I published the layout.

The obvious form fails, and its failure is the useful part: checking that all
records are type-plausible passes on the SHIFTED alignments too, 69 of 70 in both
directions. A homogeneous repeated table has the same field types in sequence, so
any window starting on a field boundary type-checks and the interior carries no
information about phase.

Only the BOUNDARIES do. A shifted reading must consume a word from outside the table
at one end, and that word does not obey the field's type -- which is exactly how the
original error surfaced, record 0's handler reading as 0x10000000. Two-sided: the
published alignment survives at both edges and both shifts fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:46:52 +00:00

112 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Type-plausibility control for a published record layout.
🔴 WHY THIS EXISTS. I published the dialog table as `{handler, id, name_ptr}`.
It is `{id, name_ptr, handler}` -- the same three fields shifted one word, so every
record was credited with the PREVIOUS record's handler. sylpheed-port had copied it
into their authored data before either of us noticed.
Their rule -- "the claims that go unchecked are the ones that carry no weight" --
did NOT protect them here, and they named the gap precisely: a wrong FIELD ORDER
looks like a fact rather than an aside, and a later reader builds on it. It carried
no weight only by luck.
So a structural claim needs a control that FAILS when the alignment is wrong. Every
field has a type, and a wrong alignment breaks the types: an id stops being small,
a string pointer stops pointing at a string, a code pointer stops looking like code.
⚠️ TWO-SIDED BY CONSTRUCTION: it checks the published layout passes AND that the
shifted alignments fail. A layout check that only confirms the current reading is
the same defect one level up.
struct_layout_control.py
"""
import struct
import sys
PE = open("/image/sylpheed.pe", "rb").read()
BASE = 0x82000000
CODE_LO, CODE_HI = 0x82000000, 0x82600000
def rd(va):
o = va - BASE
return struct.unpack_from(">I", PE, o)[0] if 0 <= o < len(PE) - 4 else None
def is_id(v):
return v is not None and v < 100_000
def is_dlg_name(v):
if v is None or not (BASE <= v < BASE + len(PE)):
return False
o = v - BASE
e = PE.find(b"\x00", o)
return e > o and PE[o:e].startswith(b"DLG_")
def is_code(v):
"""A code pointer whose target opens with a real PowerPC prologue."""
if v is None or not (CODE_LO <= v < CODE_HI) or v % 4:
return False
w = rd(v)
return w is not None and (w == 0x7D8802A6 or (w >> 26) == 37) # mflr r12 / stwu
def check(first_field_va, order, n=70):
"""order: sequence of predicates, one per 4-byte field."""
ok = 0
for k in range(n):
b = first_field_va + 12 * k
vals = [rd(b + 4 * i) for i in range(len(order))]
if all(p(v) for p, v in zip(order, vals)):
ok += 1
return ok
ID_NAME_HANDLER = (is_id, is_dlg_name, is_code)
HANDLER_ID_NAME = (is_code, is_id, is_dlg_name)
NAME_HANDLER_ID = (is_dlg_name, is_code, is_id)
TABLE = 0x820A0A30 # first record under the published layout
N = 70
# 🔴 THE OBVIOUS CONTROL DOES NOT WORK, AND THAT IS THE POINT.
# Checking "are all records type-plausible" passes on the SHIFTED alignments too:
# 69/70 in both directions. A homogeneous repeated table has the same field types
# in sequence -- id, name, handler, id, name, handler -- so ANY window starting on
# a field boundary type-checks. The interior carries no information about phase.
#
# ✅ ONLY THE BOUNDARIES DO. A shifted reading must consume a word from OUTSIDE the
# table at one end, and that word does not obey the field's type. That is exactly
# how the original error surfaced: under the shifted alignment record 0's "handler"
# was 0x10000000, the word sitting before the table.
print("── dialog table: alignment control (boundaries, not interior) ──")
print(" interior is UNINFORMATIVE, shown so nobody rebuilds it:")
for label, base, order in (("published ", TABLE, ID_NAME_HANDLER),
("shifted -1", TABLE - 4, HANDLER_ID_NAME),
("shifted +1", TABLE + 4, NAME_HANDLER_ID)):
print(f" {label} {check(base, order, N)}/{N} records type-plausible")
def edges_ok(base, order):
"""Both terminal records must type-check under this alignment."""
for k in (0, N - 1):
b = base + 12 * k
if not all(p(rd(b + 4 * i)) for i, p in enumerate(order)):
return False
return True
print("\n boundaries -- the discriminating test:")
res = [("PUBLISHED {id, name_ptr, handler}", edges_ok(TABLE, ID_NAME_HANDLER), True),
("shifted -1 {handler, id, name_ptr}", edges_ok(TABLE - 4, HANDLER_ID_NAME), False),
("shifted +1 {name_ptr, handler, id}", edges_ok(TABLE + 4, NAME_HANDLER_ID), False)]
bad = 0
for label, got, want in res:
good = (got == want)
bad += not good
print(f" {'' if good else '🔴'} {label} edges type-check: {got} (want {want})")
if bad:
print("🔴 LAYOUT CONTROL FAILED"); sys.exit(2)
print("\n ✅ only the published alignment survives at both boundaries")