formats: two more measured paint orders, and the first independent confirmation

The three orders the derived rule was built from all live in GP_TITLE.pak, so
they cannot confirm it - the rule was fitted to them. These two are from
GP_SAVE_LOAD.pak, read off the running game now that the Canary threading fix
makes the main menu dependable.

The 9-element slot-list header composites EXACTLY as the sort predicts, on all 6
instances of it, and nothing about this screen was fed into the rule:

    measured  7 8 0 1 2 3 4 5 6
    derived   7 8 0 1 2 3 4 5 6

including TWO tied groups (0xb102 x2 and 0xb210 x5) that both come out in
declaration order, and the unkeyed pfeff00.prm fade quad last.

The 13-element save/load frame differs in exactly the two open questions and no
new ones: two unkeyed pfbase.tbm backgrounds paint FIRST where the sort puts the
keyless last - the splash's palogo_eff0.prm behaviour in a different file type,
so implied_layer_key now covers it - and the 0xb100 group of four paints
10,11,8,12 where declaration order is 8,10,11,12.

That second point is a SECOND screen with a mis-ordered tie, which is what the
question needed, and it immediately kills a candidate: 10 and 11 are kind=0x2002
while 8 and 12 are 0x0000, so "descending kind then declaration index"
reproduces 10,11,8,12 exactly - and then fails both title groups, where every
element of 0x8083 is kind 0 and where 0x80a0 would predict 2,3,4,5,0,1,7 against
a measured 0,2,4,7,1,3,5. Seven candidates refuted now.

16 disc tests green.
This commit is contained in:
Sylpheed RE agent
2026-08-19 11:46:09 +00:00
parent 714c74565f
commit ebc4e08b89
5 changed files with 145 additions and 4 deletions

View File

@@ -698,7 +698,11 @@ pub fn sprite_layer_key(build: &UiBuild, bundle: &[u8], el: &Element) -> Option<
/// — and why `ComposeOptions::include_primitives` is still off by default.
pub fn implied_layer_key(name: &str) -> Option<u32> {
Some(match name {
"palogo_eff0.prm" => 0x0000,
// Full-screen backdrops, measured painting FIRST on their screens.
// `pfbase.tbm` is not a `.prm` at all — a `.tbm` with no sprite and no
// key — but it behaves identically: the save/load screen paints it and
// its `kind = 0x3004` instance before everything else.
"palogo_eff0.prm" | "pfbase.tbm" => 0x0000,
"pteff04.t32" | "pteff05.t32" => 0x8020,
"pteff02.prm" => 0x8030,
"pteff00.prm" => 0xffff_fffe,

View File

@@ -900,3 +900,67 @@ fn order_disagreements_that_change_pixels_are_pinned() {
"the set of order disagreements that actually change pixels has moved"
);
}
/// Two more paint orders read off the running game, from `GP_SAVE_LOAD` — and
/// the first **independent confirmation** of the derived rule.
///
/// The three orders the rule was built from all live in `GP_TITLE.pak`. These do
/// not, and one of them the sort gets exactly right without having been told
/// anything about it:
///
/// * the **slot list header** (9 elements) composites `[7,8,0,1,2,3,4,5,6]`,
/// which is what sorting by layer key gives — including **two tied groups**
/// (`0xb102` ×2 and `0xb210` ×5) that both come out in declaration order, and
/// the unkeyed `pfeff00.prm` fade quad last. Exact on all five bundle
/// instances of it.
/// * the **save/load frame** (13 elements) does not, in exactly two ways, and
/// both are already-known open questions rather than new ones: two unkeyed
/// `pfbase.tbm` background elements paint **first** where the sort puts the
/// keyless last, and the `0xb100` group of four paints `10,11,8,12` where
/// declaration order is `8,10,11,12`.
#[test]
fn the_save_load_screens_match_what_the_running_game_paints() {
skip_without_disc!(root);
let header: [usize; 9] = [7, 8, 0, 1, 2, 3, 4, 5, 6];
let frame: [usize; 13] = [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 8, 12, 9];
let arc = PakArchive::open(root.join("dat").join("GP_SAVE_LOAD.pak")).expect("pak");
let (mut headers, mut frames) = (0usize, 0usize);
for e in arc.entries() {
let Ok(bundle) = arc.read(e) else { continue };
let Some(b) = ui_layout::parse_build(&bundle) else {
continue;
};
let names: Vec<&str> = b.elements.iter().map(|e| e.name.as_str()).collect();
let derived = ui_layout::derived_paint_order(&b, &bundle);
if b.elements.len() == 9 && names[0] == "pftitlebase.t32" && names[6] == "pfeff00.prm" {
headers += 1;
assert_eq!(
derived,
header.to_vec(),
"the slot-list header no longer composites in the order the game paints"
);
}
if b.elements.len() == 13 && names[0] == "pfbase.tbm" {
frames += 1;
// The two known gaps, stated as the measured difference rather than
// asserted away: the sort must at least agree on everything else.
let key = |i: usize| {
let el = &b.elements[i];
ui_layout::sprite_layer_key(&b, &bundle, el)
.or_else(|| ui_layout::implied_layer_key(&el.name))
.unwrap_or(u32::MAX)
};
let dk: Vec<u32> = derived.iter().map(|&i| key(i)).collect();
let mk: Vec<u32> = frame.iter().map(|&i| key(i)).collect();
assert_eq!(
dk, mk,
"the save/load frame's layer-key sequence differs from the game's \
— the only accepted differences are WITHIN a key group\n \
derived {derived:?}\n measured {frame:?}"
);
}
}
assert!(headers >= 3, "found {headers} slot-list headers, expected several");
assert!(frames >= 1, "found {frames} save/load frames");
eprintln!("GP_SAVE_LOAD: {headers} headers exact, {frames} frames agree by layer group");
}

View File

@@ -139,9 +139,16 @@ See [`structures/ui-composable-bundles.md`](structures/ui-composable-bundles.md)
and it is pinned by a test. The third pair (`ptlogo2` vs `ptlogo_tm`) overlaps
by bounding box but shares no opaque pixel; a box test called it a defect and
the alpha says otherwise.
**Next:** it is one element on one screen, so the cheap move is a fourth
measured screen with a tied group — not more static guessing at fields that
have all now been checked. 🔴 **Attempted 2026-08-19 and blocked:** advancing
**Fourth and fifth screens measured (2026-08-19)**, from `GP_SAVE_LOAD`,
reachable now that the Canary threading fix makes the menu dependable. The
9-element slot-list header is **EXACT** under the derived rule — two tied
groups both in declaration order, unkeyed `.prm` last — and it is the first
screen outside `GP_TITLE.pak`, so it *confirms* the rule rather than being
fitted to it. The 13-element save/load frame differs in exactly the two known
ways: unkeyed `pfbase.tbm` backgrounds paint **first** (now covered by
`implied_layer_key`), and the `0xb100` group of four paints `10,11,8,12`.
🔴 **Refuted: the tie-break is not `kind`.** "Descending kind" reproduces
`10,11,8,12` exactly but fails both title groups. Seven candidates refuted now. 🔴 **Attempted 2026-08-19 and blocked:** advancing
past the title is intermittent — **1 success in 3 attempts**, same binary,
same profile, same procedure. ✅ **And now diagnosed one layer deeper:** the
title *does* act on Ⓐ — the press spawns a slot-`(1F)` loader thread (exactly

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

View File

@@ -257,3 +257,69 @@ it is not. That is why the test reads pixels.
So the residual error in the derived order is confined to **one element's blend
on one screen**, and it is pinned: a change that makes it worse fails the test.
## Two more measured orders — and the first independent confirmation (2026-08-19)
The three orders the derived rule was built from all live in `GP_TITLE.pak`, so
they cannot confirm it: the rule was fitted to them. These two are from
`GP_SAVE_LOAD.pak`, read off the running game after the Canary threading fix made
the main menu reachable ([capture](../captures/load-game-oracle.png)).
### ✅ The slot-list header — exact, without being told anything
`GP_SAVE_LOAD` ratc-indices 1/4/5/21/27, 9 elements:
```
measured 7 8 0 1 2 3 4 5 6
derived 7 8 0 1 2 3 4 5 6 EXACT, on all 6 instances
```
| element | key |
|---|---|
| 7 `pffocus1.rat`, 8 `pfslot1f_scr.rat` | `0xb102` **tied ×2** |
| 0 `pftitlebase` | `0xb200` |
| 15 `pfeff11``pftitle1` | `0xb210` **tied ×5** |
| 6 `pfeff00.prm` | none → last |
Both tied groups come out in **declaration order**, which is what the stable sort
gives, and the unkeyed fade quad lands last. Nothing about this screen was fed
into the rule. It is the first evidence that the layer key is a real ordering
mechanism rather than a description of three screens in one pak.
### 🟡 The save/load frame — differs in exactly the two known ways
`GP_SAVE_LOAD` ratc-index 18/24, 13 elements (`is_build` rejects it, so the
compositor never sees it — but it is just as good as *evidence*):
```
measured 0 1 2 3 4 5 6 7 10 11 8 12 9
derived 2 3 4 5 6 7 8 10 11 12 9 0 1
```
1. **Unkeyed elements paint first, not last.** Elements 0 and 1 are
`pfbase.tbm` — a `.tbm`, not a `.prm`, with no sprite and no key — and the
game paints them before everything else. Exactly the splash's
`palogo_eff0.prm` behaviour in a different file type, so `implied_layer_key`
now covers it and the layer-key sequences agree.
2. **A tied group in a non-declaration order.** `0xb100` holds 8 `pfwin`,
10 `pfbtn0`, 11 `pfbtn1`, 12 `pfmsg`, and the game paints
**10, 11, 8, 12**.
That second point is the tie-break question again — and it is now on a *second*
screen, which is what it needed.
### 🔴 Refuted: the tie-break is not `kind`
The new group is suggestive: 10 and 11 are `kind = 0x2002` and 8 and 12 are
`0x0000`, so "descending `kind`, then declaration index" reproduces
`10, 11, 8, 12` exactly. It does not survive the title:
* `0x8083` ×5 — every element is `kind = 0x0000`, so the rule predicts
declaration order `14,15,16,17,18`; the game paints `14,15,18,16,17`.
* `0x80a0` ×7 — kinds are `0x0` and `0x4`, so the rule predicts
`2,3,4,5` then `0,1,7`; the game paints `0,2,4,7,1,3,5`.
So `kind` is not it either, and the count of refuted candidates is now seven:
declaration order, RATC child order, first keyframe time, resting time, resting
X/Y, the `T8aD` header words, and `kind`.