Files
BFM-decomp/cookbook/C0451.md
T

2.4 KiB

§403

An undo-journal that restores by NAME corrupts any file whose symbol is declared twice — and reports success.

Both fix_arity_callers --undo-journal and cast_self_callers --undo-journal record the original text of each edit and restore it keyed by function name. When a symbol has more than one declaration in the file, the originals are restored into the wrong occurrences, silently swapping distinct prototypes.

Byte-witnessed twice in one session (P31 S70):

  • fix_arity_callers printed restored 382, kept 0, missing 0; git diff showed 97 insertions / 97 deletions in the FLEET-SHARED src/shared/engine_core.h — func_8012A828() <-> (int a0, void *a1) <-> (s32 a0, void *a1) rotated between three declaration sites.
  • cast_self_callers printed reverted 10 edit(s) across 1 file(s); src/800.c was left with the two declarations of func_80031988 swapped ((Ent30D80 *arg0) <-> (struct Ent30D80 *)).

This is R48 (never key by bare function name) applied to a journal, and R57 (an instrument's own write path is part of the instrument). The byte-gate does not necessarily catch it: the corruption is in a header, and a rebuild only differs if some binary's codegen happens to depend on the swapped prototype.

Rules. A journal records file + occurrence index (or byte offset), never just a name; and an undo hash-verifies the restored file against the recorded pre-edit content and FAILS LOUD on mismatch. After ANY undo-journal, run git diff on the touched files and restore the specific named file from HEAD if it is not clean — never a blanket git checkout -- src/ (R42).

SHIPPED (S70). The occurrence->original mapping is not recoverable from either journal format, so both tools now REFUSE (rc=2) when one (file, after) group maps back to differing before texts — naming the file and the rival originals, and writing nothing. Journals additionally record per-file sha_before, and a clean undo hash-verifies its own result. Negative-controlled against the journal that caused the corruption: it refuses and leaves the tree untouched. The refusal also showed why guessing was hopeless — extern void func_8012A828(); maps back to four distinct originals ((int a0, void *a1), (int, void *), (s32 *, s32), (s32 *a0, s32 a1)). A loud refusal costs one git checkout; a silent swap costs a corrupted fleet-shared header nobody notices.