mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-11-01 12:14:56 +02:00
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Compare a MAME VRAM dump with the logical frame the movie should show."""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
if str(ROOT) not in sys.path:
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from pipeline.movie import read_movie
|
||
from pipeline.tilepack import apply_record
|
||
|
||
VRAM_STRIDE = 256
|
||
|
||
|
||
def vram_to_logical(data: bytes, width: int = 640, height: int = 512,
|
||
scale: int = 2) -> np.ndarray:
|
||
"""Unpack a 1-bpp VRAM dump and majority-downsample scale×scale blocks."""
|
||
screen_w = width * scale
|
||
screen_h = height * scale
|
||
need = VRAM_STRIDE * screen_h
|
||
if len(data) < need:
|
||
raise ValueError("VRAM dump is %d bytes, need at least %d"
|
||
% (len(data), need))
|
||
rows = np.frombuffer(data[:need], dtype=np.uint8).reshape(screen_h,
|
||
VRAM_STRIDE)
|
||
bits = np.unpackbits(rows, axis=1, bitorder="little")[:, :screen_w]
|
||
blocks = bits.astype(np.uint16).reshape(height, scale, width, scale)
|
||
# The driver's palette: bit 1 = white, bit 0 = black.
|
||
return blocks.mean(axis=(1, 3)) < 0.5
|
||
|
||
|
||
def expected_frame(movie_path: str, frame: int) -> np.ndarray:
|
||
header, records = read_movie(movie_path)
|
||
if frame < 0 or frame >= len(records):
|
||
raise ValueError("frame %d outside 0..%d" % (frame, len(records) - 1))
|
||
canvas = np.zeros((header.height, header.width), dtype=bool)
|
||
for record in records[:frame + 1]:
|
||
apply_record(canvas, record, tile=header.tile)
|
||
return canvas
|
||
|
||
|
||
def main(argv=None) -> int:
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
parser.add_argument("--vram", required=True)
|
||
parser.add_argument("--movie", required=True)
|
||
parser.add_argument("--frame", type=int, default=0)
|
||
parser.add_argument("--scale", type=int, default=2)
|
||
args = parser.parse_args(argv)
|
||
|
||
data = Path(args.vram).read_bytes()
|
||
header, _ = read_movie(args.movie)
|
||
actual = vram_to_logical(data, header.width, header.height, args.scale)
|
||
expected = expected_frame(args.movie, args.frame)
|
||
if np.array_equal(actual, expected):
|
||
print("EXACT frame %d" % args.frame)
|
||
return 0
|
||
bad = int(np.count_nonzero(actual != expected))
|
||
ys, xs = np.where(actual != expected)
|
||
print("MISMATCH frame %d: %d logical pixels differ" % (args.frame, bad))
|
||
for y, x in list(zip(ys, xs))[:16]:
|
||
print(" (%d,%d) expected=%d actual=%d"
|
||
% (x, y, expected[y, x], actual[y, x]))
|
||
return 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|