"""Compile the original ANSI animation into compact native glyph/color frames.""" import json import re import struct from pathlib import Path from PIL import Image, ImageDraw, ImageFont COLS, ROWS = 72, 21 CELL_W, CELL_H = 8, 16 ESCAPE = re.compile(r"\x1b\[([0-9;?]*)([A-Za-z])") def palette(): colors = [0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF] levels = [0, 95, 135, 175, 215, 255] colors += [(r << 16) | (g << 8) | b for r in levels for g in levels for b in levels] colors += [((8 + i * 10) * 0x010101) for i in range(24)] return colors def parse_frame(text): cells = [[(" ", 15) for _ in range(COLS)] for _ in range(ROWS)] x = y = 0 color = 15 offset = 0 while offset < len(text): match = ESCAPE.match(text, offset) if match: parameters, command = match.groups() if command == "m": values = [int(v) for v in parameters.split(";") if v] if len(values) == 3 and values[:2] == [38, 5]: color = values[2] elif values == [0]: color = 15 else: raise ValueError(f"Unsupported foreground escape {match.group()!r}") elif command == "H": x = y = 0 elif command == "J" and parameters == "2": cells = [[(" ", 15) for _ in range(COLS)] for _ in range(ROWS)] elif parameters == "?25" and command in ("h", "l"): pass else: raise ValueError(f"Unsupported escape {match.group()!r}") offset = match.end() continue char = text[offset] offset += 1 if char == "\r": x = 0 elif char == "\n": x = 0 y += 1 else: if char != " " and (x >= COLS or y >= ROWS): raise ValueError(f"Visible cell outside frame: ({x}, {y})") if x < COLS and y < ROWS: cells[y][x] = (char, color) x += 1 return [cell for row in cells for cell in row] def glyph(char, font): image = Image.new("L", (CELL_W, CELL_H)) draw = ImageDraw.Draw(image) # Box/block glyphs fill their cells, so the old logo remains continuous. if char == "\u2588": draw.rectangle((0, 0, 7, 15), fill=255) elif char in "\u2550\u2551\u2554\u2557\u255a\u255d": if char == "\u2550": draw.line((0, 6, 7, 6), fill=255) draw.line((0, 9, 7, 9), fill=255) elif char == "\u2551": draw.line((2, 0, 2, 15), fill=255) draw.line((5, 0, 5, 15), fill=255) else: left = char in "\u2554\u255a" top = char in "\u2554\u2557" edge_x, end_y = (7 if left else 0), (15 if top else 0) for x, y in [(2, 6), (5, 9)]: draw.line((x, y, x, end_y), fill=255) draw.line((x, y, edge_x, y), fill=255) elif char != " ": draw.text((0, 0), char, font=font, fill=255) return bytes(sum((1 << (7 - x)) for x in range(CELL_W) if image.getpixel((x, y)) >= 96) for y in range(CELL_H)) def main(): root = Path(__file__).resolve().parents[2] source = root / "assets/animations/casino_intro.ans" raw = source.read_bytes() text = raw.decode("utf-8") parts = text.replace("\r\n", "\n").split("\f") frames, loop_start = [], None for part in parts: if part.strip() == "@@LOOP@@": loop_start = len(frames) elif part: frames.append(parse_frame(part)) if loop_start is None or not 0 <= loop_start < len(frames): raise ValueError("Missing loop boundary") characters = [" "] + sorted({char for frame in frames for char, _ in frame} - {" "}) if len(characters) > 256: raise ValueError("Too many glyphs") indexes = {char: i for i, char in enumerate(characters)} font = ImageFont.truetype("C:/Windows/Fonts/consola.ttf", 13) masks = b"".join(glyph(char, font) for char in characters).ljust(4096, b"\0") colors = palette() payload = struct.pack("<256I", *colors) + masks payload += b"".join(struct.pack("