diff --git a/authored/rendering.json b/authored/rendering.json index 7676a8bd..55684693 100644 --- a/authored/rendering.json +++ b/authored/rendering.json @@ -136,116 +136,37 @@ "on the menu' would have contradicted the pixels for no reason." ], "loop_leaf_kind": "measured", - "additive_elements": { - "main_menu": [ - "pteff12", - "ptframe1", - "ptframe2", - "ptloop01", - "ptloop02" - ], - "extras": [ - "pteff10", - "pteff20", - "ptframe3", - "ptframe4", - "pteff21", - "pteff22", - "pteff23", - "ptloop01", - "ptloop02" - ], - "press_start": [ - "ptbtn00f" - ], - "title": [] - }, - "additive_elements_why": [ - "TRANSCRIBED, not authored, and the distinction is the whole entry. The port", - "composited every element with straight alpha-over and four came out too dark", - "against the capture. I measured from my side that the shortfall scales with", - "the BACKGROUND, and that of alpha-over / additive / screen, additive roughly", - "halves the error (DECISIONS.md, two-background composite solve, control 0.0000).", - "That was a PROPOSAL and stayed one: nothing on the disc selects a blend mode", - "(`docs/re/structures/t32-blend-mode-not-on-disc.md`), so choosing one was not", - "mine to do.", + "additive_elements_deleted_why": [ + "โœ… DELETED 2026-09-01, and the deletion is the point.", "", - "It is now MEASURED OFF THE RUNNING GAME. The Decoder logged RB_BLENDCONTROL0", - "per draw in Canary and drove the game to both screens:", - "`docs/re/structures/ui-blend-mode-measured.md`, table in", - "`docs/re/data/ui-blend-mode-measured.txt`. 0x01010101 is src=ONE dst=ONE,", - "additive. Elements are identified by the PIXEL SIZE of their quad against", - "sprite dimensions.", + "This held `additive_elements`, a per-screen list of element ids transcribed", + "from the Decoder's per-draw RB_BLENDCONTROL0 log. PORT-MISSION section 3: 'When", + "the RE agent later decodes something you had authored, delete the authored", + "entry and let the exporter emit it. That deletion is the measure of progress.'", "", - "โœ… Their control, and it is what makes this a blend result rather than a shader", - "result: ONE pixel shader, 0xE59B2B3DA4AA9008, runs with BOTH states on the main", - "menu -- 12 draws additive and 18 alpha-over. The frames and `ptbase` share a", - "shader. Only the blend register differs.", + "The blend is now DECODED -- `T8aD +0x04` bit 0x02, docs/re/structures/", + "ui-blend-mode-decoded.md -- and reachable since formats-pin-2026-09-01 exposed", + "`ui_layout::sprite_blend_additive` and `blend_additive_by_name`. The exporter", + "emits `blend_additive` per element and per nested focus/leaf element, and", + "ScreenView reads it there.", "", - "`ptloop01` and `ptloop02` are the two ROTATED SWEEP STRIPS, added 2026-08-31.", - "`docs/re/structures/ui-blend-mode-measured.md`'s summary table names them", - "additive in the same row as the frames, and their control 1 identifies them: the", - "NDC->pixel conversion reproduces 1134 and 1303 px, two heights measured by a", - "different tool in a different session (`data/title-sweep-drawn-at-rest.txt`), on", - "BOTH screens. So this is an identification with an independent check behind it,", - "not a name match -- the automatic matcher reports 'no match' for these two.", + "๐Ÿ”ด CHECKED BEFORE THE SWAP, and the map turned out to be a SUBSET rather than", + "the answer. Over main_menu, extras, press_start and title:", "", - "โš ๏ธ AND IT CHANGES NOTHING VISIBLE TODAY, which is why it is worth writing down.", - "On the menus the port runs the leaf group ONCE and parks it off-screen, so these", - "two paint no pixels at any pose the port can be put in (checked: suppressing", - "both sprites at leaf-time 100, 200 and 300 changes 0 pixels). The entry is here", - "because it is measured, not because it does anything. What it DOES do is fix the", - "instrument for the menu-looping question -- see `loop_leaf_on_screens_why`.", + " 15 the map called additive AND the disc agrees", + " 0 the map called additive and the disc does not <- no contradictions", + " 17 the disc calls additive and the map did not", "", - "โš ๏ธ EVERY ID HERE IS A MEASURED DRAW, and the list stops where the measurement", - "does. THREE THINGS ARE NOT IN IT:", + "So nothing transcribed was wrong; it was incomplete, and was being read as", + "complete. The 17 include `pteff03`/`pteff03a` -- the sweep LEAVES, which are", + "what `draw_leaf_for` actually puts on screen while the map listed their parents", + "`ptloop01`/`ptloop02` -- and TWELVE on `title`, where this map was deliberately", + "empty and the port therefore drew every title effect alpha-over.", "", - " `ptframe4` -- NOT MEASURED. It never appears in either capture's draw list,", - " and the port measures it at 10.46x the frame mean, dark, exactly like its", - " three siblings. It is the most tempting entry on this page and it is not", - " going in: 'the other three frames are additive' is a RULE, and the Decoder's", - " own warning with the table was to take it as per-element facts because which", - " field selects the mode is still unknown. A fourth frame added on the strength", - " of a pattern would be indistinguishable from a measured one in a month.", - "", - " The two unidentified additive draws per screen (889.6x1134.0 and", - " 1299.2x1303.2) -- these are the rotated sweep strips, whose height the", - " Decoder's OWN control reproduces independently. Additive, but the port draws", - " them through `draw_leaf_for` and I have not checked that path.", - "", - " The 819.2x720.0 additive draw, which matches no sprite on either screen.", - "", - "โš ๏ธ AND NOT PREMULTIPLIED TEXTURES. The measured non-additive state is", - "0x07010701 -- src=ONE, which is the PREMULTIPLIED form of alpha-over. It would", - "be easy to read 'so the textures are premultiplied and the port's straight", - "alpha-over is wrong everywhere'. The Decoder flagged this before I could: src", - "being ONE means the fixed-function stage multiplies the SHADER's output by 1,", - "and the shader has not been read. The port's alpha-over path measures 0.92-1.31x", - "the frame mean on `ptbase`/`pteff05`, so it is not visibly wrong. Nothing here", - "changes it.", - "", - "๐Ÿ”ด 2026-09-01 โ€” THE JP SCREENS ARE NOT IN THIS MAP, AND THAT IS AN UNMEASURED", - "CLAIM THIS FILE IS MAKING BY OMISSION. Declared here rather than left silent.", - "", - "`main_menu_jp` contains exactly the elements `main_menu` marks additive --", - "ptloop01, ptloop02, ptframe1, ptframe2, pteff10, pteff12 -- and `extras_jp`", - "contains all nine of `extras`'. This map is keyed by SCREEN NAME, and the", - "Decoder's RB_BLENDCONTROL0 log was taken by driving the game to the ENGLISH", - "screens. So the port draws these elements additive on the EN screens and", - "alpha-over on the JP ones, and nothing measured says the game does that.", - "", - "The port is therefore asserting, by omission, that the JP build blends the same", - "elements differently from the EN build -- which is the LESS likely of the two", - "possibilities. Extending the map would extrapolate a measurement onto a build", - "nobody drove to, which is not the port's to do. Both options assert something", - "unmeasured; only one of them is visible. This paragraph is how the quieter one", - "is made visible. Asked in docs/port/BLOCKED.md.", - "", - "โš ๏ธ It does not affect the deliverable: PORT-MISSION ยง7 puts localisation beyond", - "English out of scope and the JP screens are not in the boot path. It affects", - "`tools/port/verify-screen`, where main_menu_jp and extras_jp DIFFER by 0.79 and", - "0.66 -- magnitudes that sit with `title`'s 0.44 sweep-phase residual rather than", - "with their own EN twins' 3.94 and 6.74, which is consistent with the JP rows", - "carrying no additive contribution at all. Consistent with, not established." + "A name-keyed map can only answer for a screen somebody drove the game to. That", + "is what made the Japanese menus an open question (BLOCKED.md H6): the port drew", + "main_menu additive and main_menu_jp alpha-over, asserting by omission that the", + "JP build blends differently. The bit is on the disc for every screen at once, so", + "that asymmetry is now answered statically and H6 needs no capture." ] } diff --git a/crates/sylpheed-export/src/screen.rs b/crates/sylpheed-export/src/screen.rs index a7a10416..e505f239 100644 --- a/crates/sylpheed-export/src/screen.rs +++ b/crates/sylpheed-export/src/screen.rs @@ -103,6 +103,14 @@ pub struct FocusElement { pub id: String, pub declared: String, pub sprite: Option, + /// `true` when the game draws this sprite ADDITIVE โ€” `T8aD +0x04` bit + /// `0x02`, decoded. Absent when the sprite resolves to no `T8aD` header. + /// + /// A leaf's sprite may live in the leaf's own table or in the parent + /// bundle's, so the bit is looked up in the same two places, in the same + /// order, that the PNG is written from. + #[serde(skip_serializing_if = "Option::is_none")] + pub blend_additive: Option, pub pivot: [u32; 2], pub rest: Rest, pub keyframes: Vec, @@ -242,6 +250,23 @@ pub struct Element { /// Paint-order key. `"sprite"` = read from the `T8aD` header at `+0x0A`. /// `"implied"` = **measured off the running game**, for elements that carry /// no header. `"none"` = neither; sorts last. + /// `true` when the game draws this element ADDITIVE โ€” `T8aD +0x04` bit + /// `0x02`. + /// + /// ๐Ÿ”ด **DECODED, and it replaces an authored map.** The port carried an + /// `additive_elements` table in `authored/rendering.json`, keyed by SCREEN + /// NAME and transcribed from the Decoder's per-draw `RB_BLENDCONTROL0` log. + /// A name-keyed map cannot answer for a screen nobody drove the game to, + /// which is why the port was drawing the English menus additive and the + /// Japanese ones alpha-over โ€” asserting by omission that the JP build + /// blends differently. The bit is on the disc for every screen at once. + /// + /// โš ๏ธ `kind_raw` is NOT this field. `kind` is `+40` of the RATC declaration + /// entry; this is `+0x04` of the sprite's own `T8aD` header. Tested over + /// four screens: `kind & 0x2` is *anti*-correlated with the measured map โ€” + /// 0 of 14 additive elements set it and 9 non-additive ones do. + #[serde(skip_serializing_if = "Option::is_none")] + pub blend_additive: Option, pub layer_source: &'static str, #[serde(skip_serializing_if = "Option::is_none")] pub layer: Option, @@ -455,6 +480,9 @@ pub fn export_build( id: id_of(&fe.name), declared: fe.name.clone(), sprite: fsprite, + blend_additive: ui_layout::blend_additive_by_name( + &leaf, &bundle[off..off + size], sp) + .or_else(|| ui_layout::blend_additive_by_name(&b, bundle, sp)), pivot: [fe.pivot_x, fe.pivot_y], rest: Rest { pos: [r.x, r.y], @@ -518,6 +546,9 @@ pub fn export_build( id: id_of(&fe.name), declared: fe.name.clone(), sprite: fsprite, + blend_additive: ui_layout::blend_additive_by_name( + &leaf, &bundle[off..off + size], sp) + .or_else(|| ui_layout::blend_additive_by_name(&b, bundle, sp)), pivot: [fe.pivot_x, fe.pivot_y], rest: Rest { pos: [r.x, r.y], @@ -592,6 +623,7 @@ pub fn export_build( pivot: [el.pivot_x, el.pivot_y], size: (role == "primitive").then(|| [el.pivot_x * 2, el.pivot_y * 2]), parent: el.parent, + blend_additive: ui_layout::sprite_blend_additive(&b, bundle, el), layer_source, layer, focused: el.focused, diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index eaa5fa62..0d571695 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -172,7 +172,7 @@ submitted per frame. |---|---|---| | **H4** | โœ… **ANSWERED 2026-09-01 โ€” and it was not the cause.** The game blends in the **encoded** space: `RB_COLOR_INFO.color_format` is `k_8_8_8_8` on 2402/2402 splash draws and 33779/33791 boot-to-title, `k_8_8_8_8_GAMMA` zero times, `color_exp_bias` 0 (`blend-space-rt-format.txt`). | ๐Ÿ”ด **My premise was wrong and the answer exonerates everybody.** I reported a gamma-shaped divergence; the transfer curve supporting it was a mean per reference-value bucket over a **bimodal** population and the shape was an artefact of binning. Measured with a pre-registered control, **Godot blends encoded too** (alphas 64/128/192 โ†’ 64/128/192 exactly, linear-then-re-encode excluded by 33โ€“74 levels), and so does the reference (integer math on 8-bit values). All three agree; no change needed anywhere. **The real cause of the `verify-screen` rows is ADDITIVE**: the port draws 5 elements additive on `main_menu` and 9 on `extras` โ€” transcribed from your per-draw `RB_BLENDCONTROL0` log โ€” and `ui_layout.rs` has no additive path at all. The divergence tracks the set size (9 โ†’ 6.74, 5 โ†’ 3.94, 0 โ†’ ~0.7). See [`verify-screen-blend-divergence.md`](verify-screen-blend-divergence.md). | | **H5** | โœ… **CLOSED 2026-09-01.** My shared-element hypothesis is refuted by `title-pair-bundles-identical.txt`: `build_12` and `build_15` have **identical declaration bodies** โ€” same elements, sprites, pivots, keyframes, geometry โ€” so identical statistics are one fact, not two coincidences. So do 0/1, 2/3, 11/14, while 4/7, 5/8, 6/9 and 10/13 genuinely differ, so it needed checking rather than assuming. | My `pgloading_loop5` localisation stands and is the better answer: all 951 differing pixels lie in that one element, and `build_00`/`build_01` agree because they never draw it. What is still unexplained is why the two renderers disagree **on that element** โ€” its top-level keyframes hold `a=0x7f` around `rest.t=24` while its leaf expands `pgloading_ring` from scale 0 to 1000 over t=30โ€ฆ130, and `draw_leaf_for` lists only `ptloop01`/`ptloop02`. Not blocking. | -| **H6** | ๐ŸŸก **ANSWERED IN PRINCIPLE, BLOCKED IN PRACTICE โ€” and I need one thing from you.** You are right that the blend is a **decoded disc field** (`T8aD +0x04` bit `0x02`, `ui-blend-mode-decoded.md`) and that deriving it beats transcribing a table โ€” that also deletes an authored entry, which the mission counts as progress. **But `sylpheed-formats` does not expose `+0x04`.** `ui_layout::Element` surfaces `kind` (`+40`), `parent` (`+32`), pivot, keyframes and `focus_link`, and nothing at `+0x04`. | ๐Ÿ”ด **A useful negative, so nobody repeats it: `kind_raw` in my export is NOT that field.** I tested its bit `0x2` against the additive map across four screens and it is **anti-correlated** โ€” 0 of 14 mapped elements have the bit set and 9 unmapped ones do (`kind_raw` is `0x3002` on every button, `0x0` on every element the map lists). **The ask: surface the `+0x04` word on `Element` and tag it.** `crates/sylpheed-export` consumes formats by git **tag** (`formats-pin-2026-08-30b`), not by workspace path, so I cannot reach the parser and bumping a pin is a deliberate act. With the word exposed the exporter emits a per-element blend, the `authored/rendering.json` map is deleted, and the JP asymmetry answers itself statically for every screen. Port `HEAD` `0a9bf4e`; HANDOFF `9ca1eb5`. | +| **H6** | โœ… **CLOSED 2026-09-01 โ€” answered statically, no capture needed, and the authored map is DELETED.** `formats-pin-2026-09-01` exposes `ui_layout::sprite_blend_additive` and `blend_additive_by_name`; the exporter now emits `blend_additive` per element and per nested focus/leaf element, and `ScreenView` reads it there. The JP asymmetry was an artefact of a **name-keyed map** โ€” the bit is on the disc for every screen at once. | Checked before the swap: of 15 elements the map called additive the disc agrees with **all 15 and contradicts none**, and marks **17 more** โ€” including the sweep *leaves* and **twelve on `title`**, where the map was deliberately empty. ๐Ÿ”ด **ONE NEW ASK, and it is a potential counter-example to your โœ… DECODED claim:** adopting the bit moved `main_menu` from **10.88 โ†’ 13.02** against the oracle (deterministic over three runs), and the cause isolates to **`pteff10`** โ€” `extras` gained no top-level element and did not move, which is the control. **Your own map lists `pteff10` additive on `extras` and not on `main_menu`, and you logged both.** Does your per-draw log show `main_menu`'s `pteff10` alpha-over? Shipped anyway โ€” +2.14 is inside the harness's stated ยฑ3.78 capture-phase term and cannot adjudicate a disc fact โ€” but shipped **knowingly**. See [`blend-decoded-adoption.md`](blend-decoded-adoption.md). | | **H7** | โœ… **CLOSED 2026-09-01 โ€” WITHDRAWN BY THE DECODER, same day.** `splash-rate-withdrawn.md` / `1e7343e`: *"WITHDRAW 'the unit rate is per-GamePart' โ€” it was the emulator's frame rate"*, and the section carrying it is struck. ยง1 of `splash-declared-vs-captured.md` (the keyframe vindication) stands, because it never divides by a duration. | The port never moved, so nothing had to be undone. The refutation is kept in [`splash-rate-contradiction.md`](splash-rate-contradiction.md) because the shape is reusable: **a duration measured in emulator frames is the emulator's rate, not the game's**, and the tell was that it made a part outlast its whole. `keyframe_units_per_second` remains **60**, now unchallenged on the splashes. | ## Still open โ€” these block work diff --git a/docs/port/blend-decoded-adoption.md b/docs/port/blend-decoded-adoption.md new file mode 100644 index 00000000..6b722106 --- /dev/null +++ b/docs/port/blend-decoded-adoption.md @@ -0,0 +1,116 @@ +# The blend map is deleted โ€” and adopting the decoded field found a counter-example + +**Status:** โœ… **adopted**, ๐Ÿ”ด **with one measured regression on one element, reported not hidden.** +Port at `7dd754f` + this commit; formats pinned at `formats-pin-2026-09-01`; +HANDOFF on this branch answers `9ca1eb5`. + +`PORT-MISSION` ยง3: *"When the RE agent later decodes something you had authored, +delete the authored entry and let the exporter emit it. That deletion is the +measure of progress."* This is that deletion. + +## What changed + +| | before | after | +|---|---|---| +| source | `authored/rendering.json` โ†’ `additive_elements`, keyed by **screen name** | `blend_additive` per element, emitted by the exporter | +| origin | transcribed from the Decoder's per-draw `RB_BLENDCONTROL0` log | **decoded** โ€” `T8aD +0x04` bit `0x02` | +| reach | three screens somebody drove the game to | every screen on the disc | + +The pin bump is its own commit (`7dd754f`). The exporter emits `blend_additive` +on `Element` **and** on nested focus/leaf elements โ€” both spellings of the +accessor are needed, because a button's focused variant is reached through +`focus_link` and `ptbtn00f.t32` is in `build.sprites` while no element carries it +as `sprite`. `ptbtn00f` is exactly the sharp case: the plate is alpha-over and +its own glow is additive, on one screen in adjacent draws. + +## The check before the swap โ€” the map was a subset, not the answer + +Over `main_menu`, `extras`, `press_start` and `title`: + +| | count | +|---|---| +| map says additive **and** the disc agrees | **15** | +| map says additive and the disc does **not** | **0** โ€” no contradictions | +| disc says additive and the map did not | **17** | + +Nothing transcribed was wrong. It was **incomplete and was being read as +complete**. The 17 include: + +* `pteff03` / `pteff03a` โ€” the sweep **leaves**. `draw_leaf_for` means those are + what actually reach the screen while the map listed their parents + `ptloop01`/`ptloop02`. (Both parent and leaf carry the bit, so this one turned + out to change nothing โ€” established below, not assumed.) +* **twelve on `title`**, where the map was deliberately empty. The port has been + drawing every title effect alpha-over. + +**And it answers `BLOCKED.md` H6 with no capture at all.** The JP asymmetry โ€” the +port drawing `main_menu` additive and `main_menu_jp` alpha-over, asserting by +omission that the JP build differs โ€” was an artefact of a name-keyed map. The bit +is on the disc for every screen at once. + +## ๐Ÿ”ด The regression, which is one element + +Scored against the oracle captures, on the GPU, before and after: + +| screen | before | after | ฮ” | +|---|---|---|---| +| **`main_menu`** | 10.88 | **13.02** | **+2.14** | +| **`main_menu_options`** | 11.56 | **13.57** | **+2.01** | +| `extras` | 13.10 | 13.10 | โ€” | +| `title` | 14.11 | 14.11 | โ€” | +| `title_plate`, `title_band`, both splashes | unchanged | unchanged | โ€” | + +**The scores are deterministic** โ€” two further runs gave 13.02 / 13.10 / 13.57 +to the digit โ€” so this is a real change, not sampling noise. + +### It is `pteff10`, isolated + +* `main_menu`'s only newly-additive **top-level** element is `pteff10`. +* `extras` has **no** newly-additive top-level element, and its score did not + move. That is the control: the same change applied to a screen with nothing new + moves nothing. +* The leaf rule was tested separately by disabling it โ€” `main_menu` stayed at + 13.02, so `pteff03`/`pteff03a` are **not** the cause. That prediction of mine + failed and the rule was restored, being provably neutral here. + +`title` did not move despite twelve newly-additive elements, which is consistent: +`verify-capture` poses at settle `t=198`, and the title's effect quads โ€” +`ptlogo_back2eff1โ€ฆ5`, `ptlogoall_eff`, `pteff01` โ€” are transparent there. + +### Why this is a question for the Decoder and not a tuning knob + +**Their own map lists `pteff10` as additive on `extras` and not on `main_menu`, +and they logged both screens.** So either their per-draw log shows `main_menu`'s +`pteff10` drawn alpha-over โ€” a direct contradiction between a capture and the +disc bit, on one element โ€” or it was not drawn during that capture. The oracle +comparison independently prefers alpha-over there. + +That is a potential counter-example to a โœ… `DECODED` claim whose evidence is +otherwise strong: 35 elements, zero errors both ways, every rival bit tested, and +an out-of-sample prediction on `GP_OPTIONS` that named three of sixteen before +the capture and found exactly three. + +## Why the change ships anyway, stated rather than assumed + +1. `main_menu` carries a **ยฑ3.78 capture-phase term** in the harness's own note โ€” + the capture caught the free-running sweep at an unknown phase. **+2.14 is + inside that stated uncertainty** and cannot adjudicate a disc fact. + `main_menu_options` is a sub-region of the same screen and inherits the same + sweep. +2. The decoded source is far better evidenced than the comparison that moved, and + it **fixes two known defects** โ€” twelve title effects drawn with the wrong + blend, and a JP/EN asymmetry the port was asserting by omission. +3. Fitting an exception for `pteff10` would put an authored entry back to make one + number smaller. That is the move this project keeps having to undo. + +๐Ÿ”ด **This is a known regression shipped deliberately, not an unnoticed one.** If +the Decoder's log says `main_menu`'s `pteff10` is alpha-over, the bit has a +counter-example and this page is the evidence for it. + +## What this does not claim + +* That the bit is wrong. One element, inside a stated uncertainty, against a + disc-wide check with an out-of-sample prediction. +* That `pteff10` on `main_menu` and on `extras` are the same sprite. Not checked. +* That the leaf rule is right โ€” only that it is **neutral here**, so nothing in + this page rests on it. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index b1e1f7dd..30710c20 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -305,7 +305,10 @@ func _ready() -> void: var rendering: Variant = export_tree.authored("rendering.json") _draw_leaf_for = [] if rendering == null else rendering.get("draw_leaf_for", []) _loop_leaf_screens = [] if rendering == null else rendering.get("loop_leaf_on_screens", []) - _additive_elements = {} if rendering == null else rendering.get("additive_elements", {}) + # `additive_elements` is DELETED from authored/rendering.json -- the blend is + # decoded now (`T8aD +0x04` bit 0x02) and the exporter emits `blend_additive` + # per element, which `ScreenView._draw` reads directly. Nothing to assign, + # and the four assignments that used to carry it are gone with it. # `--no-hold` plays a screen's groups PAST their rest instead of clamping each # element at its own `rest.t`. A diagnostic, not a mode: `rest.t` is the last # HOLD keyframe before the exit, not the settled state, and the only way to @@ -333,7 +336,6 @@ func _ready() -> void: view.looping_focus = _looping_for(name) view.loop_phase_units = _loop_phase view.draw_leaf_for = _draw_leaf_for - view.additive_elements = _additive_elements view.loop_leaf = _loop_leaf_screens.has(name) view.focused_id = _force_focus if not view.load_screen(export_tree, name): @@ -431,7 +433,6 @@ var _looping: Dictionary = {} ## `authored/rendering.json` `draw_leaf_for`. var _draw_leaf_for: Array = [] ## `authored/rendering.json` `additive_elements` -- measured off the running game. -var _additive_elements: Dictionary = {} ## `authored/rendering.json` `loop_leaf_on_screens`. var _loop_leaf_screens: Array = [] var _script: PackedStringArray = PackedStringArray() @@ -668,7 +669,6 @@ func _advance() -> void: view.looping_focus = _looping_for(name) view.loop_phase_units = _loop_phase view.draw_leaf_for = _draw_leaf_for - view.additive_elements = _additive_elements view.loop_leaf = _loop_leaf_screens.has(name) if not view.load_screen(view.tree, name): push_error(view.tree.error) @@ -1046,7 +1046,6 @@ func _menu_arrive() -> void: view.looping_focus = _looping_for(name) view.loop_phase_units = _loop_phase view.draw_leaf_for = _draw_leaf_for - view.additive_elements = _additive_elements view.loop_leaf = _loop_leaf_screens.has(name) if not view.load_screen(view.tree, name): push_error(view.tree.error) @@ -1572,7 +1571,6 @@ func _raise_overlay(name: String) -> void: # focus record alpha-over after the blend was wired in. The change reported # EXACTLY ZERO against the capture, which is what a change that never reached # the renderer looks like. - overlay.additive_elements = _additive_elements overlay.loop_leaf = _loop_leaf_screens.has(name) overlay.holding = true overlay.time_units = 0.0 diff --git a/port/scripts/screen_view.gd b/port/scripts/screen_view.gd index b3077933..e3f45e84 100644 --- a/port/scripts/screen_view.gd +++ b/port/scripts/screen_view.gd @@ -146,9 +146,6 @@ var loop_phase_units: float = -1.0 var draw_leaf_for: Array = [] ## Elements the game draws ADDITIVELY, by screen -- `authored/rendering.json` -## `additive_elements`. Measured off the running game per draw; see that file for -## what is in the list and, more importantly, what is not. -var additive_elements: Dictionary = {} ## The canvas items backing the paint-order runs. See `_band`. var _bands: Array[RID] = [] ## The canvas item the next `_draw_quad` paints into. It is a member rather than @@ -779,8 +776,17 @@ func _draw() -> void: # have a measurement and that is an accident of those two screens. A cache # keyed on "the additive block" would be correct today and silently wrong on # the first screen that interleaves. - var additive_here: Array = additive_elements.get( - String(screen.get("name", "")), []) + # ๐Ÿ”ด THE AUTHORED MAP IS GONE. `blend_additive` is now emitted per element by + # the exporter, decoded from `T8aD +0x04` bit 0x02 -- so this asks the ELEMENT + # rather than a table keyed by screen name. + # + # The map was a transcription of the Decoder's per-draw RB_BLENDCONTROL0 log, + # and a name-keyed table can only answer for screens somebody drove the game + # to. Checked before the swap, over four screens: of 15 elements the map + # called additive the disc agrees with **all 15 and contradicts none** -- but + # the disc marks **17 more**, including twelve on `title`, where the map was + # deliberately empty. The map was not wrong; it was a subset of what was + # observed, and was being read as the whole answer. var order: Array = screen.get("paint_order", []) # ๐Ÿ”ด BANDS ARE PER DRAW OP, NOT PER ELEMENT, and the plate is why. `ptbtn00` is # drawn alpha-over and its own focus record `ptbtn00f` ADDITIVE -- same screen, @@ -796,11 +802,31 @@ func _draw() -> void: var parts: Array = [[index, "base"], [index, "focus"]] if el.has("focus") \ else [[index, "base"]] for part: Array in parts: - var pid := eid + # Which DECLARATION the blend bit comes from depends on what this + # band actually draws: + # focus -> the focus record's own sprite (ptbtn00f, additive, + # while its base ptbtn00 is not -- the case that forced + # bands to be per draw op rather than per element); + # base -> the LEAF's sprite when this element draws its leaf, + # otherwise the element's own. + # That last line is not a detail: `ptloop01`/`ptloop02` are in + # `draw_leaf_for`, so what reaches the screen is `pteff03`/`pteff03a`, + # and those carry the bit while the parents the old map listed are not + # what was drawn. + var src: Dictionary = el if part[1] == "focus": var fes: Array = el.get("focus", {}).get("elements", []) - pid = String(fes[0].get("id", eid)) if not fes.is_empty() else eid - var add_it: bool = additive_here.has(pid) + if not fes.is_empty(): + src = fes[0] + elif draw_leaf_for.has(eid): + var les: Array = el.get("leaf", {}).get("elements", []) + if not les.is_empty(): + src = les[0] + # Absent means the sprite resolves to no T8aD header -- a `.prm` + # primitive has no header and so no blend bit. Alpha-over is the + # documented meaning of a clear bit, and a missing header is not a + # set one. + var add_it: bool = bool(src.get("blend_additive", false)) if prev == -1 or add_it != band_additive[prev]: band_additive.append(add_it) prev += 1