re: EXTRAS complete -- ptframe4, pteff21/22/23 and pteff10 are all ADDITIVE

The four elements the port measured as the worst on EXTRAS, and which appeared
in no draw, were in a draw all along: the 24-index additive batch holds six
quads and Canary printed the first two. Cap raised to 64, screen re-captured,
all six named. Same draw as ptframe3, whose state was already measured -- the
one-way implication doing real work.

pteff10 is identified too, and it needed the resting SCALE: it ships as 409x144
and is drawn at 200 % x 500 % = 816x720. The matcher's 'try 1x and 2x' rule
could not name it at any scale and reported a near miss against something else,
which is a failure wearing the clothes of an answer. Candidates are now the
declaration's pivot*2 scaled by the resting keyframe as well as the texture at
1x and 2x, and the tolerance is the log's own NDC print quantisation rather than
a chosen number.

Flagged rather than buried: pteff10 measuring additive is in tension with the
port measuring it nearly exact under alpha-over. Both can be true for a dim
semi-transparent glow over a dark background, and it is the one row a rendering
check does not corroborate.

Also stated: the three full-screen alpha-over draws are NOT individually
identified -- four elements declare 1280x720 -- so the label on those rows is a
candidate, not an identification.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
This commit is contained in:
sylph-decoder
2026-08-31 06:59:11 +00:00
parent f0b4f0ad5f
commit fa887b4e5f
5 changed files with 389 additions and 29 deletions

View File

@@ -45,32 +45,49 @@ def blend_name(raw):
return "%s+%s" % (FACTOR.get(src, src), FACTOR.get(dst, dst))
def sprite_sizes(builds):
"""name -> (w, h), straight off the disc via the formats crate.
def candidates(builds):
"""Every size a UI draw could legitimately have, with a label and a basis.
`builds` is a list because the live title is TWO builds composited -- 4 draws
the art and 2 draws the `PRESS (A)` plate over it -- so a capture of the
title contains elements from both and a one-build size table cannot name
them all.
Two bases, because neither alone names every element:
declared the declaration's `pivot * 2` scaled by the RESTING keyframe's
scale_x/scale_y. This is the only thing that names `pteff10`,
which ships as 409x144 and is drawn at 200 % x 500 % = 816x720 --
a scale-guessing matcher called it "no match" and offered a near
miss against something else instead.
texture the decoded `.t32`'s own pixel size, at 1x and 2x. Needed because
the pivot is NOT always half the texture (`ptmsg2` declares
384x38 for a 354x38 sprite) and because a button's focused `f`
variant has a texture and no declaration of its own.
`builds` is a list: the live title is TWO builds composited, 4 for the art
and 2 for the `PRESS (A)` plate.
"""
out = subprocess.run(
["cargo", "run", "--release", "-q", "-p", "sylpheed-formats",
"--example", "frame_alpha_census", "--"] + [str(b) for b in builds],
capture_output=True, text=True, cwd="/work",
env={**__import__("os").environ, "SYLPHEED_DISC": "/disc"}).stdout
sizes = {}
for line in out.splitlines():
env = {**__import__("os").environ, "SYLPHEED_DISC": "/disc"}
def run(example):
return subprocess.run(
["cargo", "run", "--release", "-q", "-p", "sylpheed-formats",
"--example", example, "--"] + [str(b) for b in builds],
capture_output=True, text=True, cwd="/work", env=env).stdout
out = []
for line in run("rest_scale_of").splitlines():
m = re.match(r"(\S+\.(?:t32|prm|rat))\s+\d+,\d+\s+\d+\s+\d+\s+([\d.]+)x([\d.]+)", line)
if m:
out.append((m.group(1), "declared", float(m.group(2)), float(m.group(3))))
for line in run("frame_alpha_census").splitlines():
m = re.match(r"(\S+\.t32)\s+(\d+)x(\d+)", line)
if m:
sizes[m.group(1)] = (int(m.group(2)), int(m.group(3)))
return sizes
w, h = float(m.group(2)), float(m.group(3))
out.append((m.group(1), "texture", w, h))
out.append((m.group(1), "texture@2x", w * 2, h * 2))
return out
def main():
path = sys.argv[1]
spec = sys.argv[sys.argv.index("--build") + 1] if "--build" in sys.argv else "5"
builds = [int(b) for b in spec.split(",")]
sizes = sprite_sizes(builds)
cands = candidates(builds)
lines = open(path).read().splitlines()
rows = []
for i, line in enumerate(lines):
@@ -95,19 +112,17 @@ def main():
w = (max(xs) - min(xs)) / 2 * W
h = (max(ys) - min(ys)) / 2 * H
heights.append(h)
# A sprite is drawn at its native size or at 2x: the screen is
# 1280x720 and the design space is 640x360, so `ptbase` (640x360)
# covers the screen while `pteff05` (1280x720) is 1:1. Both are
# tried and the scale is reported, because guessing one would
# silently mis-name half the draws.
best, bd, bs = "-", 1e9, 1
for n, (sw, sh) in sizes.items():
for sc in (1, 2):
d = abs(sw * sc - w) + abs(sh * sc - h)
if d < bd:
best, bd, bs = n, d, sc
tag = ("%s @%dx" % (best, bs)) if bd <= 8 else \
"(no match, nearest %s @%dx off %.0f)" % (best, bs, bd)
# The log prints NDC to TWO DECIMALS, so a width is quantised to
# 0.01 * 1280 / 2 = 6.4 px and a height to 3.6 px. The tolerance
# below is that quantisation, not a fudge factor: a match inside it
# is as close as this instrument can report.
best, bd, bb = "-", 1e9, ""
for n, basis, sw, sh in cands:
d = abs(sw - w) / 6.4 + abs(sh - h) / 3.6
if d < bd:
best, bd, bb = n, d, basis
tag = ("%s [%s]" % (best, bb)) if bd <= 2.0 else \
"(no match, nearest %s [%s] off %.1f quanta)" % (best, bb, bd)
print("%4d %4d %4d 0x%08X %-20s %7.1f x %7.1f %s"
% (idx, prim, nidx, raw, blend_name(raw), w, h, tag))
tall = sorted(h for h in heights if h > 900)