"""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 (0len(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])