diff --git a/crates/sylpheed-formats/examples/screen_configs.rs b/crates/sylpheed-formats/examples/screen_configs.rs new file mode 100644 index 0000000..331b08d --- /dev/null +++ b/crates/sylpheed-formats/examples/screen_configs.rs @@ -0,0 +1,18 @@ +//! RE probe: dump tables.pak screen-config records that mention a given token. +//! Usage: screen_configs +use sylpheed_formats::{idxd::IdxdObject, pak::PakArchive}; +fn main(){ + let a:Vec=std::env::args().collect(); + let needle=a.get(2).cloned().unwrap_or_else(||"CHALLENGE".into()); + let arc=PakArchive::open(format!("{}/dat/tables.pak",a[1])).unwrap(); + for (i,e) in arc.entries().iter().enumerate(){ + let Ok(b)=arc.read(e) else{continue}; + let Ok(o)=IdxdObject::parse(&b) else{continue}; + let t=o.tokens(); + if !t.iter().any(|s|s.to_lowercase().contains(&needle.to_lowercase())){continue} + let path=t.iter().find(|s|s.contains(".pak+")).cloned().unwrap_or_default(); + if !path.is_empty() && !path.contains("+eng"){continue} + println!("\n=== entry #{i} schema {:08x} [{path}] {} tokens ===",o.schema_hash,t.len()); + for (j,tok) in t.iter().enumerate(){println!(" {j:3} {tok}");} + } +} diff --git a/docs/re/captures/mission-select-all-story-unlocked.png b/docs/re/captures/mission-select-all-story-unlocked.png new file mode 100644 index 0000000..acecd26 Binary files /dev/null and b/docs/re/captures/mission-select-all-story-unlocked.png differ diff --git a/docs/re/captures/mission-select-ends-at-stage16.png b/docs/re/captures/mission-select-ends-at-stage16.png new file mode 100644 index 0000000..b79b498 Binary files /dev/null and b/docs/re/captures/mission-select-ends-at-stage16.png differ diff --git a/docs/re/challenge-mission-gate.md b/docs/re/challenge-mission-gate.md index dd0bbfc..fbc943e 100644 --- a/docs/re/challenge-mission-gate.md +++ b/docs/re/challenge-mission-gate.md @@ -310,6 +310,20 @@ claims stages that do not exist (`0`, `17`, `24`–`31` in word A). Poking only story ids (`0x0001FFFE` = stages 1–16) does **not** blow the heap. That the list screen changes behaviour with the mask is itself confirmation that word A feeds it. +**Poking real stage ids unlocks the story campaign in the menu** ✅. With word A = +`0x0001FFFE` (stages 1–16) every entry `Stage01`…`Stage16` is selectable +([capture](captures/mission-select-all-story-unlocked.png)) where the control had only +`Stage01`; `Stage16` reads *"Lonely Blue Planet — NO RECORD"*. So **any story stage +can now be launched from the menu**, with no save editing, by poking one word. + +**But MISSION SELECT is story-only** ❌. With word B = `0x3F` (challenge stages 24–29 +marked cleared) the list still **saturates at `Stage16`** +([capture](captures/mission-select-ends-at-stage16.png)) — the cursor stops there and +further presses do nothing. That matches the data: the debriefing config declares +exactly `px_deb_stage01…16`, so the list length is capped by the disc, not by the +mask. **The challenge missions cannot be reached through this screen**, and word B +does not feed it. + **What the poke did not do (yet):** `EXTRAS` still shows only `MISSION SELECT / MOVIE THEATER / BACK` — no challenge entry — although the menu was built 26 s *after* the poke, so this is not staleness. Entering `MISSION SELECT` then failed,