applications/PoP: порт Prince of Persia — PoC (roomtest) + пайплайн
Порт PoP на Sprinter. Текущий PoC — applications/PoP/roomtest/: комната 1 (фон-композиция тайлов) + Kid с управлением на raw-клавиатуре и коллизией с картой. - roomtest — pop_bg (фон), pop_kid (спрайты Kid, column-major флип, seqtbl-анимация), pop_ctrl (порт control() PoP на held-state kbd_raw), pop_map (коллизия seg004/005: бег/стоп у стены, падение/приземление, отскок seq_47, вертикальный прыжок K4.1). MEMORY=small (DATA сразу за CODE, ~23КБ кода не лезет в huge). - toolchain (PoP) — pop_pack_kid/pop_pack_bg/render_room/extract — распаковка res-графики MSDOS в атласы + композиция комнат. - toolchain/ (корень) — make_hdd.sh (быстрый HDD-тест вместо FDD), png_strip.py / room_compose.py (ассет-пайплайн). - docs — PORT_PLAN, KID_PLAN, форматы ресурсов (Apple II / MSDOS / DAT). - bgtest/coltest/poc — ранние PoC (фон, коллизия, первый прототип). .gitignore: build-артефакты applications/*/*/*; исключены внешние референс-репозитории (SDLPoP/mininim/PR/Apple-II — свои git-клоны) и оригинальные game-данные MSDOS/ (копирайт, только для реверса форматов). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
#!/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 = 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 <stdint.h>\n\n")
|
||||
f.write("#define SEQTBL_BASE 0x%04Xu\n\n" % SEQTBL_BASE)
|
||||
|
||||
# frame table
|
||||
f.write("/* {image, dx, dy, flags, sword}; chtab = sword>>6 (0=kid). */\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("static const kframe kid_frames[KID_NFRAMES] = {\n")
|
||||
for (img, dx, dy, fl, sw) in frames:
|
||||
dxs = dx - 256 if dx > 127 else dx
|
||||
dys = dy - 256 if dy > 127 else dy
|
||||
f.write(" {%3d,%4d,%4d,0x%02X,0x%02X},\n" % (img, dxs, dys, fl, sw))
|
||||
f.write("};\n\n")
|
||||
|
||||
# seqtbl bytes
|
||||
f.write("#define KID_SEQTBL_LEN %d\n" % len(seqtbl))
|
||||
f.write("static const uint8_t kid_seqtbl[KID_SEQTBL_LEN] = {\n")
|
||||
f.write(emit_c(seqtbl) + "\n};\n\n")
|
||||
|
||||
# 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")
|
||||
|
||||
print(f"kid_data.h: {len(frames)} кадров, seqtbl {len(seqtbl)} Б, "
|
||||
f"{len(offs)} seq-оффсетов -> {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user