You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

140 lines
3.5 KiB

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()