Files
llamamod/tools/png2cor.py
T

69 lines
2.1 KiB
Python

#!/usr/bin/env python3
"""Packs an image as Argonaut .cor (counterpart of cor2png.py).
The image is converted to 640x480 RGB and compressed with the game's LZ
scheme:
n < 0x80 -> n bytes of raw data follow (n = 1..127)
1LLL oooo oooooooo -> copy L+3 bytes (3..10) from o bytes (1..4095) back
Usage: png2cor.py input.png output.cor (requires Pillow)
"""
import struct
import sys
MAGIC = bytes.fromhex('1798af89fe42d112')
MAXOFF, MINLEN, MAXLEN = 0x0FFF, 3, 10
def pack(raw):
out = bytearray()
lit = bytearray()
last = {} # 3-byte prefix -> list of recent positions
def flush():
for k in range(0, len(lit), 127):
part = lit[k:k + 127]
out.append(len(part))
out.extend(part)
lit.clear()
i, n = 0, len(raw)
while i < n:
best_len, best_off = 0, 0
if i + MINLEN <= n:
key = raw[i:i + 3]
for p in reversed(last.get(key, ())):
off = i - p
if off > MAXOFF:
break
ln = 3
while ln < MAXLEN and i + ln < n and raw[p + ln] == raw[i + ln]:
ln += 1
if ln > best_len:
best_len, best_off = ln, off
if ln == MAXLEN:
break
step = best_len if best_len >= MINLEN else 1
for j in range(i, min(i + step, n - 2)):
lst = last.setdefault(raw[j:j + 3], [])
lst.append(j)
if len(lst) > 16:
del lst[0]
if best_len >= MINLEN:
flush()
out.append(0x80 | ((best_len - 3) << 4) | (best_off >> 8))
out.append(best_off & 0xFF)
i += best_len
else:
lit.append(raw[i])
i += 1
flush()
return MAGIC + struct.pack('<III', 1, len(raw), len(out)) + bytes(out)
if __name__ == '__main__':
from PIL import Image
img = Image.open(sys.argv[1]).convert('RGB').resize((640, 480), Image.LANCZOS)
data = pack(img.tobytes())
open(sys.argv[2], 'wb').write(data)
print(f'{sys.argv[2]}: {len(data)} bytes')