Carries `xenia-rs` `harvest/import-thunk-naming` (b4f19f1) across into the
lifted crate. That work was found UNCOMMITTED in the retired repo's working
tree on 2026-09-14 and exists nowhere else: absent from `iterate-4A`, absent
from this crate as lifted. CONSOLIDATION.md Phase 5 ends with "delete the local
clone" and Phase 7 drops the emulator, so it had a deletion scheduled against
it.
Not a cherry-pick. The lift's base is exactly the harvest's parent (8401d4d),
so each file was merged three-way -- lifted vs base vs harvest -- which is what
made the port reviewable: 10 conflicts, 9 of them pure rustfmt reflow from the
lift's formatting pass, and 1 semantic.
The semantic one is insertion ORDER. `xdbf_achievements.image_id` is now a
foreign key onto `xdbf_images(id)`, so the image rows must be inserted before
the achievement rows. The merge moved that block; the conflict was the stale
copy left at the old position.
What arrives:
* **Import-thunk recognition** (`imports.rs`, 338 lines). An XEX import is not
a PLT jump: the linker emits a four-word thunk whose first two words are
import RECORDS that the loader rewrites at module load. On disc they are
still records, so a PowerPC-only decoder prints two meaningless `.long`s in
front of an indirect branch. This maps every word of every thunk, and every
direct branch into one, back to its `imports` row. Shape-validated rather
than trusted: an entry is indexed only when the four words it points at
actually have the thunk shape.
Adds `instructions.import_address` (FK onto `imports.address`) and
`import_role` (`'record'` | `'thunk'` | `'call'`, NULL iff import_address is
NULL), plus an index. `tools/zq.py` gains `imp` and `impcalls`, and `dis`
now names imports instead of printing `.long 0x01010194`.
* **Sixteen schema-wide foreign keys**, declared wherever a column is derived
from another table. CREATE TABLE and insertion order become load-bearing.
`functions` is deliberately NOT an FK parent and the golden test now asserts
zero inbound FKs onto it: DuckDB implements UPDATE as delete+insert, so one
inbound FK would make `functions.name` un-updatable and break
`apply_re_symbols.sql`, which re-applies RE symbol names after every
regeneration.
The database path needed no change in the binary: `DbWriter` builds its own
index inside `ingest_instructions` from `info.import_libraries`. Only the two
output paths (`enrich_section` for JSONL, `write_asm`) take it as an argument.
Verified: `cargo test -p sylpheed-xexdb` = 10 passed / 0 failed, including
`db_schema_golden` (41s, builds a real DuckDB) which locks the 16-FK set and
the no-FK-onto-functions rule. `cargo clippy -p sylpheed-xexdb --all-targets
-- -D warnings` clean; `cargo fmt --all --check` clean.
Stacked on #32 -- it ports into a crate that only exists there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rustfmt, then clippy -D warnings across the three new crates. Mechanical,
except three decisions that are stated rather than silently allowed:
* lzx.rs gets file-scoped needless_range_loop/explicit_counter_loop allows.
Index arithmetic IS the algorithm -- LZX is defined over symbol indices,
Huffman slots and window positions, and a decompressor that is merely
idiomatic is worth nothing if it is not bit-exact.
* sylpheed-xexdb gets crate-scoped allows for needless_range_loop (nine
sites index reg[r] where r is the PowerPC register number -- the index is
the meaning), too_many_arguments and type_complexity. This code arrived
whole from a retired repository; a refactor here would be an unreviewed
edit dressed as a lint fix.
* Everything else clippy asked for is FIXED, including all 14 doc-indent
sites, the let-else, and a Prepared type alias in the binary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>