Instructions and generated text that still sent readers to `xenia-rs`, which is
archived and deleted locally. Provenance and dated research records are left as
they are — "lifted from xenia-rs", or a finding stating what database it was
measured against, is still true, and rewriting it would falsify the record.
Changed, because each one tells someone what to do today or writes stale text:
docs/re/README.md the RE toolchain guide queried `xenia-rs/sylpheed.db`
with `xenia-rs/zq.py`, and its "Dynamic" bullet said
to prefer xenia-rs's probe suite OVER Canary. Now:
`sylpheed.db` at the repo root (how to build it and
re-stamp names), `tools/zq.py`, dynamic = Canary, and
`.rdata` reads from the `.pe` (offset = VA - 0x82000000)
in place of `--dump-addr`.
challenge-mission-gate.md, structures/achievements.md
reproduction commands `python3 xenia-rs/zq.py dis …`
crates/sylpheed-xexdb/SCHEMA.md
titled after the retired `xenia-analysis` crate,
citing `xenia-rs dis`, pointing at a `xenia-analysis`
source path
sylpheed-xexdb/src/formatter.rs
WROTE "generated by xenia-rs" into every disassembly
it produced (no test pins the banner)
sylph-xexdb.rs, db.rs "SQLite" / "DuckDB writer for xenia-rs"
sylpheed-formats/src/hash.rs
pointed at `xenia-rs/RE_SYMBOLS.md`, now at
`docs/re/RE_SYMBOLS.md`
Verified: every rewritten reproduction command was extracted from the doc and
run as written against the regenerated database — all 5 exit 0 and return the
disassembly they describe. The README's function count (25 676) is the database's
own. `cargo fmt --all -- --check` clean.
Not changed, deliberately: the README's Oracle bullet says Canary's native Linux
ELF "crashes / does not run". That is about Canary, not xenia-rs, and it conflicts
with a July note that the native build works — unverified either way here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
26 KiB
sylph-xexdb schema reference
Authoritative documentation for the DuckDB tables and SQL views produced by
sylph-xexdb dis <xex|iso> --db sylpheed.db --analyze sql. Track schema changes here alongside any
update to the db_schema_golden test fixture.
The base + disasm tables (metadata, sections, imports, functions,
labels, instructions, xrefs, opt-in exec_trace / import_calls /
branch_trace) are documented inline in src/db.rs doc comment. This file
collects layered analysis additions and forward-work notes.
Layer M1 — .pdata boundary correction (landed)
Schema additions
functions.pdata_validated BOOLEAN NOT NULL—truewhen the row'saddressmatches aRUNTIME_FUNCTION.BeginAddressfrom.pdata. Linker ground truth.functions.pdata_length BIGINT NULL—function_length(bytes) from the matching pdata entry;NULLwhen the row is prologue-only.- New table
pdata_entries(begin_address BIGINT PRIMARY KEY, end_address BIGINT, function_length BIGINT, prolog_length BIGINT, flags BIGINT)— every parsed.pdataRUNTIME_FUNCTIONentry (raw, before any merge with prologue analysis). - Index
idx_functions_pdata_validatedonfunctions(pdata_validated).
What this layer does
- Parses
.pdata8-byteRUNTIME_FUNCTIONentries (PowerPC PE32 layout): word 0BeginAddress(absolute VA), word 1 packed{prolog_length:8, function_length:22, flags:2}, both big-endian. - Unions pdata
BeginAddressvalues into the function-candidate set fed to the prologue walker, so functions our prologue heuristic missed still get rows. - When pdata supplies a longer
function_lengththan the prologue walk found, extendsend_addressto the pdata-implied end (catches mis-split where the walker stopped at an earlyblr). - After the walker, performs a forward pass that trims
function.endto the next start when they overlap (catches mis-merge where one row spanned two prologues — the audit-031sub_824D23B0/sub_824D29F0case).
What this layer does NOT do
- Does not adjust prolog-derived
frame_size/saved_gprsfrom.pdata'sprolog_lengthfield — those remain prologue-only inferences. - Does not classify functions further than the existing
is_leaf/is_saverestorecolumns. Class membership is M3. - Does not detect functions whose entries are missing from BOTH
.pdataand the bl-target scan (extremely rare; would require executable-byte linear sweep).
Reference docs
- Microsoft PE32+ exception data spec for PowerPC RUNTIME_FUNCTION.
- xenia-canary
src/xenia/cpu/xex_module.cc:1570-1587— canary's reference parser (extractsBeginAddressonly; we additionally decode word 1).
Validation queries
-- All pdata entries found
SELECT COUNT(*) FROM pdata_entries; -- ~23073 for Sylpheed
-- Functions cross-validated against pdata
SELECT COUNT(*) FROM functions WHERE pdata_validated;
-- Functions detected ONLY by prologue (orphans of pdata)
SELECT COUNT(*) FROM functions WHERE NOT pdata_validated;
-- Pdata orphans NOT yet in functions (should be 0 after this layer)
SELECT COUNT(*) FROM pdata_entries p
LEFT JOIN functions f ON f.address = p.begin_address
WHERE f.address IS NULL;
-- Audit-031 mis-merge resolved: 0x824D29F0 should have its own row
SELECT name FROM functions WHERE address = 2186674160; -- 0x824D29F0
Layer M2 — MSVC C++ name demangler (landed)
Schema additions
- New table
demangled_names(address BIGINT NULL, mangled VARCHAR NOT NULL, raw_demangled VARCHAR NOT NULL, namespace_path VARCHAR NULL, class_name VARCHAR NULL, method_name VARCHAR NULL, params_signature VARCHAR NULL). - Indices on
address,class_name,method_name.
What this layer does
- Wraps
msvc_demangler::demangle(a Rust port of LLVM'sMicrosoftDemangle.cpp) and splits the formatted output into structured fields via a heuristic top-level parser (handles templates and nested parens correctly). - Populates
demangled_namesfrom any label whose name starts with?plus any import name that happens to be mangled (defensive — typical kernel imports use C names).
What this layer does NOT do
- Does not parse the AST returned by
msvc_demangler::parse— uses the formatted string and a heuristic split. Adequate for typical class member functions and RTTI strings; exotic template / lambda forms still getraw_demangledpopulated but may have NULL structured fields. - Does not yet ingest RTTI strings discovered in
.rdata— that's M3's job; M3 will append rows to this table at the addresses where it finds RTTI TypeDescriptors.
Reference docs
msvc-demanglercrate (https://docs.rs/msvc-demangler/0.11).- LLVM
MicrosoftDemangle.cpp(the parser this crate ports).
Layer M3 — Vtable + RTTI detection (landed)
Schema additions
vtables(address PK, length, col_address NULL, class_name, rtti_present, base_classes_json NULL)— every detected static vtable.methods(vtable_address, slot, function_address, mangled_name NULL, demangled_name NULL, PRIMARY KEY (vtable_address, slot))— one row per method slot.classes(name PK, vtable_address, rtti_present, base_classes_json NULL)— deduped by class name (first-detected vtable wins).- Indices:
methods.function_address,classes.rtti_present.
What this layer does
- Walks
.rdataand.datalooking for runs of ≥3 consecutive 4-byte BE values where each value is a known function start (from M1's correctedfunctionstable). Single-2-method vtables are intentionally rejected to control false-positive rate. - Attempts the MSVC RTTI walk
vtable[-1] → CompleteObjectLocator → TypeDescriptorfor each candidate. When successful, the demangledclass ClassNamestring fillsclass_nameand a best-effortRTTIClassHierarchyDescriptorwalk fillsbase_classes_json(JSON array of base class names). - Falls back to
ANON_Class_<8-hex>keyed by FNV-1a hash of the sorted method-PC tuple when RTTI is absent (typical for shipped game binaries). Identical vtables across the binary (multiple instances) collapse to the same anonymous name.
What this layer does NOT do
- Vtables built at runtime in heap-allocated memory (e.g. by ctors copying
static templates) are out of scope — only static
.rdata/.datacontent. - Multiple-inheritance "extra" vftables (one per base subobject) are detected as independent vtables with no link between them.
- Inheritance-tree walking beyond
RTTIClassHierarchyDescriptor's direct base list is not attempted.
Reference docs
- openrce.org "Reversing Microsoft Visual C++" — RTTI layout articles (CompleteObjectLocator at vtable[-1]; TypeDescriptor at COL+0xC; mangled name at TD+0x8).
Layer M4 — Class-aware probe targeting (landed)
CLI extension only — no schema changes. The probe-token grammar adds three
symbolic forms on top of the existing 0xADDR literal:
Class::method— joinsclasses×methods×demangled_namesto find every PC whose vtable belongs to that class and whose demangledmethod_namematches.Class::*— joinsclasses×methodsto find every method PC of that class.function_name— falls back tofunctions.namelookup for free functions / saverestore stubs / labels.
Numeric tokens never touch the DB (preserves zero-IO fast path; lockstep
digest unaffected). Symbolic tokens require the DuckDB at --probe-db PATH
or XENIA_PROBE_DB; default is sylpheed.db next to the .iso when present.
Resolution happens BEFORE guest exec begins, so it cannot affect the lockstep digest.
See crates/sylpheed-xexdb/src/lookup.rs.
Layer M5 — Indirect-dispatch reachability (landed)
Schema additions
- New value
'ind_call'in thexrefs.kindset. - New SQL view
v_indirect_reachability_from_entry— strict superset ofv_reachability_from_entry, takingind_calledges in the BFS.
What this layer does
- Walks each
FuncAnalysis.functionsentry with a per-basic-block register tracker. Recognises the canonical static-vtable pattern:lis+addi → lwz off(rA) → mtctr → bcctrl, whererAends up holding a known vtable's start address from M3. - Honours the PowerPC ABI:
bl-style calls (op 18 / 16 with LK=1) clobber volatile r0..r12 + ctr but preserve non-volatile r13..r31, so a vtable pointer parked in r30/r31 before a call survives. - Treats every M3
loc_*label as a basic-block boundary (kills register state) so jump-IN paths cannot induce false positives.
What this layer does NOT do (and observed impact)
- Vtable pointer loaded from a
this-pointer field (lwz r_vt, off(rA)whererA = this) — by far the dominant pattern in real C++ — is unresolvable without alias / points-to analysis. - On Sylpheed: the layer detects 0 edges. The binary's 1,001 lis+addi
references into vtables are mostly constructor-side vptr writes
(
stw rVtable, vptr_offset(this)), not direct dispatches. The renderer hunt's audit-009 cluster therefore needs a future M5.5 withthis-flow tracking before this layer surfaces it.
Reference docs
- IBM PowerPC ABI: register-save convention (volatile r0..r12 + ctr, non-volatile r13..r31).
Layer M7 — String / constant-pool detection (landed)
Schema additions
- New table
strings(address PK, encoding, length, content). - Index
idx_strings_encoding.
What this layer does
- Scans
.rdatafor runs of length ≥ 6 of printable ASCII bytes followed by a NUL terminator. - Scans
.rdatafor UTF-16LE runs of length ≥ 6 code units (printable-ASCII basic plane only) followed by a u16 NUL terminator. - Cross-reference is implicit: existing
xrefs.kind='ref'rows whosetargetfalls instrings.address's exact match set name the referencing PCs. SQL:SELECT s.content, x.source FROM xrefs x JOIN strings s ON s.address = x.target WHERE x.kind='ref'.
What this layer does NOT do
- No UTF-8 multibyte / non-ASCII basic plane in either encoding.
- No
.datascan (read-only-section bias). - No multi-byte CJK encodings — Japanese text in localised builds may be represented in shift_jis / utf-8 with non-printable bytes that this scanner skips.
Sylpheed yield
- 6,311 ASCII strings (including full embedded HLSL shader source).
- 0 UTF-16LE strings (binary uses ASCII / native CJK encoding).
- 9,132 lis+addi sites cross-reference into the detected strings — names the source PCs that reference each string.
Layer M6 — Extended store-class xrefs + addr_mode column (landed)
Schema additions
xrefs.addr_mode VARCHAR NULL— sub-classifies how the source instruction computes its target. NULL for control-flow edges (call / ind_call / j / br); one of the following tags for data edges:d_form— standard signed-16 displacement (lwz/stw/lfs/stfs/etc.)lis_addi— address materialised vialis + addiregister trackinglis_ori— address materialised vialis + orimultiword—lmw / stmw(one xref per slot; up to 32-rS slots)x_form_indexed—stwx / stbx / sthx / stwux / stbux / sthux / stdx / stdux / lwzx / lbzx / lhzx / lhax / lwzux / lbzux / lhzux / lhaux / ldx / ldux— emitted only when both rA and rB are tracked constantsx_form_byterev—stwbrx / sthbrx / lwbrx / lhbrxatomic—stwcx. / stdcx.reservation-conditional storesdcbz— cache-line clear (32-byte zero at rA+rB)
- Index
idx_xrefs_addr_mode.
What this layer does
- Tags every existing data xref with its addressing mode (
d_formfor the bulk;lis_addi/lis_orifor the lift-and-add cases that produce DataRef rows). - Adds new dispatch for opcode 47 (
stmw) and 46 (lmw), expanding to per-slot DataWrite / DataRead rows. - Adds new dispatch for opcode 31 X-form: stores, atomic, byte-reverse, dcbz. X-form rows are emitted ONLY when both rA and rB resolve to known constants (otherwise the address is runtime-dependent and we skip).
What this layer does NOT do
- VMX / VMX128 vector stores (opcode 31 with vector XO codes) are not emitted — they always have register-indexed addresses that the lis+addi tracker can't usually resolve, and detecting them adds noise without improving target resolution.
- The dominant runtime-of-stwx pattern (rA = base, rB = runtime index) is not resolved — by design; mem-watch covers the runtime side per VERIFY-B.
Sylpheed yield
- 28,834
lis_addirefs, 18,485d_formreads, 3,288d_formwrites — the existing baseline now properly tagged. - 442 newly-detected
x_form_indexedreads — primarily lwzx/lhzx reads from in-table dispatch (each pair (rA,rB) resolved statically). - 40 newly-detected
atomicwrites — everystwcx.site with a resolvable address; useful for reservation-table audits. - 9
lis_orirefs. - 0 multiword / dcbz / byterev — these instructions exist in the binary but are not in lis+addi-tracked code paths.
Layer M8 + M11 — Function-pointer arrays beyond vtables (landed)
Schema additions
- New table
function_pointer_arrays(address PK, length, kind)wherekindis'vtable'(M3 re-emit),'dispatch_table'(M8), or'static_init'(M11). - New table
function_pointer_array_entries(array_address, slot, function_address, PRIMARY KEY (array_address, slot))— one row per slot of every detected array (vtable + non-vtable). - Indices on
function_pointer_arrays.kindandfunction_pointer_array_entries.function_address.
What this layer does
- Walks
.rdata(only —.dataproduces too many false positives) for runs of ≥ 2 consecutive 4-byte BE values where each value is a known function entry from M1'sfunctionstable. - Skips runs whose start matches an M3 vtable head — those are re-emitted
in this table with
kind='vtable'for unified queries but not re-classified. - Heuristically classifies non-vtable runs:
static_init(M11): every entry's first instruction ismfspr r12, LRAND the next isstwu r1, -N(r1)withN ≤ 0x80(or a save-stubbl). Mirrors the typical C++ static-initialiser prologue.dispatch_table(M8): everything else.
What this layer does NOT do
- Does not parse symbol-table-bracketed regions like
__xc_a/__xc_z/__xi_a/__xi_zdirectly — Sylpheed's symbol table is stripped. - Does not chain multi-segment static-init drivers; future M11.5 could walk the entry-point's static-init driver call chain to surface ground-truth ctor PCs.
- 2-slot runs in
.rdatamay be false positives where two struct fields happen to alias function VAs; downstream queries should use a length filter (WHERE length >= 3) when high precision matters.
Sylpheed yield
- 722 vtables (M3 re-emit) + 388 dispatch_tables = 1,110 arrays in
function_pointer_arrays. - 0 static_init detected — Sylpheed's ctors don't all match the conservative prologue heuristic. Lengths concentrate at 2 slots (typical of switch-case jump tables).
Layer M9 — has_eh from .pdata exception flag (landed)
Schema additions
functions.has_eh BOOLEAN NOT NULL— true when.pdata's exception- handler-present bit (bit 31 of word 1, the high bit) is set.- Index
idx_functions_has_eh.
What this layer does
- Derived directly from M1's already-parsed
pdata.flagsbit field (no new parsing). The bit was always available inpdata_entries.flags; this layer surfaces it as a first-class column onfunctions.
What this layer does NOT do
- Does not parse the actual
__CxxFrameHandler/__C_specific_handlerscope-table records that the exception bit gates. Walking those tables would let us name try/catch ranges and per-state cleanup actions, but is out of scope for a derive-only milestone.
Sylpheed yield
- 2,975 of 23,073 pdata-validated functions have
has_eh=true(12.9%) — plausible MSVC C++ EH coverage rate. Largest EH function: 26,328 bytes (sub_823518F0).
Layer M10 — .tls section / TLS directory (landed)
Schema additions
- New table
tls_info(raw_data_start, raw_data_end, index_address, callback_array, zero_fill_size, characteristics)— at most one row (the IMAGE_TLS_DIRECTORY32). - New table
tls_callbacks(slot PK, address)— one row per resolved TLS callback function.
What this layer does
- Reads the first 24 bytes of the
.tlssection as anIMAGE_TLS_DIRECTORY32and walks the zero-terminated callback array. - All addresses stored as absolute VAs.
What this layer does NOT do
- Does not parse the raw TLS template content (the variable initialiser block); just records its start/end VAs.
Sylpheed yield
- 0 rows — Sylpheed has no
.tlssection. Infrastructure ready for any binary that uses__declspec(thread)storage.
Layer M12 — --lr-trace runtime canary-diff harness (landed)
Runtime additions (no DB)
- New CLI flag
--lr-trace=PC[,PC,...]onexec— comma-separated PCs to capture as JSONL records on every fire. Symbolic tokens (Class::method) resolve via M4's lookup against--probe-db. Settable viaXENIA_LR_TRACE. - New CLI flag
--lr-trace-out=PATH— writes JSONL to a file (one record per line). Stdout when omitted. Settable viaXENIA_LR_TRACE_OUT. - New kernel state fields
lr_trace_pcs: HashSet<u32>+lr_trace_writer: Option<Mutex<File>>and helperKernelState::fire_lr_trace_if_match(hw_id)invoked from the per-instruction probe slot.
JSONL record fields
pc, tid, hw, cycle, r3, r4, r5, r6, lr — superset of what
xenia-canary's --log_lr_on_pc patch emits, with a cycle counter added
for cross-run reproducibility.
What this layer does NOT do
- Does not capture VMX / FP register state (only GPRs r3..r6).
- Does not buffer / batch records — one
write_allper fire. For high-frequency probes (e.g. tight loops at >1M fires/sec), redirect to a file and use a SSD.
Determinism
Lockstep digest unaffected: probe firing happens after the per-instr
hooks for ctor/branch probes and only emits side-channel output. Verified
end-of-session: check sylpheed.iso --stable-digest -n 2M ×2 produced
byte-identical digests (instructions=2000005).
Layer M5.5 — this-flow indirect-dispatch resolution (landed)
Schema additions
- New table
vptr_writes(writer_pc, vtable_address, vptr_offset, writer_function)— every detectedstw rVtable, vptr_off(rThis)site. - New table
indirect_dispatch_sites(dispatch_pc PK, vptr_offset, slot, candidate_count)— one row per resolved dispatch. - New table
indirect_dispatch_candidates(dispatch_pc, vtable_address, method_address)— one row per (dispatch × candidate vtable). Joined to existingxrefs.kind='ind_call'edges (one ind_call row per candidate). - New indices on
vptr_writes.vtable_address,vptr_writes.vptr_offset,indirect_dispatch_candidates.method_address,indirect_dispatch_candidates.vtable_address,indirect_dispatch_sites.(vptr_offset, slot).
What this layer does (class-membership inference)
- Phase 1 — vptr-write scan: walk every function with the lis+addi
tracker; whenever
stw rA, off(rB)writes a known M3 vtable address, record(vtable_addr, vptr_offset, writer_pc). - Phase 2 — invert: build
vtables_by_offset[vptr_off] = {V}for the set of vtables ever written at that offset. - Phase 3 — dispatch detection: walk back ≤16 instructions from each
bcctrl/bctr LK=1, find the canonicallwz vt, off(this); lwz fn, slot*4(vt); mtctr fnchain. Extract(vptr_off, slot). Bail on register clobber, branch, or label boundary. - Phase 4 — emit: for each
(dispatch_pc, vptr_off, slot), emit onexrefs.kind='ind_call'row per candidate vtable that has a matching slot. Multi-candidate rows are an over-approximation.
What this layer does NOT do
- No alias resolution at multi-candidate sites — emits one edge per
matching vtable. Downstream queries should filter
indirect_dispatch_sites WHERE candidate_count=1for high-confidence edges. - No flow-sensitive analysis: register state is killed at every label
(basic-block boundary) and at
bl/bclcalls (volatile r0..r12 + ctr). We do NOT propagate values across calls in the chain-walker. - No tracking of vptr writes via X-form indexed (
stwx), VMX, or multiword stores. Only D-formstw rA, off(rB). - Does not synthesise vptr writes for inlined / elided constructors.
If a class never has a writer at offset
vptr_off, dispatches through that offset find no candidates.
Sylpheed yield
- 567 vptr writes covering 214 distinct vtables (~30% of M3's 722).
- 29 distinct vptr offsets used; offset 0 dominates (501/567 = 88%, single-inheritance).
- 6,842 dispatch sites resolved: 97 single-candidate (high-confidence) + 6,745 multi-candidate (over-approximation).
- 687,963
ind_callxref rows total. - 2,746 newly-reachable functions via the M5 BFS view
(
v_indirect_reachability_from_entry) compared to call/j/br alone. - Audit-009 cluster (renderer plateau): functions newly visible
include
0x823BC9E0,0x823BC290,0x823BC5A0,0x823BB158,0x823BB1E0,0x823BCAF0,0x823BC4C8— actionable starting points for the cluster's reachability hunt.
Reference docs
- IBM PowerPC ABI (volatile/non-volatile register partition).
- Itanium C++ ABI on vtable layout (offset-from-
thismodel adapted by MSVC for Win32 PPC).
Layer M9.5 — __CxxFrameHandler scope-table parsing (landed)
Schema additions
- New table
eh_funcinfo(address PK, magic, max_state, p_unwind_map, n_try_blocks, p_try_block_map, n_ip_map_entries, p_ip_to_state_map, p_es_type_list, eh_flags). - New table
eh_unwind_map(funcinfo_address, state_index, to_state, action_pc, PRIMARY KEY (funcinfo_address, state_index)). - New table
eh_try_blocks(funcinfo_address, try_index, try_low, try_high, catch_high, n_catches, p_handler_array, PRIMARY KEY (funcinfo_address, try_index)).
What this layer does
- Magic-scans
.rdatafor the documented MSVC FuncInfo signatures (0x19930520 / 0x19930521 / 0x19930522), reading 4-byte BE values on 4-byte alignment. - Sanity-checks
max_state≤ 10,000,n_try_blocks≤ 1,000, all internal pointers landing in valid sections. - Walks
pUnwindMap(8-byte UnwindMapEntry) andpTryBlockMap(20-byte TryBlockMapEntry) into one row each.
What this layer does NOT do
- Does not associate FuncInfo records with their owning function via
the
bl __CxxFrameHandlerregistration site — joins tofunctionsby best-effort PC-range queries. A future M9.6 can chase the registration to make the link explicit. - Does not parse
pHandlerArray(per-try-block catch type info).
Sylpheed yield
- 2,588 FuncInfo records (all version 0x19930522).
- 10,019 unwind-map entries.
- 315 try-blocks across the binary.
Layer M11.5 — Static-init driver chain detection (landed)
Schema additions
- Reuses existing
function_pointer_arraystable — drivers' arrays are emitted withkind='static_init', replacing M11's prologue-heuristic output where the structurally-grounded pattern fires.
What this layer does
- Walks every detected function looking for the canonical
_initterm- style loop:lwz cursor; mtctr; bcctrl; addi cursor, cursor, 4bounded by a comparison against another constant register. - Extracts
(array_start, array_end)from the cursor's initial constant value and the end-comparand register. - Reads the array, validates each entry against
func_analysis.functions, and emits the array asstatic_init.
What this layer does NOT do
- Doesn't handle drivers with multiple back-to-back trampoline loops.
- Doesn't follow
_initterm_ereturn-status semantics — both_inittermand_initterm_ematch if the loop body matches.
Sylpheed yield
- 0 drivers detected. Sylpheed's static-init structure does not match the canonical CRT loop pattern; the binary likely calls ctors via another mechanism (inline at the entry point, or via a different driver shape). Infrastructure ready for any binary with the documented MSVC pattern.
Layer VMX — Vector-store xrefs (M6 follow-up, landed)
Extends the M6 X-form opcode-31 dispatch in xref.rs with AltiVec/VMX
vector loads and stores. New entries (XO codes):
lvx(103),lvxl(359),lvebx(7),lvehx(39),lvewx(71) —addr_mode='x_form_indexed',kind='read'.stvx(231),stvxl(487),stvebx(135),stvehx(167),stvewx(199) —addr_mode='x_form_indexed',kind='write'.
Same constraint as M6: rows emitted only when both rA and rB
resolve to known constants (rare but useful).
Sylpheed yield
- 110
stvxwrites newly resolved.
Layer SJIS+UTF-8 — Localised-string detection (M7 follow-up, landed)
Extends xenia_analysis::strings::analyze with two additional scanners.
Shift_JIS detection
Per JIS X 0208: lead byte ∈ [0x81, 0x9F] ∪ [0xE0, 0xEF];
trail byte ∈ [0x40, 0x7E] ∪ [0x80, 0xFC]. Single-byte ASCII and JIS
half-width katakana (0xA1..=0xDF) are passed through. At least one
multi-byte pair must be present (so we don't double-count pure ASCII).
SJIS bytes are rendered as \\xHH escapes in the content column for
diagnostic readability — full SJIS→UTF-8 decoding is a future
enhancement.
UTF-8 detection
Validates 2-byte (110xxxxx 10xxxxxx) and 3-byte
(1110xxxx 10xxxxxx 10xxxxxx) sequences plus printable ASCII. Skips
4-byte (supplementary plane) which is rare in game text.
Sylpheed yield
- 790 Shift_JIS strings (Japanese debug + UI text, including
[WARNING] ノードに割り当てるエフェクトIDの指定がない ノードデータが見つからないstyle mission strings). - 39 UTF-8 strings.
- 6,311 ASCII strings (unchanged from M7).
Forward work (not yet landed)
- M9.6 — link
eh_funcinforecords back to their owning functions viabl __CxxFrameHandlerregistration sites + per-try-blockpHandlerArrayparsing. - M11.6 — relax M11.5 to detect non-canonical static-init driver
shapes (
_initterm_ewith status return, custom drivers). - Full SJIS → UTF-8 decoding in the
strings.contentcolumn. - VMX128 (opcode 4) vector-store xrefs — separate encoding space, low ROI; document if Sylpheed's renderer cluster uses it.