This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/ui-prm-primitives.md
Sylpheed RE agent 393e549752 formats: settle what .prm elements are — untextured quads marked by kind bit 0x10
369 .prm elements exist in the disc's screen builds and every composite is
missing them. Swept statically:

  * none of the 369 has a RATC child of its own name — no payload, nothing to
    draw, so .prm is a primitive and not a sprite;
  * kind & 0x10 and a .prm name agree with ZERO exceptions in either direction
    over all 965 builds, so the format marks primitives as a decoded field and a
    port need not parse filenames;
  * 361 of 369 are exactly 1280x720 at scale 100% in the corner, and their
    keyframe 'fade' ARGB is overwhelmingly black at some alpha — these are the
    fade-to-black, dim-behind-menu and flash layers, i.e. the PRMD dim-quad the
    compositor's backdrop has been standing in for.

Refuted before believing: drawing them at Element::rest() is wrong. The title's
pteff00.prm is opaque -> transparent -> transparent -> opaque, a transition whose
resting pose is the transparent plateau; rest() picks by longest dwell and lands
on the opaque endpoint, which is painted LAST on that screen and would black out
the title. A test asserts that wrong answer deliberately so that fixing the
resting rule fails it and leads to the note.

No compositing change: the resting rule is not .prm-specific and has to be A/B'd
against the title framebuffer capture first.
2026-08-19 06:30:05 +00:00

4.5 KiB
Raw Blame History

.prm elements are untextured full-screen quads, and kind bit 0x10 says so

Status: CONFIRMED statically across all 965 screen builds on the disc. 🔴 Drawing them at Element::rest() is refuted — it would black out the title screen. the resting rule for a fade group is unsolved, and that is the blocker on actually compositing them.

What they are

Every composite the port produces is missing its .prm elements: they resolve to no sprite, so compose skips them. 369 of them exist. Swept over the disc:

.prm elements in screen builds 369
…with a RATC child of their own name (a payload) 0
…with kind & 0x10 set 369
non-.prm elements with kind & 0x10 set 0
…with pivot × 2 == 1280×720 (the design space) 361
…resting at scale 100 %, position (0,0) 361 / 356

Three things follow, none of them inferred:

  1. They carry no texture. Not one has a payload child; there is nothing in the bundle for them to draw. .prm is a primitive, not a sprite.
  2. The format marks them. kind & 0x10 and the .prm extension agree with zero exceptions in either direction. A port can classify an element as a primitive from the declaration table alone, without parsing filenames — which is what a decoded field is for.
  3. They are screen-sized colour fills. 361 of 369 are exactly the design space at 1:1 in the corner. The 8 that are not are small coloured quads (844×600 at (291,60), and two degenerate 0×720).

Where the colour is

The keyframe's fade word, ARGB — the field named for the alpha ramp it drives on a sprite. On a .prm there is no sprite to modulate, so it is the fill. Resting values across the disc:

 189  0x00000000   (transparent — nothing on screen)
 102  0xff000000   (opaque black)
  32  0x7f000000     6  0x60ff0000 (red)     6  0x0000ffd4 (cyan)
  20  other blacks   6  0x008000ff (violet)  2  0xf0ffffff (near-white flash)

Overwhelmingly black at some alpha: these are the screen's fade-to-black, dim-behind-a-menu and flash layers. That matches the standing note on ComposeOptions::backdrop, which describes the compositor's dim slate as a stand-in for "the PRMD dim-quad" — this is that quad, and it is in the file.

Refuted: you cannot just draw them at rest

The obvious next step — treat a .prm as a quad and blit it at rest() — is wrong, and would have been a visible disaster rather than a subtle one.

The title screen's pteff00.prm:

kf0  fade=0xff000000  t=12     opaque black
kf1  fade=0x00000000  t=64     transparent
kf2  fade=0x00000000  t=74     transparent
kf3  fade=0xff000000  t=None   opaque black

That is a transition: the screen fades up out of black, sits clear, and fades back down on the way out. What the title screen shows is the transparent plateau, kf1kf2.

Element::rest() picks the keyframe with the largest gap to the next keyframe's time — 12→64 is the biggest gap, so it picks kf0, opaque black. And in the measured paint order pteff00.prm is painted last on the title screen. Drawn at rest(), it is a full-screen opaque black quad over everything.

The rule's assumption is the problem: it treats a keyframe as a pose that is held until the next one. For a fade it is the start of a ramp. The pose a screen actually rests at is the plateau — a run of consecutive keyframes with equal values — which for this group is kf1kf2, transparent.

This is pinned by the_title_fade_quad_is_a_transition_and_rest_picks_the_wrong_end, which asserts the current (wrong) answer on purpose, so that fixing the resting rule fails the test and leads whoever does it here.

What is not settled

  • The resting rule. "Longest plateau of equal consecutive keyframes" is the candidate, and it is not .prm-specific — it would change rest() for every element on the disc, including the ones checked against the title framebuffer capture. It has to be A/B'd against those before it can land. That is the next step and the reason .prm compositing is not in this change.
  • Blend mode. A dim quad at 0x7f000000 is presumably straight alpha over what is beneath, but the flash (0xf0ffffff) and the coloured ones (0x60ff0000) may well be additive. Nothing measured.
  • The 8 non-full-screen ones, including two with a zero dimension.
  • 🟡 kind = 0x3010 (38 elements) is 0x10 plus 0x3000, the button-record bits — a primitive that is part of a button. Unexamined.