this container kills long runs Three results, and the middle one is deliberately not upgraded. First: NEW GAME -> DIFFICULTY -> NORMAL -> SELECT DATA -> pick a slot -> a movie plays, with no crash. The previous run's throw at 0x82307128 is therefore not inherent to the menu path -- the same six presses got through it -- which fits the incomplete-cache trigger the corpus already names. n=1 either way, and the note says so. Worth recording because one observation had already hardened once into "A on NEW GAME hangs", and it would have hardened again into "the new-game path crashes". Second: with the path open, the manifest's MS00A -> S00A.wmv finally got a runtime test, and it did NOT reproduce the quality of the attract-movie identification. The first four samples give a clean monotonic S00A playhead of 2, 6, 9, 13 s and the strongest frame matches at 0.916, but the run as a whole is not one advancing timeline, most frames sit at contrast 23-37 where this page's own rule says discrimination is poor, and one high-contrast frame prefers ADV. So it stays amber: consistent with S00A, corroborating the manifest decode, not confirming it. A clean answer needs the 30fps x11grab route rather than 5-second screenshots. Third, and it bounds the rest: the emulator vanished at ~145 s with no crash line in its own log, last entries routine MEM-WATCH at 1.15 GB RSS. Second time this session -- the other was ~50 s into a boot. That is an external kill on a box with ~1 GB free and swap exhausted, not a guest fault. METHOD and the handoff's residue table both now say dynamic experiments here have to fit in about two minutes of guest time, which is why the DELTASABER plates went unseen again: they need a mission load and the run never got there.
283 lines
21 KiB
Markdown
283 lines
21 KiB
Markdown
# Handoff — what the menu port needs, and where it stands
|
||
|
||
The single page the **port agent** reads. Everything here is produced by the
|
||
container agent's reverse engineering; nothing here is a design decision about
|
||
the port itself.
|
||
|
||
Keep it current. It is a summary with links into `docs/re/`, not a second copy of
|
||
the findings — but an answer that is not reachable from this page has not been
|
||
delivered.
|
||
|
||
## How to read an answer
|
||
|
||
Every row below is one of exactly three things, and the distinction is the point:
|
||
|
||
| | meaning | what the port should do |
|
||
|---|---|---|
|
||
| **decoded** | a field on the disc, with a disc-wide check | read it from the data |
|
||
| **measured** | not on the disc in any form we found, but the running game does *this* | hardcode it, and cite this page |
|
||
| **undecodable** | we looked in these places, it is not there, here is the reach of the negative | author it by hand, knowingly |
|
||
|
||
There is no fourth kind. If a row says *measured* or *undecodable*, the port is
|
||
**authoring** that value, not transcribing it — and it should be kept somewhere a
|
||
human can see it is a human decision, so that when it is later decoded the
|
||
authored version can be deleted.
|
||
|
||
## Status
|
||
|
||
| | Question | State | Answer / link |
|
||
|---|---|---|---|
|
||
| Q1 | keyframe time unit + ramp shape | ✅ answered | ramp is **linear**; **2 units per rendered frame**; **`1 unit = 1/60 s` — settled**, the idle title presents at 28.5 fps so the game is 30 Hz — [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md) |
|
||
| Q2 | which build is which screen state | ✅ answered | `GP_TITLE` is **8 screens shipped twice, EN/JP**: 4/7 title art, 2/3 the `PRESS Ⓐ` plate, 5/8 main menu, 6/9 `EXTRAS`, 0/1 and 10/11 two unidentified `DELTASABER` plates — [`ui-title-build-map.md`](../re/ui-title-build-map.md) |
|
||
| Q3 | paint order for the six screens | ✅ answered | **decoded**: a `u16` layer key at `+0x0A` of each `T8aD` sprite header, stable-sorted with declaration index; unkeyed elements get an implied key. Confirmed on 5 measured orders + `EXTRAS` vs a capture. One residual: the **tie-break** is unknown and bites on one element of the title — [`structures/ui-paint-order-key.md`](../re/structures/ui-paint-order-key.md) |
|
||
| Q4 | button → GamePart | ✅ answered | **measured** which screen all **5** buttons open — `NEW GAME` → `DIFFICULTY` → `SELECT DATA`, not a hang. The **GamePart id is still a name match**, not a measurement — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) |
|
||
| Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 2× `NEW GAME`); ⬆⬇ one step, **wraps both ends**; ⬅➡ do nothing; Ⓑ returns to the parent **with focus restored**; Ⓑ on the main menu → title; Ⓑ on the title → nothing — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) |
|
||
| Q6 | boot sequence + what drives it | ✅ answered | sequence **measured** end to end; the driver is **code, not data** — four search spaces closed, so the port **authors** the sequence — [`boot-config-and-gamepart-registry.md`](../re/boot-config-and-gamepart-registry.md) |
|
||
| Q7 | transitions | ✅ answered | a **fade through black**, drawn by the screen's own last-painting `.prm` quad. Fade-in ramp is **decoded** from its keyframes; the ~0.4 s fade-out is **measured** (not in the file) — [`screen-transitions.md`](../re/screen-transitions.md) |
|
||
| Q8 | menu audio bindings | ✅ answered | cue vocabulary + bank **decoded**; event binding is a **name match** (the authors' own event names); SE audio is **undecodable from the disc** — no XACT container exists anywhere — [`menu-audio-cues.md`](../re/menu-audio-cues.md) |
|
||
| Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE`→`ADV.wmv` (boot intro *and* attract are one asset), `MS00A`→`S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. ✅ **one Ⓐ skips a movie** (title at 57 s vs a 193 s baseline) — [`movie-binding.md`](../re/movie-binding.md) |
|
||
| Q10 | music-bank sub-wave roles (intro+loop?) | ✅ answered | **two stems of one performance, played together** — sample-synchronous, equal duration, 32/32 banks. **Concatenating is wrong.** Not a seamless loop either — [`structures/bgm-two-stems.md`](../re/structures/bgm-two-stems.md) |
|
||
| S1 | Ready Room go/no-go | ✅ **no-go** | it is 2D and enumerates fine (60 builds), but `GP_READY_ROOM.pak` holds **briefing/tactical-map** content, not the six-button Ready Room menu — [`ready-room-probe.md`](../re/ready-room-probe.md) |
|
||
|
||
## Already settled — the port can rely on these today
|
||
|
||
* **`GP_TITLE.pak` is eight screens, each shipped twice — English and Japanese.**
|
||
Build 4 is the English title art and 7 its Japanese twin; **2/3 are the
|
||
`PRESS Ⓐ BUTTON` plate, a build of their own** composited over the title and
|
||
faded in a beat later; 5/8 are the five-button main menu; 6/9 are the `EXTRAS`
|
||
submenu — the **only** submenu inside this archive. Builds 0/1 and 10/11 are a
|
||
`DELTASABER / SYLPHEED A.I.` plate that was **never seen running**, in the boot
|
||
path, any title-side screen, or the attract loop. ✅ measured against live
|
||
captures for the four English screens the boot path shows;
|
||
[`ui-title-build-map.md`](../re/ui-title-build-map.md).
|
||
**Withdrawn:** the earlier "builds 6/8/9 are submenus" — 8 is the Japanese main
|
||
menu. The other four main-menu buttons leave the archive, and where each one
|
||
goes is **measured** — see the button-destination bullet below.
|
||
* **Buttons are identifiable as data.** Element kind `0x3002` = button, `0x0` =
|
||
decoration, `0x10` = primitive. ✅ decoded **for the title-side screens**.
|
||
⚠️ `0x3002` is one member of a `0x3000` family with sub-bits, and it is not the
|
||
only button kind on the disc: `GP_READY_ROOM`'s 902 bundles contain **zero**
|
||
`0x3002` and use `0x3000` / `0x3004` / `0x300c` / `0x3008` instead. Nothing in
|
||
this milestone changes — every screen in scope is `GP_TITLE` — but do not ship
|
||
`kind == 0x3002` as a general button test.
|
||
(The kind is the 4th `u32` of the 60-byte declaration entry, at `+40`.)
|
||
* **Menu order is geometric.** Buttons sorted top-to-bottom by resting Y. This is
|
||
✅ correct for a vertical menu and is **not** a decoded neighbour graph — the
|
||
disc's real navigation structure is unknown, and `opt ` is *not* a focus link
|
||
(measured and refuted, see
|
||
[`ui-focus-and-effect-elements.md`](../re/structures/ui-focus-and-effect-elements.md)).
|
||
* **Highlighted states pair by name** — `ptbtn01.rat` ↔ `ptbtn01f.rat`. 🟡 a
|
||
naming convention that holds for all 54 real pairs, not a decoded field.
|
||
* **The resting pose is the hold**, not the first, last or longest-dwell keyframe;
|
||
a keyframe is the **start of a ramp**.
|
||
[`ui-resting-pose.md`](../re/structures/ui-resting-pose.md). ✅
|
||
* **That ramp is linear, and it runs at 2 keyframe time units per rendered
|
||
frame.** Measured frame-by-frame off the running game's own draw stream: a
|
||
declared 15-unit fade lands on `round(255·k/15)` for all seven of its samples,
|
||
with `k` stepping 2, 4, 6, 8, 10, 12, 14 on seven consecutive submitted frames.
|
||
**measured**, not decoded — the disc says `t=30`, it does not say what a `t` is.
|
||
✅ **The seconds conversion is settled: `1 unit = 1/60 s`, a 30 Hz title.** The
|
||
re-test this page used to name has been run — 300 submitted frames timed on the
|
||
**idle** title (nothing loading) came out at **28.8 and 28.3 fps**, the same
|
||
rate as the 27.6 fps measured during the loading splash. The competing 60 Hz
|
||
reading is excluded: it needs the emulator at 47 % of real time while idling on
|
||
a screen that costs ~5 draws per frame.
|
||
A second, independent line agrees — the transition quad is declared black for
|
||
12 units (0.20 s under this conversion) and a capture measured the pure-black
|
||
plateau at 0.17–0.23 s
|
||
([`screen-transitions.md`](../re/screen-transitions.md)).
|
||
⚠️ Still **measured, not decoded**: no field on the disc says "sixtieths of a
|
||
second".
|
||
* **The paint order is derivable from the file.** Each `T8aD` sprite header
|
||
carries a **`u16` layer key at `+0x0A`** (the upper half of the 32-bit word at
|
||
`+0x08` is zero in all 21 184 sprites on the disc). Paint order is that key,
|
||
**stable-sorted** so equal keys keep declaration order; elements with no sprite
|
||
(`.prm` primitives, `.tbm`) have no key and take an implied one — the backdrop
|
||
and dim quads sort early, the screen-transition fade (`pteff00.prm`,
|
||
`pfeff00.prm`) sorts **last**. ✅ decoded. Checked against five paint orders read
|
||
off the running game, one of them (`GP_SAVE_LOAD`'s slot-list header, 6
|
||
instances) **exact and independent of the screens the rule was fitted to**, and
|
||
against a fresh `EXTRAS` capture. It reorders 341 of the disc's 965 builds, so
|
||
it is not a no-op dressed as a rule.
|
||
🟡 **The one residual: ties.** Where two elements share a key the game
|
||
sometimes paints them in an order nothing predicts — eight candidates refuted,
|
||
including declaration order, RATC child order, keyframe times, resting X/Y and
|
||
`kind`. Measured cost: on the three screens with ground truth it changes the
|
||
blend of **one element on one screen** (a title glow). Take the stable sort and
|
||
accept that.
|
||
|
||
* **Menu movement, measured off the running game.** ⬆⬇ move one item per press
|
||
and **wrap at both ends** (5-item main menu and 3-item `EXTRAS` both). ⬅➡ do
|
||
nothing. Ⓑ goes up one level **and restores focus to the item you came from**;
|
||
Ⓑ on the main menu returns to the title; Ⓑ on the title does nothing. **Initial
|
||
focus is not stable**: four boots of the same script gave `TUTORIAL`,
|
||
`TUTORIAL`, `NEW GAME`, `NEW GAME`. Do not hardcode it; pick one and say you
|
||
picked it. All **measured**, none of it on the disc.
|
||
* **Each button's destination is measured; its GamePart id is not.**
|
||
**`NEW GAME` → `DIFFICULTY`** (`EASY`/`NORMAL`/`HARD`/`BACK`, opening on
|
||
`NORMAL`) **→ `SELECT DATA`** — it does *not* hang; the run then hits the
|
||
already-documented `sub_823070B0` cache crash, which is not a menu problem.
|
||
`LOAD GAME` → the save-slot list, `TUTORIAL` → the lesson list, `OPTIONS` →
|
||
the settings menu, `EXTRAS` → `GP_TITLE` build 6, `EXTRAS ▸ MISSION SELECT` →
|
||
the stage list. The
|
||
GamePart ids (`3`, `25`, `8`, `5`, `7`) are the entries of the decoded id table
|
||
whose **names match the screens seen**; that binding is authored, not measured.
|
||
|
||
* **A screen change is a fade through black.** Each screen carries a full-screen
|
||
black `.prm` quad that paints last (`pteff00.prm` / `pfeff00.prm`) whose
|
||
keyframe group *is* the transition: black at `T0`, clear by `T1`, clear until
|
||
`T2`, then back to black on exit. ✅ decoded, with a disc-wide check — and in
|
||
`GP_TITLE` exactly the six **screen** builds carry it while the six overlays do
|
||
not. The fade-in length is `T1 − T0` and is read from the file (0.87 s for
|
||
`EXTRAS`, 0.97 s main menu, 4.08 s title). ❔ **The fade-OUT length is not on
|
||
the disc** — the last keyframe has no time slot; **measured ~0.4 s**, twice.
|
||
The black hold measures 0.17–0.23 s. ⚠️ Do not time the fade-in off a capture's
|
||
brightness: the incoming screen's own element animations dominate it and run
|
||
much longer than the quad.
|
||
|
||
* **The movie manifest names every boot-side video by role.** `dat/tables.pak`
|
||
entry `0x5b983a08`: `LOGO1`–`LOGO4` → `logo1.wmv`–`logo4.wmv` (**not on the
|
||
disc** — this is why the splash is a screen), **`ADVERTISE_MOVIE` → `ADV.wmv`**,
|
||
`STAFF_ROLL` → `SYLPH_HD720p_8M-CBR_2ch.wmv`, **`MS00A` → `S00A.wmv`** (the
|
||
new-game intro, 93.9 s, with subtitle + `VOICE_S00A` + a text overlay),
|
||
`MS01A` → `S01A.wmv`. ✅ decoded.
|
||
**The boot intro and the attract movie are the SAME asset** — there is no
|
||
separate boot slot, and 15 of 19 captured attract frames match `ADV.wmv` with a
|
||
monotonically advancing playhead ending at its full 137 s. One video, not two.
|
||
✅ The attract movie **plays to its end**; nothing cuts it short.
|
||
✅ **A movie is skippable with a single Ⓐ.** Measured: one tap ~45 s into the
|
||
boot brought the title at ~57 s against a ~193 s no-input baseline over three
|
||
boots, with Canary's own keystroke counter proving exactly one press was
|
||
delivered — and the skipped-to title is **fully functional** (`PRESS Ⓐ` plate
|
||
present, Ⓐ opens the main menu). What breaks the boot is *hammering*: 88
|
||
presses left a permanent black screen. One press is fine.
|
||
|
||
* **The menu's sound events are named on the disc.** `tables.pak`'s `SOUNDS`
|
||
record carries 322 `SE_*` cues, and the low block is the UI vocabulary — named
|
||
after the *event*: `SE_UI_CURSOR` (2), `SE_UI_DECIDE` (3), `SE_UI_CANSEL` (4),
|
||
`SE_UI_IMPOSI` (5, the error), `SE_UI_SUB_WIN_OPN`/`_CLS` (8/9),
|
||
`SE_UI_SPLASH_IN`/`_OUT` (12/13). ✅ decoded, full list in
|
||
[`data/se-ui-cues.txt`](../re/data/se-ui-cues.txt). They all live in **one**
|
||
bank — `BANK_SE` is a single field naming `Static.slb`, and 0 of the 322 has
|
||
its own `FILES` entry.
|
||
🟡 **Which event fires which cue is a name match**, not a measurement — strong,
|
||
because these are the authors' own event names, but nobody has watched the game
|
||
emit cue 2 on a d-pad press. The port is authoring it.
|
||
❔ **The SE audio cannot be exported at all from the disc.** `Static.slb`'s
|
||
8 353 472 readable bytes contain **0 `RIFF`, 0 `seek`, 0 `WAVE`** — the boundary
|
||
marker that works for all 7 620 other banks is absent — and there is **no XACT
|
||
container anywhere**: 0 × `XGSF`/`SDBK`/`WBND` across all 1.08 GB of
|
||
`sound.pak`, and no `XACT`/`.xgs` string in the executable. The `.xgs`/`.slb`
|
||
names come from the authoring tool, not the shipped format. The bank is
|
||
certainly the right one — 8 353 472 B over 322 cues is 0.84–1.01 s each at the
|
||
disc's own bitrates — but the index into it exists only at runtime. **The UI
|
||
sound effects must be sourced or authored; they cannot be extracted.**
|
||
|
||
* **The boot sequence is not data-driven — the port authors it.** ❔ Four places
|
||
were checked and the order is in none: `config.ini`'s `[SYSTEM]` is empty, the
|
||
movie manifest carries assets not transitions, the requested GamePart id lives
|
||
**only as a stack argument in flight** (no persistent field, no literal store),
|
||
and the string `GP_ADVERTISE_DEMO` has **zero xrefs**. A transition is a call
|
||
with an id argument, chosen by code. The sequence itself is fully measured:
|
||
splash → `ADV.wmv` → title + `PRESS Ⓐ` → (idle ~8–10 s → `ADV.wmv` in full →
|
||
title) → Ⓐ → main menu, with Ⓑ from the main menu returning to the title.
|
||
|
||
* **`config.ini` picks the language, and that is what picks the EN/JP build.**
|
||
The disc's **only** config file (400 bytes, at the root; one `find` over the
|
||
whole extract). Its `[LANGUAGE]` section maps the console's `XC_LANGUAGE_*`
|
||
value to `eng`/`jpn`/`deu`/`fra`/`esp`/`ita`, defaulting to `eng` — that code
|
||
selects `GP_TITLE`'s English or Japanese build and the `<lang>.pak` families.
|
||
✅ decoded.
|
||
❔ Its `[SYSTEM]` section — which the file's own comment says holds what the
|
||
game and every game part share — is **empty**, so the boot *order* is not in
|
||
disc-side configuration at all.
|
||
* **Five GameParts are named but never registered.** Pulling every
|
||
`RegisterToFactory<N, class silph::GamePart_X>` diagnostic string binds **24 of
|
||
the 29 ids to a C++ class**
|
||
([`data/gamepart-class-ids.txt`](../re/data/gamepart-class-ids.txt)). Ids `1`,
|
||
`2`, `16`, `18`, `28` have no registration site — including
|
||
**`GP_ADVERTISE_DEMO` (1)**, which agrees with the measurement that the attract
|
||
loop is the *title* replaying `ADV.wmv` rather than a separate part. 🟡 an
|
||
argument from a diagnostic string, not from the code.
|
||
Also: **`3` and `4` are both `GamePart_SaveLoad`** — one part, two ids.
|
||
|
||
* **The GamePart id table** — 29 entries at `.rdata 0x820A1630`, confirmed by the
|
||
executable's own registration strings. ✅ This is the screen vocabulary; which
|
||
button reaches which entry is Q4 and is *not* part of it.
|
||
* **The logo splash is a screen, not a video.** `logo1`–`logo4` are
|
||
manifest-bound with no `.wmv` on the disc. ✅
|
||
* **Sprites carry their own labels.** No font rendering or localisation is needed
|
||
for this milestone. ✅ — and the localisation is *already baked in*: the
|
||
Japanese screens are separate builds in the same pak, not a text swap.
|
||
|
||
## Facts the port will trip over
|
||
|
||
* **`ADV.wmv` is WMV3 video + WMA Pro audio**, 1280×720 at 30 fps, 137 s. Godot 4
|
||
plays only Ogg Theora natively. How to handle that is the port's decision, not
|
||
ours — but it is not optional.
|
||
* **The disc holds 3.3 GB of video, and exactly two files are in scope:**
|
||
`dat/movie/ADV.wmv` (the boot intro *and* the attract loop — one asset) and
|
||
`dat/movie/S00A.wmv` (the new-game intro, 93.9 s). Named from the movie
|
||
manifest, ✅ decoded.
|
||
* **`Static.slb` over-declares its size** by 616 768 bytes — it is the
|
||
highest-offset entry in `sound.pak` and its size field is an allocation size. A
|
||
reader must allow a short read there and only there.
|
||
* **Voice downmixes to mono, music does not.** The left-channel downmix is correct
|
||
for spoken lines and discards half a music mix.
|
||
* **A music bank is TWO STEMS THAT PLAY TOGETHER — do not concatenate.**
|
||
✅ The "10 KB + 4.47 MB + 4.67 MB" reading was wrong: the 10 KB is the bank
|
||
header, and a bank is exactly **two waves of identical duration** (32/32 banks
|
||
on the disc). `BGM_001`'s two are **sample-synchronous** — transient
|
||
correlation peaks at lag 0.00 s over ±5 s, and both stop at the same
|
||
millisecond, 167.663 s. Wave 1 is quieter, far more L/R-decorrelated and has
|
||
almost no bass, so it reads as a surround-rear pair or a second intensity
|
||
layer — 🟡 which of those is unsettled, and `ChannelMask` is `0x0002` on both,
|
||
so the file will not say. Today's 347 s concatenation plays the piece twice,
|
||
the second time as a bass-less stem.
|
||
❔ **And it is not a seamless loop**: `BGM_001` fades out at 167.663 s and is
|
||
followed by 6.15 s of silence, with no loop-point field identified. A menu loop
|
||
is authored.
|
||
❔ **Which bank is the menu's music is not on the disc** — all 32 BGM cues are
|
||
named `BGM_001`…`BGM_109`, no semantic name in `SOUNDS`, `FILES` or the bank
|
||
headers. The port is choosing a track.
|
||
* **`JNGL_001.slb` does not decode.** One bank in 9 519; its payload is not a whole
|
||
number of XMA1 packets from any known data offset.
|
||
|
||
## What is still open
|
||
|
||
Every question above is answered, so this is the honest residue rather than a
|
||
work queue. **None of it blocks the five screens.** (Q1's seconds conversion was
|
||
here until 2026-08-28 and is now settled.)
|
||
|
||
| | what | why it is stuck |
|
||
|---|---|---|
|
||
| 🟡 | **cue → event binding as a measurement** (Q8) | nobody has watched the game emit `SE_UI_CURSOR` on a d-pad press. Needs the guest's cue call observed with its argument |
|
||
| ❔ | **the wave index into `Static.slb`** (Q8) | same instrumentation. This container runs `--mute=true` against an SDL dummy device, so it cannot watch the audio path at all |
|
||
| 🟡 | **the paint-order tie-break** (Q3) | eight candidates refuted; costs one element's blend on one screen |
|
||
| 🟡 | **GamePart ids behind the buttons** (Q4) | the *screens* are measured; the ids are a name match onto the executable's class names |
|
||
| ❔ | **the boot transitions in code** (Q6) | bounded as code-not-data, not proven. `sub_821C6458` — the substantial function in `GamePart_Title`'s neighbourhood — has **not been read** |
|
||
| 🟡 | **`MS00A` → `S00A.wmv` watched only weakly** (Q9) | the path completes now, but 5-second screenshots gave a partial playhead, not the clean one the attract test gave. Manifest decode is still the primary evidence |
|
||
| ❔ | **builds 0/1 and 10/11**, the `DELTASABER` plates (Q2) | never seen anywhere in the boot path, the title-side screens or the attract loop. A mission load is the remaining candidate and this container kills runs before one completes |
|
||
|
||
The first two share one unblocker — an emulator build whose audio path can be
|
||
observed — and would be the only entries here that change a port decision.
|
||
|
||
⚠️ **A container caveat that bounds all of the above:** the emulator has twice
|
||
been killed mid-run with no crash line in its own log (at ~50 s and ~145 s), on a
|
||
box sitting at ~1 GB free with swap exhausted. Dynamic experiments here have to
|
||
fit in roughly two minutes of guest time, which is why several of these residuals
|
||
are unfinished rather than unattempted.
|
||
|
||
## Reference data
|
||
|
||
Committed alongside the findings, so the port can be built without a disc in the
|
||
loop during development:
|
||
|
||
* `sylpheed-cli screen info --build <n> GP_TITLE.pak` — the element table, per
|
||
build, with pivots, kinds, focus links, keyframes and resting poses.
|
||
* `sylpheed-cli screen render` — the reference composite. When the port draws a
|
||
screen, this is what it should be diffed against; where they disagree, one of
|
||
them is wrong and the disagreement is worth reporting back.
|
||
* `docs/re/captures/` — framebuffer captures of the real screens, for anything
|
||
that has to be checked against the game rather than against our renderer.
|