cd8d566d82
Порт 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>
62 lines
2.9 KiB
Python
62 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
extract_room.py — вырезает ОДНУ комнату (foretable+backtable, 30+30
|
|
байт) + стартовую позицию уровня (start_room/start_pos/start_dir, 3
|
|
байта) из level-ресурса формата "res20NN.bin" (SDLPoP data/LEVELS/).
|
|
|
|
Layout подтверждён структурным разбором: файл начинается СРАЗУ с
|
|
foretable[720] (24 комнаты * 30 тайлов), затем backtable[720] — без
|
|
заголовка (applications/PoP/docs/POP-DAT-FormatSpecifications.pdf §3.4
|
|
+ декод байт 2026-07-15). Тайл tileOffset 0-9 = верхний ряд, 10-19 =
|
|
средний, 20-29 = нижний (см. level.h level_tile()).
|
|
|
|
Смещение start_room/start_pos/start_dir НЕ сразу после links (как
|
|
предполагалось изначально) — сверено со структурой level_type в
|
|
applications/PoP/SDLPoP/src/types.h (маленький структурный факт,
|
|
порядок полей, не объём кода): fg[720]+bg[720]+doorlinks1[256]+
|
|
doorlinks2[256]+roomlinks[96]+used_rooms[1]+roomxs[24]+roomys[24]+
|
|
fill_1[15] = 2112 -> start_room(u8), start_pos(u8), start_dir(s8)
|
|
(2026-07-16, подтверждено на res2001.bin: start_room=1, start_pos=0,
|
|
start_dir=-1 — согласуется с links[room1] и решулось эмпирически
|
|
через таблицу room-links, которая идеально легла на смещение 1952).
|
|
|
|
Использование:
|
|
extract_room.py LEVEL.bin ROOM OUT.dat
|
|
|
|
Выход: OUT.dat — 63 байта (fore[30]+back[30]+start_room+start_pos+
|
|
start_dir), формат level_load_room() (room.c). ROOM — 1-based (как в
|
|
файле/level.h); start_* — общеуровневые (не зависят от ROOM).
|
|
"""
|
|
import sys
|
|
|
|
FORETABLE_OFF = 0
|
|
BACKTABLE_OFF = 720
|
|
ROOM_TILES = 30
|
|
START_OFF = 720 + 720 + 256 + 256 + 96 + 1 + 24 + 24 + 15 # 2112
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) != 4:
|
|
sys.exit(__doc__.strip())
|
|
src, room_s, out = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
room = int(room_s)
|
|
|
|
data = open(src, "rb").read()
|
|
if len(data) < START_OFF + 3:
|
|
sys.exit(f"extract_room: {src}: {len(data)} Б — меньше ожидаемых "
|
|
f"{START_OFF + 3} (foretable+backtable+...+start_position)")
|
|
|
|
base = (room - 1) * ROOM_TILES
|
|
fore = data[FORETABLE_OFF + base:FORETABLE_OFF + base + ROOM_TILES]
|
|
back = data[BACKTABLE_OFF + base:BACKTABLE_OFF + base + ROOM_TILES]
|
|
start = data[START_OFF:START_OFF + 3]
|
|
open(out, "wb").write(fore + back + start)
|
|
print(f"extract_room: комната {room} из {src} -> {out} "
|
|
f"({len(fore)}+{len(back)}+{len(start)} Б); "
|
|
f"start_room={start[0]} start_pos={start[1]} "
|
|
f"start_dir={start[2] if start[2] < 128 else start[2] - 256}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|