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/agents/CONSOLIDATION.md b/docs/agents/CONSOLIDATION.md
index 0b8ecf07..03b57bd0 100644
--- a/docs/agents/CONSOLIDATION.md
+++ b/docs/agents/CONSOLIDATION.md
@@ -27,6 +27,30 @@ that is the correct reading: **each unresolved path here is work outstanding.**
---
+## ✅ Closed 2026-09-16
+
+**The end state is reached: two live repositories.** `Syplheed-Reborn`,
+`Sylpheed-Godot`, `xenia-rs` and `xex2tractor` are archived read-only on Gitea
+(by the human, 2026-09-16), not deleted. The harvests are on `main`: the DuckDB
+tool (`crates/sylpheed-xexdb`, #32), the import-thunk naming (#35, re-landed as
+#39), the xex2tractor reference files (#30) and the orphan assets (#34). The last
+reference files from the project root follow in #46. `sylpheed.db` lives in this
+repository's root and was rebuilt from scratch.
+
+| phase | outcome |
+|---|---|
+| **5** archive | ✅ the four repos archived; `xenia-rs` and `sylpheed-reborn` clones deleted from this machine (the other two never had one here) |
+| **7** drops | ✅ `Sylpheed/target/` deleted; `agent-backups/`, `stock-oracle/`, `texcompare/`, `ship_render/`, the `pi/*` branches and the root Canary logs are not on this machine |
+| fork | ✅ Canary's default branch is `sylpheed-re`, no longer `xenia-rs` |
+
+Still the human's, unchanged: the **retention** question (all repositories are
+public; `docs/re/captures/` holds game screenshots) and the **history rewrite**,
+recommended against in Phase 5.
+
+The rest of this page is the working record as it stood during the move. Its
+paths into the retired repositories no longer resolve because those clones are
+gone, not because work is outstanding.
+
## Where things stand
| repo | server | local tree | tracked | disposition |
@@ -127,7 +151,7 @@ Four things exist only there.
| take | why |
|---|---|
-| **XEX2 devkit + XEX1 retail master keys** | `xenia-xex` hardcodes retail only; devkit is present but `#[allow(dead_code)]` and XEX1 is absent. xex2tractor tries all three in a validating loop. |
+| ~~**XEX2 devkit + XEX1 retail master keys**~~ | `xenia-xex` hardcodes retail only; devkit is present but `#[allow(dead_code)]` and XEX1 is absent. xex2tractor tries all three in a validating loop. ✅ **Not taken, by decision (human, 2026-09-16):** Project Sylpheed is a retail XEX2, so the retail key is the only one needed — the dead devkit key was removed from `sylpheed-xex` instead. |
| ~~**`extract -r`**~~ | 🔴 **RUN, AND THE INFERENCE WAS WRONG** — see the gate below. Optional, and dangerous if it is ever mistaken for the `.pe`. |
| **`doc/xex2_format.md`** (39 KB) and **`doc/xbox360_exports.json`** (938 KB, 2,913 exports across xboxkrnl / xam / xbdm) | byte-identical to untracked loose copies in the project root. Tracked by **no repo**. |
| **`LICENSE`** (MIT) | Sylpheed has none. |
@@ -413,7 +437,7 @@ citations" turn out to be one.)
is a deletion on the human's disk, and rebuilding it is hours, so it stays until
asked for.
-## Phase 5 — Verify the invariant, then archive ⬜ **NOT STARTED**
+## Phase 5 — Verify the invariant, then archive ✅ **DONE 2026-09-16**
For each of `Syplheed-Reborn`, `Sylpheed-Godot`, `xenia-rs`, `xex2tractor`:
re-run the containment check **at that moment** — not from this page's ledger,
@@ -472,7 +496,7 @@ Tracked by nobody today, and tools or references by the two-repo rule:
| `scratch/xbg7/extract.py` — clean-room XBG7 scanner | superseded by `sylpheed_formats::mesh`; keep as history or drop |
| `Sylpheed-try/…/examples/_voice_span.rs` — 46-line untracked probe | keep or drop |
-## Phase 7 — Intended drops ⬜ **NOT STARTED**
+## Phase 7 — Intended drops ✅ **DONE 2026-09-16**
Stated so that nothing is dropped by omission.
@@ -490,7 +514,7 @@ Stated so that nothing is dropped by omission.
---
-# ▶️ STATUS 2026-09-13 — paused here, for the other machine to finish
+# STATUS 2026-09-13 — paused here, for the other machine to finish (superseded: see ✅ Closed 2026-09-16 at the top)
**Six of eight phases are done and pushed.** Everything below is committed;
nothing is left in a working tree. Five pull requests are open and **none has
@@ -506,8 +530,8 @@ been merged** — merging is the human's, and #32 contains #30 and #31.
| **4** captures + the missing check | ✅ done | **PR #33** `fix/capture-citations` |
| **6** adopt the orphan assets | ✅ done | **PR #34** `chore/adopt-orphan-assets`, + Canary `99cc72662` |
| — containers and agents | ✅ done | **PR #31** `docs/containers-setup` |
-| **5** archive the four repos | ⬜ **open — needs the human** | |
-| **7** intended drops | ⬜ **open — partly needs the human** | |
+| **5** archive the four repos | ✅ done 2026-09-16 | archived by the human |
+| **7** intended drops | ✅ done 2026-09-16 | |
## 🔴 Five claims on this page were wrong. Read these before trusting the rest.
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("I", stub, 4)[0]
blob = b"".join(open(s, "rb").read() for s in sorted(glob.glob(base + ".p[0-9][0-9]")))
diff --git a/tools/re-capture/eff_bit_census.py b/tools/re-capture/eff_bit_census.py
index 1149a8be..b1f61e90 100755
--- a/tools/re-capture/eff_bit_census.py
+++ b/tools/re-capture/eff_bit_census.py
@@ -7,6 +7,7 @@ preceding name is the ELEMENT's (opt) name and the child list is the SPRITE's.
This test uses the element name and says so.
"""
import struct, zlib, glob, os, re, collections
+from disc import disc_root
NAME = re.compile(rb'[A-Za-z0-9_.]{2,31}\x00')
def entries(base):
@@ -25,7 +26,7 @@ def entries(base):
set_eff = set_noneff = clear_eff = clear_noneff = 0
examples = []
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for d in entries(pak[:-4]):
for m in re.finditer(b"T8aD", d):
o = m.start()
diff --git a/tools/re-capture/effect_homes.py b/tools/re-capture/effect_homes.py
index f86d4eb9..4ba255cb 100644
--- a/tools/re-capture/effect_homes.py
+++ b/tools/re-capture/effect_homes.py
@@ -12,11 +12,12 @@ prefix.
Regenerates docs/re/data/effect-homes.txt.
"""
import glob, os, re, subprocess, sys, collections
+from disc import disc_root
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from unit_substructures import pak_entries
import unitgroup as U
-XPR = '/work/sylph_extract/hidden/resource3d'
+XPR = disc_root() + '/hidden/resource3d'
FX = re.compile(r'(FxModel|EffectName|Effect_|ShellModel|CoverModel)')
def width(n):
@@ -34,7 +35,7 @@ def main():
home[name].add(os.path.basename(path))
bound = set()
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
for _h, b in pak_entries(pk):
if b[:4] != b'IDXD':
continue
diff --git a/tools/re-capture/focus_from_capture.py b/tools/re-capture/focus_from_capture.py
index 82e2b64f..bc5f0435 100755
--- a/tools/re-capture/focus_from_capture.py
+++ b/tools/re-capture/focus_from_capture.py
@@ -35,7 +35,7 @@ import sys
import numpy as np
from PIL import Image
-CAP = "/work/docs/re/captures/title-builds"
+CAP = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "docs", "re", "captures", "title-builds"))
REF_A = f"{CAP}/live-main-menu.png" # NEW GAME focused
REF_B = f"{CAP}/live-main-menu-options-focused.png" # OPTIONS focused
BUTTONS = ["NEW GAME", "LOAD GAME", "TUTORIAL", "OPTIONS", "EXTRAS"]
diff --git a/tools/re-capture/generic_partition.py b/tools/re-capture/generic_partition.py
index 4f9f0af1..9c98a8fc 100644
--- a/tools/re-capture/generic_partition.py
+++ b/tools/re-capture/generic_partition.py
@@ -1,13 +1,14 @@
-import sys, glob, collections
-S="/tmp/claude-1000/-home-fabi-RE-Project-Sylpheed/b113cc12-4769-4ed8-ad66-c2b48f800773/scratchpad"
-sys.path.insert(0,S+"/wt-root/Syplheed-Reborn/tools/re-capture")
-exec(open(S+"/regn_decode.py").read().split("# ── the object")[0])
+import os, sys, glob, collections
+from disc import disc_root
+_SD = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, _SD)
+exec(open(os.path.join(_SD, "regn_decode.py")).read().split("# ── the object")[0])
import unitgroup as U
print("# `Generic` is the per-FILE header record, not a table")
print("#")
print("# One `Generic` per IDXD pak entry; its schema is set by the file's TYPE.")
print("# Regenerate: see docs/re/structures/unit-datasheet-static.md")
-for pk in sorted(glob.glob("/work/sylph_extract/**/GP_MAIN_GAME_*.pak",recursive=True)):
+for pk in sorted(glob.glob(disc_root() + "/**/GP_MAIN_GAME_*.pak",recursive=True)):
E=pak_entries(pk); items=E.items() if isinstance(E,dict) else E
kinds=collections.Counter(); ids=set(); types=collections.Counter()
nidxd=0; man=eff=0; ctl_ok=ctl_bad=0; one_per=True
diff --git a/tools/re-capture/hangar_loadouts.py b/tools/re-capture/hangar_loadouts.py
index 6f0279eb..75786271 100644
--- a/tools/re-capture/hangar_loadouts.py
+++ b/tools/re-capture/hangar_loadouts.py
@@ -6,6 +6,7 @@ per-slot ALLOW-LIST record whose positional (unnamed) fields are the ordered
candidate item names. Regenerates docs/re/data/hangar-loadouts.txt.
"""
import sys, os, glob, collections
+from disc import disc_root
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
@@ -17,10 +18,10 @@ ITEMF = {'Dependency', 'MissionObjective', 'Model', 'Package',
SLOTS = ('Arm1', 'Arm2', 'Arm3', 'Nose')
def main():
- ars = glob.glob('/work/sylph_extract/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0]
+ ars = glob.glob(disc_root() + '/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0]
unit_ids, char_ids = set(), set()
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
for h, b in pak_entries(pk):
if b[:4] != b'IDXD': continue
try: recs = U.parse(b)
diff --git a/tools/re-capture/hud_config.py b/tools/re-capture/hud_config.py
index e8b85f5d..c62fa5e7 100644
--- a/tools/re-capture/hud_config.py
+++ b/tools/re-capture/hud_config.py
@@ -4,6 +4,7 @@
Regenerates docs/re/data/hud-config.txt.
"""
import sys, os, glob, collections
+from disc import disc_root
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
@@ -14,10 +15,10 @@ PRE = ['', '2d\\', 'ui\\', 'hud\\'] + [l + '\\' for l in ('eng', 'jpn', 'fra', '
DEFAULT = ('HudResource.tbl', 'HudMarkerResource.tbl')
def main():
- p2 = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E2D.pak', recursive=True)[0]
+ p2 = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E2D.pak', recursive=True)[0]
E = dict(pak_entries(p2))
H = set()
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
for h, b in pak_entries(pk):
H.add(h)
@@ -75,7 +76,7 @@ def main():
print(" %-30s %s" % (sq, " ".join("%s=%s" % (k, v) for k, v in sorted(n.items()))))
print("\n## ResourceTable -- 29 pairs, one per stage, with a single override")
- for pk in sorted(glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_*2D.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/GP_MAIN_GAME_*2D.pak', recursive=True)):
for h, b in pak_entries(pk):
if b[:4] != b'IDXD': continue
for r in U.parse(b):
diff --git a/tools/re-capture/idxd_unnamed_keys.py b/tools/re-capture/idxd_unnamed_keys.py
index 6fb9b31c..89e13dea 100644
--- a/tools/re-capture/idxd_unnamed_keys.py
+++ b/tools/re-capture/idxd_unnamed_keys.py
@@ -20,11 +20,12 @@ The headline measurement (see docs/re/structures/idxd-unnamed-keys.md):
7 052 of them are < 0x10000 -- author-assigned ordinals / element ids
42 of them are >= 0x01000000 -- genuine tag_hash values
-`SYLPHEED_DISC` (default /work/sylph_extract) points at the extracted disc.
+`SYLPHEED_DISC` (required) points at the extracted disc.
"""
import argparse, collections, glob, os, re, struct, sys, zlib
+from disc import disc_root
-DISC = os.environ.get('SYLPHEED_DISC', '/work/sylph_extract')
+DISC = disc_root()
# --------------------------------------------------------------------------- hashes
diff --git a/tools/re-capture/isl_dialogue.py b/tools/re-capture/isl_dialogue.py
index b2188e33..5cf723c5 100644
--- a/tools/re-capture/isl_dialogue.py
+++ b/tools/re-capture/isl_dialogue.py
@@ -10,6 +10,7 @@ across the 28 stages resolve, so this is a total mapping rather than a sample. 3
message names span more than one page; every page is printed.
"""
import collections
+from disc import disc_root
import struct
import sys
@@ -86,7 +87,7 @@ def walk(b, base, nxt, sink):
def main():
ssb = sys.argv[1]
pak = sys.argv[2] if len(sys.argv) > 2 else \
- '/work/sylph_extract/dat/GP_MAIN_GAME_E.pak'
+ disc_root() + '/dat/GP_MAIN_GAME_E.pak'
sys.path.insert(0, sys.path[0])
from unitgroup import read_entry # noqa: F401 (kept for parity)
import zlib
diff --git a/tools/re-capture/kf_rotation_census.py b/tools/re-capture/kf_rotation_census.py
index 5b9b5b53..182871c3 100644
--- a/tools/re-capture/kf_rotation_census.py
+++ b/tools/re-capture/kf_rotation_census.py
@@ -17,6 +17,7 @@ The filter keys on SHAPE, not on a count: >=2 consecutive blocks whose fade is
0x??ffffff, whose tint is 0xffffffff, and whose scale words are 1..4000.
"""
import struct, zlib, glob, os, sys, collections, re
+from disc import disc_root
def entries(base):
stub=open(base+'.pak','rb').read()
@@ -65,7 +66,7 @@ if not all(True for _ in [0]): sys.exit(1)
hist=collections.Counter(); nz=collections.Counter(); total=0
examples=collections.defaultdict(list)
-for pak in sorted(glob.glob('/work/sylph_extract/dat/GP_*.pak')):
+for pak in sorted(glob.glob(disc_root() + '/dat/GP_*.pak')):
base=pak[:-4]
for h,d in entries(base):
if b'RATC' not in d[:4] and d[:4]!=b'RATC': pass
diff --git a/tools/re-capture/main_game_unnamed.py b/tools/re-capture/main_game_unnamed.py
index 9755141b..95381adf 100644
--- a/tools/re-capture/main_game_unnamed.py
+++ b/tools/re-capture/main_game_unnamed.py
@@ -8,6 +8,7 @@ the two objects that belong to no documented family.
Regenerates docs/re/data/main-game-unnamed.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)
@@ -64,7 +65,7 @@ def classify(shape):
return None, None
def main():
- paks = sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True))
+ paks = sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True))
NAMES = name_map(paks)
mains = sorted(p for p in paks
if re.match(r'GP_MAIN_GAME_[A-Z]\.pak$', os.path.basename(p)))
diff --git a/tools/re-capture/mission_scoring.py b/tools/re-capture/mission_scoring.py
index 42c3e890..d845c763 100644
--- a/tools/re-capture/mission_scoring.py
+++ b/tools/re-capture/mission_scoring.py
@@ -8,11 +8,12 @@ language pack, each holding Score_Easy / Score_Normal / Score_Hard with one
Run: python3 mission_scoring.py > ../../docs/re/data/mission-scoring.txt
"""
import sys, os, re, glob, collections
+from disc import disc_root
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from unit_substructures import pak_entries
import unitgroup as U
-DAT = '/work/sylph_extract/dat'
+DAT = disc_root() + '/dat'
KEY = 'RankScore_S'
DIFFS = ('Score_Easy', 'Score_Normal', 'Score_Hard')
diff --git a/tools/re-capture/ob_scan.py b/tools/re-capture/ob_scan.py
index 8cf5bc91..3e98f717 100644
--- a/tools/re-capture/ob_scan.py
+++ b/tools/re-capture/ob_scan.py
@@ -24,7 +24,7 @@ Two traps this encodes, both paid for:
once it tracks a transition it was not selected by.
"""
import os, struct, sys
-sys.path.insert(0, "/work/Syplheed-Reborn/tools/re-capture")
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gmem
def scan(path, val):
diff --git a/tools/re-capture/paint_key_census.py b/tools/re-capture/paint_key_census.py
index e507e21e..11aab4b4 100755
--- a/tools/re-capture/paint_key_census.py
+++ b/tools/re-capture/paint_key_census.py
@@ -22,11 +22,12 @@ number before it did:
across six paks. They are collapsed into one family here.
"""
-import sys, glob, struct, re, collections
+import os, sys, glob, struct, re, collections
+from disc import disc_root
-S="/tmp/claude-1000/-home-fabi-RE-Project-Sylpheed/b113cc12-4769-4ed8-ad66-c2b48f800773/scratchpad"
-sys.path.insert(0,S+"/wt-root/Syplheed-Reborn/tools/re-capture")
-exec(open(S+"/regn_decode.py").read().split("# ── the object")[0])
+_SD = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, _SD)
+exec(open(os.path.join(_SD, "regn_decode.py")).read().split("# ── the object")[0])
Hh=lambda b,o: struct.unpack_from(">H",b,o)[0]
def fam(n): return re.sub(r'_[DEFIJS]2D\.pak$','_2D.pak',n)
keys=collections.Counter(); kfam=collections.defaultdict(set)
@@ -37,7 +38,7 @@ def take(b,o,base,lang):
if Hh(b,o+8)==0: hi_zero+=1
k=Hh(b,o+10); keys[k]+=1; kfam[k].add(base)
if lang: langsets[lang].add(k)
-ROOT = sys.argv[1] if len(sys.argv) > 1 else "/work/sylph_extract"
+ROOT = sys.argv[1] if len(sys.argv) > 1 else disc_root()
for f in sorted(glob.glob(ROOT + "/**/*.pak", recursive=True)):
try: E=pak_entries(f)
except Exception: continue
diff --git a/tools/re-capture/plateau_census.py b/tools/re-capture/plateau_census.py
index 804f6eec..370bba90 100644
--- a/tools/re-capture/plateau_census.py
+++ b/tools/re-capture/plateau_census.py
@@ -6,6 +6,7 @@ every texture). CONTROL: it must reproduce the three known fallback elements of
GP_TITLE build 7 (indices 8, 13, 14) and find ptlogo_eff3 among them.
"""
import struct, zlib, glob, os, collections
+from disc import disc_root
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
@@ -56,7 +57,7 @@ def has_plateau(poses):
# ---- CONTROL -------------------------------------------------------------
ctl = {i: (n, p) for i, n, p in elements(
- dict((i, d) for i, h, d in entries("/work/sylph_extract/dat/GP_TITLE"))[7])}
+ dict((i, d) for i, h, d in entries(disc_root() + "/dat/GP_TITLE"))[7])}
fb = sorted(i for i, (n, p) in ctl.items() if not has_plateau(p))
print(f"CONTROL GP_TITLE build 7: fallback elements {fb} (want [8, 13, 14])")
print(f" index 8 is {ctl[8][0]!r} (want ptlogo_eff3.t32)")
@@ -66,7 +67,7 @@ assert fb == [8, 13, 14] and ctl[8][0].startswith("ptlogo_eff3"), "control faile
tot = fb_n = 0
by_name = collections.Counter()
worst = []
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
base = pak[:-4]
for i, h, d in entries(base):
for idx, name, poses in elements(d):
@@ -119,7 +120,7 @@ def elements_t(d):
alpha = lambda p: (p[0] >> 24) & 0xff
stats = collections.Counter()
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for i, h, d in entries(pak[:-4]):
for idx, name, times, poses in elements_t(d):
if has_plateau(poses): continue
diff --git a/tools/re-capture/plateauless_endstate.py b/tools/re-capture/plateauless_endstate.py
index 80b819c4..fcd11c07 100755
--- a/tools/re-capture/plateauless_endstate.py
+++ b/tools/re-capture/plateauless_endstate.py
@@ -1,6 +1,7 @@
"""Plateau-less elements whose LAST keyframe is visible -- do they contradict
the entry->hold->exit model, or are they 'slides in and stops'?"""
import struct, zlib, glob, os, collections
+from disc import disc_root
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
def entries(base):
stub=open(base+".pak","rb").read()
@@ -41,7 +42,7 @@ def elements(d):
alpha=lambda q:(q[0]>>24)&0xff
mono=stuck=other=0
ex=[]
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for d in entries(pak[:-4]):
for nm,p in elements(d):
if len(p)==1: continue
diff --git a/tools/re-capture/preset_messages.py b/tools/re-capture/preset_messages.py
index 59130371..0651c8e9 100644
--- a/tools/re-capture/preset_messages.py
+++ b/tools/re-capture/preset_messages.py
@@ -9,11 +9,12 @@ in the layout cutscene-message-table.md already documents.
Run: python3 preset_messages.py > ../../docs/re/data/preset-messages.txt
"""
import sys, os, re, glob, collections
+from disc import disc_root
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from unit_substructures import pak_entries
import unitgroup as U
-DAT = '/work/sylph_extract/dat'
+DAT = disc_root() + '/dat'
PAK = os.path.join(DAT, 'GP_MAIN_GAME_E.pak')
MARKER = 'ORDOR_SQUADRON_EXTENDED'
STAGES = ['S%02d' % i for i in list(range(1, 17)) + list(range(18, 30))]
diff --git a/tools/re-capture/prt_parts.py b/tools/re-capture/prt_parts.py
index 015320d1..b10c00e3 100644
--- a/tools/re-capture/prt_parts.py
+++ b/tools/re-capture/prt_parts.py
@@ -4,6 +4,7 @@
Regenerates docs/re/data/prt-parts.txt.
"""
import sys, os, glob, re, struct, collections
+from disc import disc_root
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
@@ -33,7 +34,7 @@ def main():
elem2d = collections.defaultdict(set)
dirs = collections.Counter()
rat = collections.defaultdict(set)
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
base = os.path.basename(pk)
for h, b in pak_entries(pk):
H[h].add(base)
diff --git a/tools/re-capture/ratc_opt_name_census.py b/tools/re-capture/ratc_opt_name_census.py
index 2be41a70..187b7f2e 100644
--- a/tools/re-capture/ratc_opt_name_census.py
+++ b/tools/re-capture/ratc_opt_name_census.py
@@ -14,11 +14,12 @@ not depend on the Rust parser it is checking. Takes a few minutes.
"""
import struct, zlib, glob, os, collections, bisect
+from disc import disc_root
MAG = (b"T8aD", b"RATC", b"ttcf", b"\x89PNG")
ext = collections.Counter()
tan_sites = []
opt_total = 0
-DISC = os.environ.get("SYLPHEED_DISC", "/work/sylph_extract")
+DISC = disc_root()
for pakpath in sorted(glob.glob(f"{DISC}/dat/*.pak")):
base = pakpath[:-4]
pak = open(pakpath, "rb").read()
diff --git a/tools/re-capture/rest_rule_blast_radius.py b/tools/re-capture/rest_rule_blast_radius.py
index 1d6b22ea..e7b93e67 100755
--- a/tools/re-capture/rest_rule_blast_radius.py
+++ b/tools/re-capture/rest_rule_blast_radius.py
@@ -5,6 +5,7 @@ scored disc-wide. What CAN be quantified is the blast radius: how often the
rules disagree, and how often each returns a pose that draws nothing.
"""
import struct, zlib, glob, collections
+from disc import disc_root
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
def entries(base):
stub=open(base+".pak","rb").read()
@@ -48,7 +49,7 @@ def dwell(t,p):
alpha=lambda q:(q[0]>>24)&0xff
n=agree=0
inv=collections.Counter(); deg=collections.Counter()
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for d in entries(pak[:-4]):
for t,p in elements(d):
if len(p)==1: continue
diff --git a/tools/re-capture/rotation_toplevel_census.py b/tools/re-capture/rotation_toplevel_census.py
index 2b74b3cd..5d63b82a 100644
--- a/tools/re-capture/rotation_toplevel_census.py
+++ b/tools/re-capture/rotation_toplevel_census.py
@@ -21,6 +21,7 @@ the first version of this control did exactly that and reported 0 rotations for
screen that plainly has two. `screen list` prints the mapping.
"""
import struct, zlib, glob, os, sys, collections
+from disc import disc_root
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
def entries(base):
@@ -64,8 +65,8 @@ def rotated(d):
return out, total
# ---- CONTROL -------------------------------------------------------------
-t = dict(entries("/work/sylph_extract/dat/GP_TITLE"))
-g = dict(entries("/work/sylph_extract/dat/GP_DIALOG"))
+t = dict(entries(disc_root() + "/dat/GP_TITLE"))
+g = dict(entries(disc_root() + "/dat/GP_DIALOG"))
ct, _ = rotated(t[4]); cg, _ = rotated(g[2]) # build 0 == entry 2, see the note above
print(f"CONTROL GP_TITLE build 4 (nested rotations only): {len(ct)} top-level rotated (want 0)")
print(f"CONTROL GP_DIALOG build 0: {len(cg)} top-level rotated (want 2)")
@@ -75,7 +76,7 @@ assert len(ct) == 0 and len(cg) == 2, "control failed"
# ---- census --------------------------------------------------------------
tot = rot = 0
per_pak = collections.Counter(); names = collections.Counter()
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for i, d in entries(pak[:-4]):
r = rotated(d)
if not r: continue
diff --git a/tools/re-capture/route_liveness_probe.sh b/tools/re-capture/route_liveness_probe.sh
index 0002079d..32a56e23 100755
--- a/tools/re-capture/route_liveness_probe.sh
+++ b/tools/re-capture/route_liveness_probe.sh
@@ -4,7 +4,7 @@
set -u
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 XENIA_PAD_FILE=/tmp/xenia_pad.txt
export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages
-SD=/tmp/claude-1000/-home-fabi-RE-Project-Sylpheed/b113cc12-4769-4ed8-ad66-c2b48f800773/scratchpad/wt-root/Syplheed-Reborn/tools/re-capture
+SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export PATH="$SD/bin:$PATH"
OUT=/tmp/route; rm -rf $OUT; mkdir -p $OUT
pad(){ python3 "$SD/pad.py" "$@"; }
diff --git a/tools/re-capture/script_manifest.py b/tools/re-capture/script_manifest.py
index 4fd6912a..9d2ab93e 100644
--- a/tools/re-capture/script_manifest.py
+++ b/tools/re-capture/script_manifest.py
@@ -4,6 +4,7 @@
Regenerates docs/re/data/script-manifest.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)
@@ -11,7 +12,7 @@ import unitgroup as U
from unit_substructures import pak_entries
def main():
- mg = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
+ mg = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
E = dict(pak_entries(mg))
recs = U.parse(E[U.name_hash('Stage\\script.tbl')])
@@ -34,7 +35,7 @@ def main():
print(" %-22s %r" % (n, v))
idx = {}
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
idx[os.path.basename(pk)] = {h for h, b in pak_entries(pk)}
targets = ['MissionDialogMessage.tbl', 'MissionDialog_local_string.tbl',
'pgmsg_start.prt', 'pgmsg_end.prt', 'pgmsg_update.prt',
diff --git a/tools/re-capture/slb_extract_wave.py b/tools/re-capture/slb_extract_wave.py
index 21404bcf..b4e15e1e 100755
--- a/tools/re-capture/slb_extract_wave.py
+++ b/tools/re-capture/slb_extract_wave.py
@@ -14,6 +14,7 @@ See docs/re/menu-audio-cues.md.
Decode with: ffmpeg -i out.riff out.wav
"""
import os, struct, sys
+from disc import disc_root
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
exec(open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"slb_segment_phase.py")).read().split("def cmd_phases")[0])
@@ -41,7 +42,7 @@ def main():
ch = int(a[3]) if len(a) > 3 else 1
rate = int(a[4]) if len(a) > 4 else 48000
out = a[5] if len(a) > 5 else f"{name.split('.')[0]}_{off:#x}.riff"
- disc = os.environ.get("SYLPHEED_DISC", "/work/sylph_extract")
+ disc = disc_root()
pak = Pak(os.path.join(disc, "dat", "sound.pak"))
i = pak.find(name)
if i is None:
diff --git a/tools/re-capture/slb_segment_phase.py b/tools/re-capture/slb_segment_phase.py
index 87fcaacb..f981c58c 100755
--- a/tools/re-capture/slb_segment_phase.py
+++ b/tools/re-capture/slb_segment_phase.py
@@ -21,7 +21,7 @@ Subcommands
chain [n] the tiling arithmetic across n consecutive entries
Usage
- SYLPHEED_DISC=/work/sylph_extract python3 slb_segment_phase.py verify
+ SYLPHEED_DISC=/path/to/extracted/disc python3 slb_segment_phase.py verify
"""
import bisect
diff --git a/tools/re-capture/sound_cues.py b/tools/re-capture/sound_cues.py
index 172d7067..eafe693a 100644
--- a/tools/re-capture/sound_cues.py
+++ b/tools/re-capture/sound_cues.py
@@ -13,13 +13,14 @@ a script message `MSG_` plays the cue `VOICE_`, and the bank file is
`jpn\\Voice\\VOICE_.slb`. See docs/re/structures/sound-cue-table.md.
"""
import collections
+from disc import disc_root
import os
import re
import struct
import sys
import zlib
-TABLES = '/work/sylph_extract/dat/tables.pak'
+TABLES = disc_root() + '/dat/tables.pak'
def be32(b, o):
@@ -98,7 +99,7 @@ def wstr(b, pool, off):
def captions(lang='E'):
"""`MSG_*__` -> text, from the pack's IXUD field tables."""
- pak = '/work/sylph_extract/dat/GP_MAIN_GAME_%s.pak' % lang
+ pak = disc_root() + '/dat/GP_MAIN_GAME_%s.pak' % lang
out = {}
for b in pak_entries(pak).values():
if b[:4] != b'IXUD':
@@ -124,7 +125,7 @@ def captions(lang='E'):
def demo_messages(lang='E'):
"""The cutscene message table: id -> per-page (speaker, face, dur, cue)."""
- pak = '/work/sylph_extract/dat/GP_MAIN_GAME_%s.pak' % lang
+ pak = disc_root() + '/dat/GP_MAIN_GAME_%s.pak' % lang
out = {}
for b in pak_entries(pak).values():
if b[:4] != b'IDXD' or b'MSG_DEMO' not in b:
diff --git a/tools/re-capture/splash_boundaries.py b/tools/re-capture/splash_boundaries.py
index cdc1a8b4..df5a970f 100755
--- a/tools/re-capture/splash_boundaries.py
+++ b/tools/re-capture/splash_boundaries.py
@@ -16,8 +16,8 @@ alpha > 0 changes:
so each run yields calibration points at t = 15, 45 and the splash's end.
"""
-import sys, collections
-sys.path.insert(0, '/work/tools/re-capture')
+import os, sys, collections
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from quads_per_frame import quads
def batch_runs(path, lo=0, hi=400):
diff --git a/tools/re-capture/stage_numbering.py b/tools/re-capture/stage_numbering.py
index 49293c1e..7e77a11c 100644
--- a/tools/re-capture/stage_numbering.py
+++ b/tools/re-capture/stage_numbering.py
@@ -6,6 +6,7 @@ player-craft mesh; the Arsenal pak's 168 stage-scoped entries say the same thing
from the other side. Regenerates docs/re/data/stage-numbering.txt.
"""
import sys, os, glob, collections
+from disc import disc_root
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
@@ -13,8 +14,8 @@ import unitgroup as U
from unit_substructures import pak_entries
def main():
- mg = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
- ars = glob.glob('/work/sylph_extract/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0]
+ mg = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
+ ars = glob.glob(disc_root() + '/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0]
H = {h for h, b in pak_entries(mg)}
E = {h: b for h, b in pak_entries(ars)}
diff --git a/tools/re-capture/stagetbl.py b/tools/re-capture/stagetbl.py
index 2ad59f69..49d56ca5 100644
--- a/tools/re-capture/stagetbl.py
+++ b/tools/re-capture/stagetbl.py
@@ -11,10 +11,11 @@ docs/re/structures/unit-group-table.md and tools/re-capture/unitgroup.py.
# tables it names
"""
import argparse, re, sys
+from disc import disc_root
sys.path.insert(0, __file__.rsplit('/', 1)[0])
from unitgroup import read_entry, name_hash, parse, named
-PAK = '/work/sylph_extract/dat/GP_MAIN_GAME_E.pak'
+PAK = disc_root() + '/dat/GP_MAIN_GAME_E.pak'
def stage_record_hash(stage):
"""The per-stage definition record is not name-addressed; find it by content."""
@@ -63,7 +64,7 @@ def main():
seen.add(v)
src, n = PAK, 'stage\\' + v.split('+')[-1]
if '+' in v and 'DefTables' in v:
- src, n = '/work/sylph_extract/hidden/DefTables.pak', v.split('+')[-1]
+ src, n = disc_root() + '/hidden/DefTables.pak', v.split('+')[-1]
print()
try: dump('%s (via %s)' % (n, fn), parse(read_entry(src, name_hash(n))), a.limit)
except Exception as e: print('=== %s -> unresolved: %s' % (n, e))
diff --git a/tools/re-capture/turret_coverarea.py b/tools/re-capture/turret_coverarea.py
index 42514f5f..940d18e9 100644
--- a/tools/re-capture/turret_coverarea.py
+++ b/tools/re-capture/turret_coverarea.py
@@ -4,6 +4,7 @@
Regenerates docs/re/data/turret-coverarea.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)
@@ -30,7 +31,7 @@ def wclass(w):
return w or ''
def main():
- mg = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
+ mg = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
units = units_of(mg)
rows = [(gn.get('ID'), gn.get('Type'), U.named(r))
for recs, gn in units for r in recs if TUR.fullmatch(r['squadron'])]
@@ -71,7 +72,7 @@ def main():
% (m(n), n.get('Frame'), n.get('YawLimit'), n.get('WeaponID', '')))
wid, mounted, vers = set(), set(), collections.Counter()
- for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
+ for pk in sorted(glob.glob(disc_root() + '/**/*.pak', recursive=True)):
for h, b in pak_entries(pk):
if b[:4] != b'IDXD': continue
try: recs = U.parse(b)
diff --git a/tools/re-capture/unit_discover.py b/tools/re-capture/unit_discover.py
index fd1f2403..54435dbc 100644
--- a/tools/re-capture/unit_discover.py
+++ b/tools/re-capture/unit_discover.py
@@ -16,7 +16,7 @@ import struct
import sys
from collections import defaultdict
-sys.path.insert(0, "/home/fabi/RE - Project Sylpheed/sylpheed-reborn/tools/re-capture")
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gmem # noqa: E402
DELTAS = [0x00, 0x10, 0x08, 0x04, 0x0C, 0x14, 0x18, 0x20]
diff --git a/tools/re-capture/unit_runtime.py b/tools/re-capture/unit_runtime.py
index 758c374a..e3ea5092 100644
--- a/tools/re-capture/unit_runtime.py
+++ b/tools/re-capture/unit_runtime.py
@@ -23,7 +23,7 @@ import struct
import sys
from collections import defaultdict
-sys.path.insert(0, "/home/fabi/RE - Project Sylpheed/sylpheed-reborn/tools/re-capture")
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import gmem # noqa: E402
DEF_VTABLE = 0x820AF844
diff --git a/tools/re-capture/unit_substructures.py b/tools/re-capture/unit_substructures.py
index ba30c1b6..1eff9646 100644
--- a/tools/re-capture/unit_substructures.py
+++ b/tools/re-capture/unit_substructures.py
@@ -7,6 +7,7 @@ records: `Turret_NNN`, `Bridge_NNN`, `Hatch_NNN`, `ShieldGenerator_NNN`,
`Thruster_NNN`, `Versatile_NNN`. Regenerates docs/re/data/unit-substructures.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)
@@ -48,7 +49,7 @@ def units_of(pak):
return out
def main():
- pak = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
+ pak = glob.glob(disc_root() + '/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
units = units_of(pak)
print("# Sub-records inside the %d unit tables of %s" % (len(units), os.path.basename(pak)))
print("# Regenerate: python3 tools/re-capture/unit_substructures.py")
diff --git a/tools/re-capture/unitgroup.py b/tools/re-capture/unitgroup.py
index 2b18546e..010cea2e 100644
--- a/tools/re-capture/unitgroup.py
+++ b/tools/re-capture/unitgroup.py
@@ -30,6 +30,7 @@ which holds for 1160 of 1160 squadrons across the 28 stage tables present in
GP_MAIN_GAME_E.pak.
"""
import argparse, glob, os, struct, sys, zlib
+from disc import disc_root
MODULUS, RECIP = 0x00FFF9D7, 0x80031493
@@ -155,10 +156,11 @@ def load_stage(pak, stage):
def main():
ap = argparse.ArgumentParser()
ap.add_argument('stage', nargs='?', default='S02')
- ap.add_argument('--pak', default='/work/sylph_extract/dat/GP_MAIN_GAME_E.pak')
+ ap.add_argument('--pak', help='default: $SYLPHEED_DISC/dat/GP_MAIN_GAME_E.pak')
ap.add_argument('--all', action='store_true', help='every stage S01..S29')
ap.add_argument('--check', action='store_true', help='only report the Count*4+5 identity')
a = ap.parse_args()
+ a.pak = a.pak or disc_root() + '/dat/GP_MAIN_GAME_E.pak'
stages = ['S%02d' % n for n in range(1, 30)] if a.all else [a.stage]
held = total = 0
for st in stages:
diff --git a/tools/re-capture/whatchanges.py b/tools/re-capture/whatchanges.py
index d2c754ed..a6aee415 100644
--- a/tools/re-capture/whatchanges.py
+++ b/tools/re-capture/whatchanges.py
@@ -8,10 +8,11 @@ capture: constant, smoothly varying (a continuous quantity), or jumpy.
Usage: whatchanges.py [name-substring] [max-report]
"""
import math
+import os
import struct
import sys
-sys.path.insert(0, "/home/fabi/RE - Project Sylpheed/sylpheed-reborn/tools/re-capture")
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from flight_analyze import load # noqa: E402
diff --git a/tools/re-capture/zero_scale_census.py b/tools/re-capture/zero_scale_census.py
index 054e56d5..9ca94d3b 100644
--- a/tools/re-capture/zero_scale_census.py
+++ b/tools/re-capture/zero_scale_census.py
@@ -11,6 +11,7 @@ the change. It counts elements whose guessed rest pose is zero-scale and whose
alpha is non-zero, i.e. the ones the old code actually painted.
"""
import struct, zlib, glob, os, collections
+from disc import disc_root
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
def entries(base):
stub=open(base+".pak","rb").read()
@@ -59,7 +60,7 @@ def dwell_pick(t,p):
return best[0]
alpha=lambda p:(p[0]>>24)&0xff
n=paint=0; where=collections.Counter(); ex=[]
-for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
+for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
for i,h,d in entries(pak[:-4]):
for idx,name,t,p in elements_t(d):
if has_plateau(p): continue
diff --git a/tools/zq.py b/tools/zq.py
index 4697dfd9..9e0e9b4d 100755
--- a/tools/zq.py
+++ b/tools/zq.py
@@ -56,9 +56,8 @@ def _resolve_db():
"""Locate the database: `$SYLPHEED_DB` / `$SYLPH_XEXDB`, else `/sylpheed.db`.
🔴 This used to be one hardcoded absolute path, and it pointed *inside*
- `xenia-rs` -- a repository `docs/agents/CONSOLIDATION.md` archives in Phase 5
- and drops in Phase 7. It resolved on exactly one machine and would have
- started failing there too, with `duckdb` raising about a missing file rather
+ `xenia-rs` -- a repository that has since been archived. It resolved on
+ exactly one machine and would have started failing there too, with `duckdb` raising about a missing file rather
than anything saying why.
The database is a build artefact of several hundred MB and is not in the