#!/usr/bin/env python3 """Converte um cartucho TIC-80 em texto (.lua com blocos "-- " etc.) para o binario .tic, como o TIC-80 1.2.0 grava (project.c / cart.c). Deterministico. Uso: tic80-lua-to-tic.py entrada.lua saida.tic Chunks (tipo): TILES 1, SPRITES 2, MAP 4, CODE 5, FLAGS 6, SAMPLES 9, WAVEFORMS 10, PALETTE 12. O texto traz cada byte com os nibbles trocados (exceto a paleta, que e RGB direto): tiles, sprites, mapa, sfx e waves trocam os nibbles ao gravar. Conferido contra o cart.tic gravado pelo proprio TIC-80 (chunks identicos). Chunks tem os zeros finais cortados, como o TIC-80 faz.""" import re, sys # tag -> (tipo, tamanho de cada linha em bytes, troca nibbles, tamanho maximo do chunk) BLOCKS = [ ("TILES", 1, 32, True, 8192), ("SPRITES", 2, 32, True, 8192), ("MAP", 4, 240, True, 32640), ("SFX", 9, 66, True, 64 * 66), ("WAVES", 10, 16, True, 16 * 16), ("PALETTE", 12, 48, False, 48), ("FLAGS", 6, 1, False, 512), ] def main(src, dst): text = open(src, encoding="utf-8", newline="").read().replace("\r\n", "\n") lines = text.split("\n") first = None # primeira linha de um bloco de dados blocks = {} i = 0 while i < len(lines): m = re.fullmatch(r"-- <([A-Z]+)>", lines[i]) if m: if first is None: first = i tag, rows, i = m.group(1), [], i + 1 while lines[i] != f"-- ": rows.append(lines[i]); i += 1 blocks[tag] = rows i += 1 code = "\n".join(lines[:first]).rstrip("\n") if first is not None else text out = bytearray() for tag, typ, rowsize, flip, maxsize in BLOCKS: if tag not in blocks: continue data = bytearray(maxsize) for row in blocks[tag]: m = re.fullmatch(r"-- ([0-9a-f]+):([0-9a-f]+)", row) if not m: raise SystemExit(f"{tag}: linha invalida: {row[:40]}") idx, hexs = int(m.group(1)), m.group(2) # indice decimal b = bytes.fromhex(hexs) if flip: b = bytes(((x & 15) << 4) | (x >> 4) for x in b) if len(b) > rowsize: raise SystemExit(f"{tag}: linha {idx} grande demais") data[idx * rowsize: idx * rowsize + len(b)] = b data = bytes(data).rstrip(b"\0") if data: out += bytes([typ, len(data) & 255, len(data) >> 8, 0]) + data cb = code.encode("utf-8") if len(cb) >= 65536: raise SystemExit("codigo grande demais para o chunk CODE") out += bytes([5, len(cb) & 255, len(cb) >> 8, 0]) + cb open(dst, "wb").write(out) if __name__ == "__main__": main(*sys.argv[1:3])