import argparse import pathlib import subprocess import sys import tempfile from typing import NoReturn from PIL import Image GREEN = (0, 255, 0) RED = (255, 0, 0) def abort(msg: str) -> NoReturn: print(msg, file=sys.stderr) sys.exit(1) def generate_coll_map(in_path: pathlib.Path, width: int, height: int) -> str: png = Image.open(in_path).convert("RGB") if png.width % 8 != 0 or png.height % 8 != 0: abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)") if png.width // 8 != width or png.height // 8 != height: abort(f"file '{in_path}' has different size from map") out_bytes = [] bits = [] for y in range(png.height // 8): for x in range(png.width // 8): pixel = png.getpixel((x * 8, y * 8)) bit = None if pixel == RED: bit = 1 elif pixel == GREEN: bit = 0 else: abort(f"unsupported pixel in collision map: {pixel}") bits.append(bit) if len(bits) == 8: byte = sum([bit << i for i, bit in enumerate(bits)]) out_bytes.append(byte) bits = [] png.close() lines = [] for line_no in range(0, len(out_bytes), 16): line = out_bytes[line_no : line_no + 16] lines.append(" DB " + ", ".join(["$%02X" % b for b in line])) return "\n".join(lines) def format_bytes(data: bytes) -> str: lines = [] for line_no in range(0, len(data), 16): line = data[line_no : line_no + 16] lines.append(" DB " + ", ".join(["$%02X" % b for b in line])) return "\n".join(lines) def generate_map(pngfile: str) -> None: pngpath = pathlib.Path(pngfile).resolve() incpath = pngpath.parent / pngpath.name.replace(".png", ".inc") spath = pngpath.parent / pngpath.name.replace(".png", ".s") png = Image.open(pngpath) if png.width % 8 != 0 or png.height % 8 != 0: abort(f"file '{pngfile}' has invalid dimensions (should be multiple of 8)") width = png.width // 8 height = png.height // 8 png.close() with tempfile.NamedTemporaryFile() as tilef, tempfile.NamedTemporaryFile() as mapf: subprocess.run( [ "rgbgfx", "-u", "-t", tilef.name, "-o", mapf.name, pngfile, ] ) map_data = format_bytes(tilef.read()) tile_data = format_bytes(mapf.read()) section = pngpath.name.replace(".png", "") collpath = pngpath.parent / pngpath.name.replace(".png", "_coll.png") coll_map = generate_coll_map(collpath, width, height) with open(incpath, "w") as outf: outf.write( f"""DEF {section}_WIDTH EQU {width} DEF {section}_HEIGHT EQU {height} DEF {section}_NUM_TILES EQUS "({section}_TILES_end - {section}_TILES)" DEF {section}_MAP_SIZE EQUS "({section}_MAP_end - {section}_MAP)" ASSERT {section}_MAP_SIZE == {section}_WIDTH * {section}_HEIGHT """ ) with open(spath, "w") as outf: outf.write( f"""SECTION "MAP - {section}", ROMX {section}_MAP:: {map_data} {section}_MAP_end:: {section}_TILES:: {tile_data} {section}_TILES_end:: {section}_COLLISION:: {coll_map} {section}_COLLISION_end:: """ ) def main() -> None: parser = argparse.ArgumentParser("generate_map") parser.add_argument("png") args = parser.parse_args() generate_map(args.png) if __name__ == "__main__": main()