Files
Sylpheed/tools/re-capture/plateauless_endstate.py
Sylpheed RE agent 6be691222b re(ui): the structural case for last -- 2 293 of 2 305, checked disc-wide
The weakness in the rest-rule finding was that `last` had been SCORED on
only two elements. It cannot be scored on more -- only two ambiguous
elements sit on a screen with a live capture -- but the entry -> hold ->
exit model makes a prediction that can be checked on all 2 305: what does
each element's FINAL keyframe look like?

  final keyframe invisible (a = 0)          1 618   transient: gone at rest
  final keyframe visible, at max alpha        675   faded in and stopped
  final keyframe visible, BELOW max alpha      12   genuinely unclear

Of the 687 that end visible, 472 have monotonically non-decreasing alpha
-- a plain fade-in that stops, [0, 255] over two keyframes in the
commonest case (pjex_eff.rat, pghud_speed_cut.t32) -- and another 203 end
at their maximum after dipping.

So `last` is structurally defensible for 2 293 of 2 305 (99.5 %), against
a dwell rule that returns a mid-movement frame by construction. Observed
correct for 2, structural for 675, model-consistent for 1 618, unclear
for 12.

The assumption carrying the 1 618 is stated rather than buried: that a
plateau-less element's animation has finished by the time the screen is
settled. The draw log establishes exactly this for the two splash glows
(drawn frames 94-115, logos 116-211) and establishes nothing for the
rest.

Default still unchanged. The case is now observational, structural and
model-based rather than two data points, but it would move 1 896 elements
and the decision belongs with whoever owns the renderer.
2026-08-29 04:34:21 +00:00

63 lines
2.8 KiB
Python
Executable File

"""Plateau-less elements whose LAST keyframe is visible -- do they contradict
the entry->hold->exit model, or are they 'slides in and stops'?"""
import struct, zlib, glob, os, collections
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
def entries(base):
stub=open(base+".pak","rb").read()
if stub[:4]!=b"IPFB": return
n=struct.unpack_from(">I",stub,4)[0]
segs=sorted(glob.glob(base+".p[0-9][0-9]"))
if not segs: return
blob=b"".join(open(s,"rb").read() for s in segs)
for i in range(n):
h,off,sz=struct.unpack_from(">III",stub,0x10+12*i)
st=blob[off:off+sz]
if len(st)<10: continue
try: yield (zlib.decompress(st[10:]) if st[:2]==b"Z1" else st)
except Exception: continue
def elements(d):
if d[:4]!=b"RATC": return []
count=struct.unpack_from(">I",d,0x14)[0]
if not (0<count<4096): return []
names=[]
for i in range(count):
o=DECL_AT+i*DECL_ENTRY
if o+DECL_ENTRY>len(d): return []
names.append(d[o:o+28].split(b"\0")[0].decode("ascii","replace"))
out,pos=[],DECL_AT+count*DECL_ENTRY
while True:
if pos+8>len(d): break
idx,frames=struct.unpack_from(">II",d,pos)
if idx>=count or frames==0 or frames>4096: break
first=pos+12; end=first+frames*KEYFRAME-4
poses=[]
for k in range(frames):
b=first+k*KEYFRAME
if b+36>len(d) or b+36>end: break
poses.append(struct.unpack_from(">I",d,b)+struct.unpack_from(">II",d,b+16)+struct.unpack_from(">ii",d,b+28))
if poses: out.append((names[idx],poses))
pos=end
return out
alpha=lambda q:(q[0]>>24)&0xff
mono=stuck=other=0
ex=[]
for pak in sorted(glob.glob("/work/sylph_extract/dat/GP_*.pak")):
for d in entries(pak[:-4]):
for nm,p in elements(d):
if len(p)==1: continue
if any(p[i]==p[i+1] for i in range(len(p)-1)): continue
if alpha(p[-1])==0: continue # ends invisible: transient
a=[alpha(q) for q in p]
if all(a[i]<=a[i+1] for i in range(len(a)-1)): # alpha never decreases
mono+=1
if len(ex)<8: ex.append((os.path.basename(pak), nm, a, [(q[1],q[2]) for q in p]))
elif a[-1]==max(a): stuck+=1
else: other+=1
tot=mono+stuck+other
print(f"plateau-less, multi-keyframe, ending VISIBLE: {tot}")
print(f" alpha never decreases (a fade/slide IN that stops): {mono} ({100*mono/max(tot,1):.1f} %)")
print(f" ends at its maximum alpha but dips on the way : {stuck} ({100*stuck/max(tot,1):.1f} %)")
print(f" ends visible but NOT at maximum alpha : {other} ({100*other/max(tot,1):.1f} %)")
print("\nexamples of the monotone kind (alpha sequence, then scale pairs):")
for e in ex: print(" ", e[0], e[1], e[2], e[3])