KAZIK/tools/tests/check_startup_frames.py
Artvasil09 3ad5bf4c9d Develop assembly-only casino scenes and native games
Add native Durak, poker, blackjack, roulette, Buckshot and billiards scenes with animation assets and regression tests. Improve arrival, city, hall, profiles, shops, audio and exit handling; remove the portable C game core and refresh shipping executables.
2026-10-08 22:39:19 +03:00

52 lines
2.8 KiB
Python

"""Check/export the real MASM startup frames, not a substitute renderer."""
import hashlib
import json
import struct
from pathlib import Path
from PIL import Image, ImageChops, ImageDraw, ImageStat
def main():
root = Path(__file__).resolve().parents[2]
output = root / "build/actions"
times = [0, 240, 700, 1300, 2500, 6500]
frames = [Image.open(output / f"studio-{time}.bmp").convert("RGB") for time in times]
assert all(frame.size == (640, 480) for frame in frames)
hashes = [hashlib.sha256(frame.tobytes()).hexdigest() for frame in frames]
assert len(set(hashes)) == len(hashes), "Ident animation frames are identical"
means = [sum(ImageStat.Stat(frame).mean) / 3 for frame in frames]
assert means[0] > 1, "The first visible frame is blank"
assert means[3] > means[2] > means[1] > means[0], "The reveal did not brighten progressively"
packed = (root / "assets/animations/intro/eblan-games.bin").read_bytes()
magic, width, height, size = struct.unpack_from("<4sIII", packed)
assert (magic, width, height, size) == (b"EBG1", 640, 480, 1228800)
assert len(packed) == size + 16
original = Image.frombytes("RGBA", (640, 480), packed[16:], "raw", "BGRA").convert("RGB")
difference = ImageStat.Stat(ImageChops.difference(original, frames[3]).crop((0, 0, 640, 420)))
assert max(difference.mean) < 8, "The settled ident is distorted or corrupted"
originals = [Image.open(output / f"intro-{time}.bmp").convert("RGB") for time in times]
original_hashes = [hashlib.sha256(frame.tobytes()).hexdigest() for frame in originals]
assert len(set(original_hashes)) == len(original_hashes), "Original animation is frozen"
assert sum(ImageStat.Stat(originals[3]).mean) > sum(ImageStat.Stat(originals[1]).mean)
sequence = (root / "assets/animations/intro/eblan-sequence.bin").read_bytes()
header = struct.unpack_from("<4s8I", sequence)
assert header == (b"EBGA", 72, 21, 100, 36, 45, 55, 307520, 1)
assert len(sequence) == 36 + header[7]
fallback = Image.open(output / "intro-fallback.bmp").convert("RGB")
assert len(fallback.getcolors(640 * 480)) >= 3, "Fallback branding is missing"
sheet = Image.new("RGB", (960, 1048), "#121718")
draw = ImageDraw.Draw(sheet)
for index, frame in enumerate(originals + frames):
x, y = (index % 3) * 320, (index // 3) * 262
sheet.paste(frame.resize((320, 240), Image.Resampling.NEAREST), (x, y))
variant = "Original" if index < 6 else "Studio / F2"
draw.text((x + 8, y + 244), f"MASM {variant} / {times[index % 6]} ms", fill="#e8d2ae")
sheet.save(output / "intro-contact-sheet.png")
originals[4].save(output / "intro-ready.png")
print(json.dumps({"result": "PASS", "original_frames": 100, "rendered_variants": 2,
"frame_mean_brightness": means, "snapshots": 12}))
if __name__ == "__main__":
main()