diff --git a/crates/sylpheed-ppc/examples/decode_table_check.rs b/crates/sylpheed-ppc/examples/decode_table_check.rs index b7928310..d7210813 100644 --- a/crates/sylpheed-ppc/examples/decode_table_check.rs +++ b/crates/sylpheed-ppc/examples/decode_table_check.rs @@ -6,7 +6,7 @@ //! or a mis-decode in our tables. //! //! ```text -//! cargo run --release -p xenia-cpu --example decode_table_check -- +//! cargo run --release -p sylpheed-ppc --example decode_table_check -- //! ``` //! where each line is `0xWORD name`. use std::io::BufRead; diff --git a/crates/sylpheed-viewer/src/iso_loader.rs b/crates/sylpheed-viewer/src/iso_loader.rs index 94442e65..fc9f23d0 100644 --- a/crates/sylpheed-viewer/src/iso_loader.rs +++ b/crates/sylpheed-viewer/src/iso_loader.rs @@ -5542,12 +5542,16 @@ mod font_sample_tests { /// panicking (regression for the egui `set_fonts` crash). #[test] fn real_font_rasterizes() { - let pak = Path::new("/tmp/sylph_extract/dat/movie/eng.pak"); + let Some(disc) = std::env::var_os("SYLPHEED_DISC") else { + eprintln!("SKIP: SYLPHEED_DISC not set"); + return; + }; + let pak = Path::new(&disc).join("dat/movie/eng.pak"); if !pak.exists() { - eprintln!("SKIP: extract not present"); + eprintln!("SKIP: {} not present", pak.display()); return; } - let arc = sylpheed_formats::PakArchive::open(pak).unwrap(); + let arc = sylpheed_formats::PakArchive::open(&pak).unwrap(); let bytes = arc.read_by_hash(0x5cd0_fca6).unwrap().unwrap(); assert!(sylpheed_formats::font::is_font(&bytes)); let img = render_font_sample(&bytes, "The quick brown fox 0123", 34.0) diff --git a/crates/sylpheed-xex/src/header.rs b/crates/sylpheed-xex/src/header.rs index a52d5d13..f56cc48d 100644 --- a/crates/sylpheed-xex/src/header.rs +++ b/crates/sylpheed-xex/src/header.rs @@ -32,7 +32,7 @@ pub struct Xex2SecurityInfo { pub load_address: u32, pub export_table_address: u32, pub image_flags: u32, - /// Encrypted session key (decrypted with retail/devkit key to get actual session key). + /// Encrypted session key (decrypted with the retail key to get the actual session key). pub aes_key: [u8; 16], pub page_descriptors: Vec, } diff --git a/crates/sylpheed-xex/src/loader.rs b/crates/sylpheed-xex/src/loader.rs index 4d86afbc..839e4c9a 100644 --- a/crates/sylpheed-xex/src/loader.rs +++ b/crates/sylpheed-xex/src/loader.rs @@ -493,14 +493,13 @@ fn load_basic_compressed(source: &[u8], info: &FileFormatInfo) -> io::Result Vec { @@ -528,7 +527,6 @@ fn aes_decrypt_cbc(key: &[u8; 16], input: &[u8]) -> Vec { } /// Derive the session key by decrypting the XEX's aes_key field with the retail key. -/// Falls back to devkit key if retail produces invalid results. fn derive_session_key(header: &Xex2Header) -> [u8; 16] { let sec = match &header.security_info { Some(s) => s, diff --git a/crates/sylpheed-xexdb/tests/disasm_goldens.rs b/crates/sylpheed-xexdb/tests/disasm_goldens.rs index edb3a2c1..ba9b71fa 100644 --- a/crates/sylpheed-xexdb/tests/disasm_goldens.rs +++ b/crates/sylpheed-xexdb/tests/disasm_goldens.rs @@ -1,10 +1,10 @@ -//! Analysis-side goldens: every row in the xenia-cpu fixtures must +//! Analysis-side goldens: every row in the sylpheed-ppc fixtures must //! round-trip cleanly through the [`sylpheed_xexdb::ppc`] shim. This //! pins the shim's behaviour to the canonical `sylpheed_ppc::disasm::format` //! output so that any future refactor of the shim layer surfaces here. //! //! Loads the same JSON fixtures committed under -//! `crates/xenia-cpu/tests/golden/`. No separate analysis-side fixture +//! `crates/sylpheed-ppc/tests/golden/`. No separate analysis-side fixture //! files — the cpu canon is the source of truth. use std::path::PathBuf; diff --git a/docs/port/AUDIO-VERIFICATION.md b/docs/port/AUDIO-VERIFICATION.md index 0599ca00..f4dbc810 100644 --- a/docs/port/AUDIO-VERIFICATION.md +++ b/docs/port/AUDIO-VERIFICATION.md @@ -60,7 +60,7 @@ dialogue folds into L/R, so this changes how speech sits against music — an aesthetic judgement, not a container detail. Pin it explicitly and record it, exactly as MISSION §6 requires of the transcode command itself. -[mac]: https://git.mc02.dev/fabi/Syplheed-Reborn/src/branch/main/docs/re/structures/movie-audio-channels.md +[mac]: ../re/structures/movie-audio-channels.md ## 2. Engine routing — Godot writes a WAV instead of a device diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index da7785af..d2b0a955 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -3579,8 +3579,8 @@ matches build 4. The conclusions are unchanged, the indices are not. **So the next step is the guest code**, not the file: the splash draw path from the emulator-era work (`sub_821CC7A0`, item vtable `0x820b30b4`) submits with -exactly the PS hash `E59B2B3D` this capture sees, and `xenia-rs/sylpheed.db` is -available in the container. +exactly the PS hash `E59B2B3D` this capture sees, and `sylpheed.db` (repository +root, queried with `tools/zq.py`) is available. **And a second screen is NO LONGER BLOCKED, but it is not routine either.** The main menu has been reached (screenshot in diff --git a/docs/re/README.md b/docs/re/README.md index de4513da..9d0f7398 100644 --- a/docs/re/README.md +++ b/docs/re/README.md @@ -101,13 +101,14 @@ XEX load, identical across our emulator and canary. `--dump-addr`) went with that emulator when it was retired on 2026-09-16. For static `.rdata` reads that `--dump-addr` served, read the `.pe` directly: it is a flat VA dump, file offset = `VA − 0x82000000`. -- **Oracle (correctness ground truth):** canary — the **Wine cross-build** - `xenia-canary/build-cross/bin/Windows/Debug/xenia_canary.exe` (the native Linux ELF - **crashes / does not run** — do not use it). This is the only emulator that reaches the - in-game menu; our retired `xenia-rs` never got past the intro video. Use canary to *observe output* - (capture its framebuffer for texture colours), not usually to instrument code — though its - `build-cross` toolchain does compile, so small C++ probes + rebuild are possible when needed. - Run **muted, one emulator process at a time**, point it at the real ISO (not the symlink). +- **Oracle (correctness ground truth):** canary, in either of two builds — the **Wine + cross-build** in `xenia-canary/build-cross/bin/Windows/Debug/` (launched by + `run-canary-safe.sh`) or the **native Linux build** in `xenia-canary-native/` (launched by + `run-canary-native-safe.sh`). This is the only emulator that reaches the in-game menu; our + retired `xenia-rs` never got past the intro video. Use canary to *observe output* + (capture its framebuffer for texture colours), not usually to instrument code — though both + builds compile, so small C++ probes + rebuild are possible when needed. + Run **muted, one emulator process at a time**, point it at the real ISO. > ⚠️ **VA-equality caveat:** join **code** by PC (fixed), but **never** assume a data VA > holds the same bytes across emulators — allocators differ. Compare data by content/layout. diff --git a/docs/re/RE_SYMBOLS.md b/docs/re/RE_SYMBOLS.md index a69dcb85..cfc6251c 100644 --- a/docs/re/RE_SYMBOLS.md +++ b/docs/re/RE_SYMBOLS.md @@ -115,7 +115,7 @@ extraction limit. field registrations (`name → offset`) and default-init stores (`offset → value`), join them — heavy, and offset↔name correlation is error-prone. (B, runtime) dump a loaded craft object's registry - (`obj+64`) / struct from xenia-rs or Canary — a fully-loaded craft already + (`obj+64`) / struct from Canary — a fully-loaded craft already holds every default; one dump yields `name → value`. **B is the efficient finish** given the framework is generic; A's framework map above is the prerequisite either way. diff --git a/docs/re/mission-freeze-resume-spin.md b/docs/re/mission-freeze-resume-spin.md index 65f1a1c6..4a5ba055 100644 --- a/docs/re/mission-freeze-resume-spin.md +++ b/docs/re/mission-freeze-resume-spin.md @@ -172,7 +172,7 @@ in guest memory that never changes. The question is now narrow: **which guest PC is the spinning thread executing?** Canary knows every `XThread`'s PPC context, so a diagnostic that dumps each thread's guest PC on demand (or after N seconds without a frame) would name the -loop, and `xenia-rs/sylpheed.db` can then say what function it is in. That is a +loop, and `sylpheed.db` (`tools/zq.py fn `) can then say what function it is in. That is a `build-canary` run plus a reproduction — the cost is worth stating up front, and it is the only avenue that does not involve guessing. diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index cd6cdb4a..0f1dccff 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -1251,7 +1251,7 @@ and it is worth recording rather than re-attempting the same way. (`%rsi` here, given `mov 0x110(%rsi),%rbx`), so the **guest PC is recoverable from the context block** at the moment of the write. Reading the right offset out of `$rsi` would name the guest instruction. That needs Xenia's context layout — -which is in the xenia-rs sources on this box — and is a separate, tractable +`PPCContext` in Canary's `src/xenia/cpu/ppc/ppc_context.h` — and is a separate, tractable piece of work rather than another blind run. ## 🟡 `sub_8226E458` is a splice — but I have not shown it touches the trigger queue diff --git a/docs/re/structures/regn-map-grid.md b/docs/re/structures/regn-map-grid.md index d426acb3..2d0d346a 100644 --- a/docs/re/structures/regn-map-grid.md +++ b/docs/re/structures/regn-map-grid.md @@ -379,7 +379,7 @@ nothing rises above chance. ❔ **What would actually settle it** is the code — find what reads a `REGN` object in the executable and watch which fields it dereferences. That is static -PE work (`/work/*.pe`, offset = VA − 0x82000000) of the same kind that cracked +PE work (the flat `.pe` in the project root, offset = VA − 0x82000000) of the same kind that cracked the `.slb` packing phase, and it is the honest next step rather than a twenty-first correlation. --- diff --git a/docs/re/structures/slb-data-offset.md b/docs/re/structures/slb-data-offset.md index 3e667faa..1a4efd0b 100644 --- a/docs/re/structures/slb-data-offset.md +++ b/docs/re/structures/slb-data-offset.md @@ -322,7 +322,7 @@ self-describing. `game:\dat\sound.pak+` and `SETTINGS.PARAM` is `Pj_Silph.xgs`, so there is code that opens a bank by name and seeks to its data; the constant, or the table it indexes, should be visible there. That is static PE work -(`/work/*.pe`, offset = VA − 0x82000000), not another pass over the archive — +(the flat `.pe` in the project root, offset = VA − 0x82000000), not another pass over the archive — this page has taken the byte-level evidence about as far as it goes. diff --git a/docs/re/ui-title-paint-order-capture.md b/docs/re/ui-title-paint-order-capture.md index 65bad6d1..ea5d3072 100644 --- a/docs/re/ui-title-paint-order-capture.md +++ b/docs/re/ui-title-paint-order-capture.md @@ -244,7 +244,7 @@ that gets adopted as a rule if only the agreeing five are counted. ## A foothold in the guest code, and what it is not -The remaining avenue is the code, and `xenia-rs/sylpheed.db` is in the container. +The remaining avenue is the code, and `sylpheed.db` is in the repository root (`tools/zq.py`). What is established so far is small, and stated so it is not mistaken for more: * **The item class from the splash-era work is real.** Vtable `0x820b30b4` is diff --git a/tools/extract_movie_subtitles.py b/tools/extract_movie_subtitles.py index f6eaabe8..805f25ca 100644 --- a/tools/extract_movie_subtitles.py +++ b/tools/extract_movie_subtitles.py @@ -16,7 +16,7 @@ def nh(name): a=(a-(q*MOD))&0xFFFFFFFF return (((bb<<24)&0xFF000000)|(a&0xFFFFFF))&0xFFFFFFFF -EX="/home/fabi/RE Project Sylpheed/sylph_extract" +EX = os.environ.get("SYLPHEED_DISC") or exit("set SYLPHEED_DISC to the extracted disc (the directory holding dat/)") def load_pak(base): """base like dat/movie/eng ; returns list of (hash,off,size), data bytes""" pak=open(f"{EX}/{base}.pak","rb").read() diff --git a/tools/re-capture/aiparams_census.py b/tools/re-capture/aiparams_census.py index 09f06cc9..25adedb2 100644 --- a/tools/re-capture/aiparams_census.py +++ b/tools/re-capture/aiparams_census.py @@ -7,6 +7,7 @@ whether the 34-name roster is shared, and whether `Type` really predicts the field count. Regenerates docs/re/data/aiparams-census.txt. """ import sys, os, glob, collections +from disc import disc_root HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) @@ -32,7 +33,7 @@ def objects(pak): return out def main(): - paks = sorted(p for p in glob.glob('/work/sylph_extract/**/*.pak', recursive=True) + paks = sorted(p for p in glob.glob(disc_root() + '/**/*.pak', recursive=True) if os.path.basename(p).startswith('GP_MAIN_GAME_') and '2D' not in os.path.basename(p)) print("# AIParams across the whole disc") diff --git a/tools/re-capture/archive_naming.py b/tools/re-capture/archive_naming.py index 25709399..87903fee 100644 --- a/tools/re-capture/archive_naming.py +++ b/tools/re-capture/archive_naming.py @@ -6,6 +6,7 @@ the known path prefixes, and reports per archive how many of its entries are explained. Regenerates docs/re/data/archive-naming.txt. """ import sys, os, glob, re, collections +from disc import disc_root HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) @@ -17,7 +18,7 @@ PRE = ['', '2d\\', 'ui\\', 'hud\\', 'Stage\\', 'stage\\', 'message\\', 'language 'prt\\', 'view\\'] + [l + '\\' for l in ('eng', 'jpn', 'fra', 'deu', 'ita', 'esp')] def main(): - paks = sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)) + paks = sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)) names = set() decl = set() nenum = collections.Counter() diff --git a/tools/re-capture/arsenal_chain.py b/tools/re-capture/arsenal_chain.py index 767e6eca..5fdd0093 100644 --- a/tools/re-capture/arsenal_chain.py +++ b/tools/re-capture/arsenal_chain.py @@ -7,6 +7,7 @@ what points into the 131-record `Weapon` datasheet. Regenerates docs/re/data/arsenal-chain.txt. """ import sys, os, glob, re, collections +from disc import disc_root HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) @@ -17,8 +18,8 @@ ITEMF = {'Dependency', 'MissionObjective', 'Model', 'Package', 'PlayerWeapon', 'Points', 'Power', 'Range'} def main(): - ars = glob.glob('/work/sylph_extract/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0] - mg = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0] + ars = glob.glob(disc_root() + '/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0] + mg = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E.pak', recursive=True)[0] slots = collections.defaultdict(dict); wids = set() for h, b in pak_entries(mg): diff --git a/tools/re-capture/bgm_wave_census.py b/tools/re-capture/bgm_wave_census.py index 76db133a..057e41aa 100755 --- a/tools/re-capture/bgm_wave_census.py +++ b/tools/re-capture/bgm_wave_census.py @@ -2,10 +2,12 @@ """Every BGM bank's sub-wave structure: count, bytes, PsuedoBytesPerSec, seconds. Static, disc-only. See docs/re/structures/bgm-two-stems.md.""" -import sys, struct -sys.path.insert(0,"/work/Syplheed-Reborn/tools/re-capture") -exec(open("/work/Syplheed-Reborn/tools/re-capture/slb_segment_phase.py").read().split("def cmd_phases")[0]) -pak=Pak("/work/sylph_extract/dat/sound.pak") +import os, sys, struct +from disc import disc_root +_SD = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, _SD) +exec(open(os.path.join(_SD, "slb_segment_phase.py")).read().split("def cmd_phases")[0]) +pak=Pak(disc_root() + "/dat/sound.pak") def bps(b, riff_at): # PsuedoBytesPerSec at RIFF+0x20 (u32 LE), SampleRate at +0x24 return struct.unpack_from("