main png2swc.py
  1#!/usr/bin/env python3
  2
  3import getopt
  4import subprocess
  5import sys
  6from pathlib import Path
  7# yes its python, sorry 
  8
  9PARTS = [
 10    ("top_left", "top_left.png"),
 11    ("top", "top.png"),
 12    ("top_right", "top_right.png"),
 13    ("left", "left.png"),
 14    ("right", "right.png"),
 15    ("bottom_left", "bottom_left.png"),
 16    ("bottom", "bottom.png"),
 17    ("bottom_right", "bottom_right.png"),
 18]
 19
 20
 21def usage(file):
 22    file.write(
 23        "usage: png2swc.py [-a asset-dir] [-o output] [-s symbol]\n"
 24    )
 25
 26
 27def run(cmd):
 28    return subprocess.run(cmd, check=True, capture_output=True)
 29
 30
 31def image_size(path: Path):
 32    result = run(["magick", str(path), "-format", "%w %h", "info:"])
 33    width, height = result.stdout.decode("utf-8").strip().split()
 34    return int(width), int(height)
 35
 36
 37def image_argb8888(path: Path):
 38    width, height = image_size(path)
 39    result = run(["magick", str(path), "rgba:-"])
 40    raw = result.stdout
 41    expected = width * height * 4
 42    if len(raw) != expected:
 43        raise ValueError(f"unexpected byte count for {path}: {len(raw)} != {expected}")
 44
 45    pixels = []
 46    for i in range(0, len(raw), 4):
 47        r, g, b, a = raw[i:i + 4]
 48        pixels.append((a << 24) | (r << 16) | (g << 8) | b)
 49    return width, height, pixels
 50
 51
 52def format_pixels(name, pixels):
 53    lines = []
 54    row = []
 55    for i, pixel in enumerate(pixels, start=1):
 56        row.append(f"0x{pixel:08x}u")
 57        if i % 8 == 0:
 58            lines.append(", ".join(row))
 59            row = []
 60    if row:
 61        lines.append(", ".join(row))
 62
 63    joined = ",\n    ".join(lines)
 64    return f"static const uint32_t {name}_pixels[] = {{\n    {joined}\n}};\n"
 65
 66
 67def generate_header(asset_dir: Path, output: Path, symbol: str):
 68    arrays = []
 69    parts = []
 70
 71    for part_name, filename in PARTS:
 72        path = asset_dir / filename
 73        width, height, pixels = image_argb8888(path)
 74        arrays.append(format_pixels(f"{symbol}_{part_name}", pixels))
 75        parts.append(
 76            "static const struct swc_decor_part "
 77            f"{symbol}_{part_name} = {{ .width = {width}u, .height = {height}u, "
 78            f".stride = {width * 4}u, .data = {symbol}_{part_name}_pixels }};\n"
 79        )
 80
 81    body = "\n".join(arrays + parts)
 82    mapping = (
 83        f"static const struct swc_decor_parts {symbol}_parts = {{\n"
 84        f"    .top_left = {symbol}_top_left,\n"
 85        f"    .top = {symbol}_top,\n"
 86        f"    .top_right = {symbol}_top_right,\n"
 87        f"    .left = {symbol}_left,\n"
 88        f"    .right = {symbol}_right,\n"
 89        f"    .bottom_left = {symbol}_bottom_left,\n"
 90        f"    .bottom = {symbol}_bottom,\n"
 91        f"    .bottom_right = {symbol}_bottom_right,\n"
 92        f"}};\n"
 93    )
 94
 95    guard = f"{symbol.upper()}_H"
 96    text = (
 97        f"#ifndef {guard}\n"
 98        f"#define {guard}\n\n"
 99        f"#include <stdint.h>\n"
100        f"#include <swc.h>\n\n"
101        f"/* generated from {asset_dir.name} png decor parts, probably dont edit this */\n\n"
102        f"{body}\n"
103        f"{mapping}\n"
104        f"#endif\n"
105    )
106    output.write_text(text, encoding="ascii")
107
108
109def main():
110    asset_dir = "."
111    output = "parts.h"
112    symbol = "afterstep_qnx"
113
114    try:
115        opts, args = getopt.getopt(
116            sys.argv[1:], "a:o:s:h", ["asset-dir=", "output=", "symbol=", "help"]
117        )
118    except getopt.GetoptError:
119        usage(sys.stderr)
120        raise SystemExit(1)
121
122    for opt, val in opts:
123        if opt in ("-a", "--asset-dir"):
124            asset_dir = val
125        elif opt in ("-o", "--output"):
126            output = val
127        elif opt in ("-s", "--symbol"):
128            symbol = val
129        elif opt in ("-h", "--help"):
130            usage(sys.stdout)
131            raise SystemExit(0)
132
133    if args:
134        usage(sys.stderr)
135        raise SystemExit(1)
136
137    generate_header(Path(asset_dir).resolve(), Path(output).resolve(), symbol)
138
139
140if __name__ == "__main__":
141    main()