Files
openroller/tools/pack_psp_jacket.py
tsuki 831d96e562 Initial public source release
Split reusable rendering and format support into vectorail-core and vectorail-gc.
2026-08-02 17:05:27 +02:00

40 lines
1.3 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import struct
import subprocess
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser(description="Convert a GC menu DDS jacket to PSP RGBA4444")
parser.add_argument("input_dds")
parser.add_argument("output_orpj")
args = parser.parse_args()
command = [
"ffmpeg", "-hide_banner", "-loglevel", "error", "-i", args.input_dds,
"-vf", "crop=197:197:0:0,scale=128:128:flags=lanczos",
"-frames:v", "1", "-f", "rawvideo", "-pix_fmt", "rgba", "-",
]
result = subprocess.run(command, stdout=subprocess.PIPE, check=False)
expected = 128 * 128 * 4
if result.returncode != 0 or len(result.stdout) != expected:
raise SystemExit(f"ffmpeg produced {len(result.stdout)} bytes, expected {expected}")
pixels = bytearray(128 * 128 * 2)
for index in range(128 * 128):
r, g, b, a = result.stdout[index * 4:index * 4 + 4]
value = (r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8) | ((a >> 4) << 12)
struct.pack_into("<H", pixels, index * 2, value)
output = Path(args.output_orpj)
output.parent.mkdir(parents=True, exist_ok=True)
header = struct.pack("<4sHHHHI", b"ORPJ", 1, 128, 128, 0, len(pixels))
output.write_bytes(header + pixels)
return 0
if __name__ == "__main__":
raise SystemExit(main())