port: 4 of 5 menu destinations are blocked on one hardcoded archive
Probed with the EXISTING build detector -- no new decoding. 24 disc archives contain UI screen builds; the exporter reads one (dat/GP_TITLE.pak, hardcoded). GP_OPTIONS has 14 builds, GP_SAVE_LOAD 18, GP_DIALOG 105, GP_TUTORIAL 2. So LOAD GAME, TUTORIAL, OPTIONS and the NEW GAME difficulty chain are not blocked on the Decoder and need no format work. They are an exporter scope limit, and the exporter is the port's. Prioritised by the filter adopted after the plate: 'if this is wrong, what does a player experience?' Four dead menu entries beat an audio level, which beats timing minutiae. Flagged as NOT established: that the screens render (is_build says the record parses, not that sprites resolve or names are known), which GP_DIALOG entry is the difficulty dialog, and that more screens are free -- every one joins check-all's per-screen comparisons and needs a naming decision. Next unit widens to GP_OPTIONS only. Not all four: 139 new screens at once would make any regression unattributable.
This commit is contained in:
42
crates/sylpheed-export/examples/probe_archives.rs
Normal file
42
crates/sylpheed-export/examples/probe_archives.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//! Which disc archives contain UI screen builds?
|
||||||
|
//!
|
||||||
|
//! The exporter reads `dat/GP_TITLE.pak` and nothing else, so four of the five
|
||||||
|
//! main-menu destinations have no screen file to go to: `authored/flow.json`
|
||||||
|
//! records LOAD GAME as `GP_SAVE_LOAD`, OPTIONS as `GP_OPTIONS`, and NEW GAME's
|
||||||
|
//! chain as `DLG_SELECT_DIFFICULTY` -> `SELECT DATA`, all measured destinations
|
||||||
|
//! that this export cannot reach.
|
||||||
|
//!
|
||||||
|
//! This asks the cheap question before anyone refactors the exporter: does the
|
||||||
|
//! EXISTING build detector find anything in those archives? It changes nothing
|
||||||
|
//! and writes nothing.
|
||||||
|
//!
|
||||||
|
//! cargo run --release -p sylpheed-export --example probe_archives
|
||||||
|
use sylpheed_formats::{pak::PakArchive, ui_layout};
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let disc = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into());
|
||||||
|
let mut names: Vec<String> = std::fs::read_dir(format!("{disc}/dat"))?
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.map(|e| e.file_name().to_string_lossy().into_owned())
|
||||||
|
.filter(|n| n.ends_with(".pak"))
|
||||||
|
.collect();
|
||||||
|
names.sort();
|
||||||
|
println!("{:<32} {:>7} {:>8}", "archive", "entries", "builds");
|
||||||
|
for n in names {
|
||||||
|
let path = format!("{disc}/dat/{n}");
|
||||||
|
let Ok(ar) = PakArchive::open(&path) else {
|
||||||
|
println!("{n:<32} {:>7} {:>8}", "-", "open failed");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let total = ar.entries().len();
|
||||||
|
let builds = ar
|
||||||
|
.entries()
|
||||||
|
.iter()
|
||||||
|
.filter(|e| ar.read(e).map(|b| ui_layout::is_build(&b)).unwrap_or(false))
|
||||||
|
.count();
|
||||||
|
if builds > 0 || n.contains("OPTIONS") || n.contains("SAVE") || n.contains("DIALOG") {
|
||||||
|
println!("{n:<32} {total:>7} {builds:>8}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
70
docs/port/menu-destinations-are-one-line-away.md
Normal file
70
docs/port/menu-destinations-are-one-line-away.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Four of five main-menu destinations are blocked on ONE hardcoded archive
|
||||||
|
|
||||||
|
**Status:** ✅ feasibility established, nothing changed yet. 2026-09-03.
|
||||||
|
|
||||||
|
## The gap, in player terms
|
||||||
|
|
||||||
|
| button | destination | today |
|
||||||
|
|---|---|---|
|
||||||
|
| NEW GAME | `DLG_SELECT_DIFFICULTY` → SELECT DATA → video | **jumps straight to the video** |
|
||||||
|
| LOAD GAME | `GP_SAVE_LOAD` | **dead** |
|
||||||
|
| TUTORIAL | — | **dead** |
|
||||||
|
| OPTIONS | `GP_OPTIONS` | **dead** |
|
||||||
|
| EXTRAS | `extras` | works |
|
||||||
|
|
||||||
|
All four are recorded in `authored/flow.json` as **measured destinations** —
|
||||||
|
somebody drove the real game to them. They are `blocked` for one structural
|
||||||
|
reason, stated there: *"not a GP_TITLE build, so there is no screen file to go
|
||||||
|
to."*
|
||||||
|
|
||||||
|
## The cause is one line
|
||||||
|
|
||||||
|
`crates/sylpheed-export/src/main.rs` hardcodes `let archive = "dat/GP_TITLE.pak"`.
|
||||||
|
|
||||||
|
## And the reader already works on the rest
|
||||||
|
|
||||||
|
`examples/probe_archives.rs` runs the **existing** `ui_layout::is_build` over
|
||||||
|
every `.pak` on the disc. It decodes nothing new:
|
||||||
|
|
||||||
|
| archive | entries | builds |
|
||||||
|
|---|---|---|
|
||||||
|
| `GP_OPTIONS` | 26 | **14** |
|
||||||
|
| `GP_SAVE_LOAD` | 108 | **18** |
|
||||||
|
| `GP_DIALOG` | 140 | **105** |
|
||||||
|
| `GP_TUTORIAL` | 2 | **2** |
|
||||||
|
| `GP_TITLE` | 16 | 12 |
|
||||||
|
|
||||||
|
**24 archives contain UI screen builds. The exporter reads one.**
|
||||||
|
|
||||||
|
> So this is not blocked on the Decoder and needs no new format work. It is an
|
||||||
|
> exporter scope limit, and the exporter is the port's.
|
||||||
|
|
||||||
|
## Why this is worth doing before the queued items
|
||||||
|
|
||||||
|
Measured against *"if this is wrong, what does a player experience?"* — the
|
||||||
|
filter this port adopted after spending two rounds on a plate pulse that turned
|
||||||
|
out not to be a defect:
|
||||||
|
|
||||||
|
* **four dead menu entries** and a missing difficulty screen: a player hits them
|
||||||
|
immediately and three of them do nothing at all;
|
||||||
|
* the audio mix (F2): a player notices, but the menu still works;
|
||||||
|
* the repeat rate (F1) and the title track (F3): both blocked on measurement.
|
||||||
|
|
||||||
|
## ⚠️ What this does NOT establish
|
||||||
|
|
||||||
|
* **That the screens will render.** `is_build` says the record parses as a build,
|
||||||
|
not that its sprites resolve, its names are known, or its layout is complete.
|
||||||
|
`GP_HANGAR_ARSENAL` reports 390 builds and is squarely gameplay, out of scope.
|
||||||
|
* **Which entry is the difficulty dialog.** `GP_DIALOG` has 105 builds and none
|
||||||
|
of them is named yet; `DLG_SELECT_DIFFICULTY` is a name from the flow, not an
|
||||||
|
entry index.
|
||||||
|
* **That more screens are free.** Every screen the export gains is a screen
|
||||||
|
`check-all`'s comparisons iterate over, and screen names are authored per
|
||||||
|
archive+entry — unnamed screens need a naming decision, not just a loop bound.
|
||||||
|
|
||||||
|
## Next unit
|
||||||
|
|
||||||
|
Widen the exporter to **one** further archive — `GP_OPTIONS`, the smallest at 26
|
||||||
|
entries — as data rather than a second hardcoded constant, and see what actually
|
||||||
|
comes out. Not all four at once: 139 new screens arriving together would make any
|
||||||
|
regression unattributable.
|
||||||
Reference in New Issue
Block a user