#!/usr/bin/env python3 """ pop_extract_kid_data.py — извлекает данные анимации Kid из исходников SDLPoP в C-заголовок roomtest/kid_data.h (Фаза K1). Извлекает: frame_table_kid[] (seg006.c) -> kframe kid_frames[] {image,dx,dy,flags} original_seqtbl[] (seqtbl.c) -> byte kid_seqtbl[] (2310 Б, база 0x196E) original_seqtbl_offsets[] (seqtbl.c) -> word kid_seq_off[] (seq_id -> адрес) seqtbl адресуется базой SEQTBL_BASE=0x196E: элемент = kid_seqtbl[addr-0x196E]. Автогенерация — чтобы не переписывать ~180 кадров + 2КБ байткода руками. """ import os import re SRC = "/Users/alex/Projects/DIY/Z80/Sprinter/C-Compiler/applications/PoP/SDLPoP/src" OUT_BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "poc", "res", "kid") OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "roomtest", "kid_data.h") SEQTBL_BASE = 0x196E def read(name): return open(os.path.join(SRC, name)).read() def parse_frame_table(txt): """frame_table_kid[] -> список (image, dx, dy, flags). Записи вида { IMG, 0xC0| 4, DX, DY, 0x40| 4 } — sword/flags как OR-выражения.""" m = re.search(r'const frame_type frame_table_kid\[\]\s*=\s*\{(.*?)\n\};', txt, re.S) body = m.group(1) frames = [] for row in re.findall(r'\{([^}]*)\}', body): parts = [p.strip() for p in row.split(',')] image = int(parts[0], 0) sword = eval(parts[1], {}, {}) # напр. "0x00| 9" dx = int(parts[2], 0) dy = int(parts[3], 0) flags = eval(parts[4], {}, {}) frames.append((image & 0xFF, dx & 0xFF, dy & 0xFF, flags & 0xFF, sword & 0xFF)) return frames def parse_byte_array(txt, name): m = re.search(r'const byte %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S) return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))] def parse_word_array(txt, name): m = re.search(r'const word %s\[\]\s*=\s*\{(.*?)\}' % name, txt, re.S) return [int(x, 0) for x in re.findall(r'0x[0-9A-Fa-f]+|\d+', m.group(1))] def emit_c(vals, per_line=12, fmt="0x%02X"): out = [] for i in range(0, len(vals), per_line): out.append(" " + ",".join(fmt % v for v in vals[i:i + per_line]) + ",") return "\n".join(out) def main(): seg006 = read("seg006.c") seqc = read("seqtbl.c") frames = parse_frame_table(seg006) seqtbl = parse_byte_array(seqc, "original_seqtbl") offs = parse_word_array(seqc, "original_seqtbl_offsets") with open(OUT, "w") as f: f.write("/* kid_data.h — данные анимации Kid (frame_table + seqtbl +\n" " * offsets), извлечено toolchain/pop_extract_kid_data.py из\n" " * SDLPoP. НЕ править вручную. seqtbl адресуется базой\n" " * SEQTBL_BASE: kid_seqtbl[addr - SEQTBL_BASE]. */\n") f.write("#ifndef KID_DATA_H\n#define KID_DATA_H\n#include \n\n") f.write("#define SEQTBL_BASE 0x%04Xu\n\n" % SEQTBL_BASE) # frame table и seqtbl В .h НЕ ПОПАДАЮТ — они уезжают в kid_data.bin # (EMM-страница): 3.5 КБ в _CODE окна W1/W2 нам дороже, чем один # gfx_w0_map за тик. См. applications/PoP/docs/layout_plan_v2.md §5.1. f.write("/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid).\n" " * САМА таблица кадров и seqtbl лежат в kid_data.bin (EMM-\n" " * страница); здесь только тип и размеры. */\n") f.write("typedef struct { uint8_t image; int8_t dx, dy; " "uint8_t flags, sword; } kframe;\n") f.write("#define KID_NFRAMES %d\n" % len(frames)) f.write("#define KID_SEQTBL_LEN %d\n\n" % len(seqtbl)) # раскладка kid_data.bin (смещения ОТ НАЧАЛА ДАННЫХ страницы) f.write("/* Раскладка kid_data.bin: кадры (KID_NFRAMES*5), затем seqtbl. */\n") f.write("#define KID_BIN_FRAMES_OFF 0\n") f.write("#define KID_BIN_SEQTBL_OFF %d\n\n" % (len(frames) * 5)) # seq offsets f.write("#define KID_NSEQ %d\n" % len(offs)) f.write("static const uint16_t kid_seq_off[KID_NSEQ] = {\n") f.write(emit_c(offs, 8, "0x%04X") + "\n};\n\n") f.write("#endif\n") # kid_data.bin: кадры (по 5 байт) + seqtbl подряд os.makedirs(OUT_BIN_DIR, exist_ok=True) binpath = os.path.join(OUT_BIN_DIR, "kid_data.bin") with open(binpath, "wb") as b: for (img, dx, dy, fl, sw) in frames: b.write(bytes((img & 0xFF, dx & 0xFF, dy & 0xFF, fl & 0xFF, sw & 0xFF))) b.write(bytes(seqtbl)) print(f"kid_data.bin: {len(frames)*5 + len(seqtbl)} Б -> {binpath}") print(f"kid_data.h: {len(frames)} кадров, seqtbl {len(seqtbl)} Б, " f"{len(offs)} seq-оффсетов -> {OUT}") if __name__ == "__main__": main()