69 lines
2.1 KiB
Python
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')
|