Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 879f2bae31 | |||
| b3f9a7430c | |||
| fee3bdb354 | |||
| 6a97124e0d | |||
| 8610a8c178 | |||
| 81e8ed4676 | |||
| 8490288d79 | |||
| 31d0075090 | |||
| 6176f6da31 | |||
| 7377bf6a2c | |||
| b34997073e | |||
| 63bfd997a9 | |||
| decbec79de | |||
| b0e7130d0b | |||
| 4e12aa50d1 | |||
| c218e8b983 | |||
| a3aaa30e53 | |||
| 90e304f071 | |||
| b55d4d11e3 | |||
| ca67895667 | |||
| a5252b1c60 | |||
| 3a0e847353 | |||
| df5071a967 | |||
| 2349481b86 | |||
| 589894c50d | |||
| ea8efdb0fd | |||
| 623199337e | |||
| 602c3a20fa | |||
| 4ac3584bf9 | |||
| 3bf28da8ee | |||
| 770b946a36 | |||
| 89e9663753 | |||
| ea07a8d7b0 | |||
| d23126983e | |||
| 50570881f5 | |||
| f25ed37d85 | |||
| 3449f6f8c9 | |||
| 6dabe9b4b1 | |||
| a05970cd36 | |||
| 2176c12cc5 |
@@ -9,3 +9,21 @@ args = [
|
|||||||
"mcp<2",
|
"mcp<2",
|
||||||
"/Volumes/SAM8/Projects/DIY/Z80/Sprinter/C-Compiler/mame/sources/MAME/src/mame_mcp.py",
|
"/Volumes/SAM8/Projects/DIY/Z80/Sprinter/C-Compiler/mame/sources/MAME/src/mame_mcp.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.clear_breakpoint]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.list_breakpoints]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.press_key]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.step_out]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.debugger_command]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|
||||||
|
[mcp_servers.mame-z80.tools.pause]
|
||||||
|
approval_mode = "approve"
|
||||||
|
|||||||
@@ -132,15 +132,9 @@ applications/PoP/PoP1_DOS_music/
|
|||||||
# не идёт (сама roomtest и есть версируемая ветка разработки).
|
# не идёт (сама roomtest и есть версируемая ветка разработки).
|
||||||
applications/PoP/R1/
|
applications/PoP/R1/
|
||||||
|
|
||||||
# SprPoP — автономное приложение. В репозиторий идут исходники, генерируемые
|
# SprPoP — автономное приложение, и правила игнора у него СВОИ:
|
||||||
# заголовки (gen/) и упакованные ресурсы (assets/packed/), потому что без них
|
# applications/SprPoP/.gitignore. Он написан так, чтобы стать корневым
|
||||||
# из чистого клона ничего не собрать. НЕ идут:
|
# .gitignore, когда SprPoP выделят в отдельный репозиторий, — поэтому
|
||||||
# assets/orig/ оригинальные данные игры — копирайт (SDLPoP, MSDOS,
|
# здесь его содержимое НЕ дублируется (иначе разъедется). Коротко: в
|
||||||
# записи музыки DOS-версии); откуда их взять,
|
# репозиторий не идут assets/orig/ (чужие данные — их выкачивает
|
||||||
# написано в assets/orig/README.md
|
# `make fetch`) и assets/packed/LEVELS/ (уровни из оригинала как есть).
|
||||||
# assets/packed/LEVELS/ уровни res20xx.bin ложатся на диск прямо из
|
|
||||||
# оригинала, конвертации нет — то есть это тот же
|
|
||||||
# копирайтный материал, только в раскладке диска
|
|
||||||
# build/ уже покрыт общим правилом build/ выше
|
|
||||||
applications/SprPoP/assets/orig/
|
|
||||||
applications/SprPoP/assets/packed/LEVELS/
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
# # EXTRA_SRCS := helper.c util.c # additional .c files in this dir
|
# # EXTRA_SRCS := helper.c util.c # additional .c files in this dir
|
||||||
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
|
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
|
||||||
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
|
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
|
||||||
|
# # HDD_DEST_DIR := games/myapp # общий каталог файлов в `make hdd`
|
||||||
#
|
#
|
||||||
# include $(PROJ_ROOT)/app.mk
|
# include $(PROJ_ROOT)/app.mk
|
||||||
#
|
#
|
||||||
@@ -40,7 +41,7 @@ FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img
|
|||||||
# образ у себя и связывает его с MAME символьной ссылкой.
|
# образ у себя и связывает его с MAME символьной ссылкой.
|
||||||
HDD_IMG ?= $(MAME_DIR)/IMG/test_hdd.chd
|
HDD_IMG ?= $(MAME_DIR)/IMG/test_hdd.chd
|
||||||
MAKE_DISK := $(MAME_DIR)/make_disk.py
|
MAKE_DISK := $(MAME_DIR)/make_disk.py
|
||||||
MAKE_HDD := $(PROJ_ROOT)/toolchain/make_hdd.sh
|
MAKE_HDD ?= $(PROJ_ROOT)/toolchain/make_hdd.sh
|
||||||
RUN_MAME := $(MAME_DIR)/run_mame.sh
|
RUN_MAME := $(MAME_DIR)/run_mame.sh
|
||||||
|
|
||||||
# Optional knobs — see top of file.
|
# Optional knobs — see top of file.
|
||||||
@@ -67,6 +68,9 @@ SOURCES := $(MAIN_SRC) $(EXTRA_SRCS)
|
|||||||
# Аргументы упаковщика HDD. Обычно это exe и EXTRA_DATA; приложение со
|
# Аргументы упаковщика HDD. Обычно это exe и EXTRA_DATA; приложение со
|
||||||
# своей раскладкой каталогов может переопределить переменную до include.
|
# своей раскладкой каталогов может переопределить переменную до include.
|
||||||
HDD_PACK_ARGS ?= $(EXE) $(EXTRA_DATA)
|
HDD_PACK_ARGS ?= $(EXE) $(EXTRA_DATA)
|
||||||
|
# Общий каталог назначения внутри HDD. Пустое значение сохраняет прежнюю
|
||||||
|
# укладку в корень; вложенные КАТАЛОГ:файл считаются относительно него.
|
||||||
|
HDD_DEST_DIR ?=
|
||||||
|
|
||||||
CC_FLAGS := --memory $(MEMORY)
|
CC_FLAGS := --memory $(MEMORY)
|
||||||
ifneq ($(STACK_SIZE),)
|
ifneq ($(STACK_SIZE),)
|
||||||
@@ -88,11 +92,19 @@ RUNTIME_DEPS := $(wildcard $(PROJ_ROOT)/runtime/*.s)
|
|||||||
# bankN_*.asm; обычным небанковым программам ничего не стоит.
|
# bankN_*.asm; обычным небанковым программам ничего не стоит.
|
||||||
BANK_CHECK := $(PROJ_ROOT)/toolchain/check_bank_calls.py
|
BANK_CHECK := $(PROJ_ROOT)/toolchain/check_bank_calls.py
|
||||||
|
|
||||||
|
# ПРОВЕРКА ISR-СТАБА W0-СТРАНИЦ — там же, по свежей карте. Ловит стаб
|
||||||
|
# _gfx_w0_isr, оставшийся в W1: программа, кладущая свои страницы в W0
|
||||||
|
# (атласы спрайтов, gfx_w0_map), получает недетерминированные зависания,
|
||||||
|
# когда прерывание приходит во время вызова DSS и W1 перемаплен. Только
|
||||||
|
# предупреждение: страницы в W0 кладут не все. Разбор — в шапке скрипта.
|
||||||
|
W0ISR_CHECK := $(PROJ_ROOT)/toolchain/check_w0_isr.py
|
||||||
|
|
||||||
$(EXE): $(SOURCES) $(MKEXE) $(LIB) $(RUNTIME_DEPS)
|
$(EXE): $(SOURCES) $(MKEXE) $(LIB) $(RUNTIME_DEPS)
|
||||||
$(if $(strip $(BUILD_DIR)),@mkdir -p $(dir $@))
|
$(if $(strip $(BUILD_DIR)),@mkdir -p $(dir $@))
|
||||||
$(SPRINTER_CC) $(CC_FLAGS) -o $@ $(SOURCES)
|
$(SPRINTER_CC) $(CC_FLAGS) -o $@ $(SOURCES)
|
||||||
@d=$(dir $@).sprinter-cc-$(EXAMPLE); \
|
@d=$(dir $@).sprinter-cc-$(EXAMPLE); \
|
||||||
if ls $$d/bank*_*.asm >/dev/null 2>&1; then python3 $(BANK_CHECK) $$d; fi
|
if ls $$d/bank*_*.asm >/dev/null 2>&1; then python3 $(BANK_CHECK) $$d; fi; \
|
||||||
|
if ls $$d/*.map >/dev/null 2>&1; then python3 $(W0ISR_CHECK) $$d; fi
|
||||||
|
|
||||||
$(MKEXE):
|
$(MKEXE):
|
||||||
$(MAKE) -C $(PROJ_ROOT)/toolchain/mkexe
|
$(MAKE) -C $(PROJ_ROOT)/toolchain/mkexe
|
||||||
@@ -125,7 +137,7 @@ run: floppy
|
|||||||
# используется MCP-мостом к MAME (run_bridge.sh). После пересборки образа
|
# используется MCP-мостом к MAME (run_bridge.sh). После пересборки образа
|
||||||
# MAME ОБЯЗАН полный рестарт (chdman -f = новый inode; см. memory).
|
# MAME ОБЯЗАН полный рестарт (chdman -f = новый inode; см. memory).
|
||||||
hdd: $(EXE)
|
hdd: $(EXE)
|
||||||
$(MAKE_HDD) $(HDD_IMG) $(HDD_PACK_ARGS)
|
$(MAKE_HDD) $(if $(strip $(HDD_DEST_DIR)),--dest "$(HDD_DEST_DIR)") $(HDD_IMG) $(HDD_PACK_ARGS)
|
||||||
@echo
|
@echo
|
||||||
@echo "HDD (D:) ready: $(HDD_IMG) (with $(EXAMPLE).exe$(if $(EXTRA_DATA), + $(EXTRA_DATA)))"
|
@echo "HDD (D:) ready: $(HDD_IMG) (with $(EXAMPLE).exe$(if $(EXTRA_DATA), + $(EXTRA_DATA)))"
|
||||||
@echo "ВНИМАНИЕ: перезапусти MAME (run_bridge.sh) — образ пересобран."
|
@echo "ВНИМАНИЕ: перезапусти MAME (run_bridge.sh) — образ пересобран."
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
* Left: turn or run left
|
||||||
|
* Right: turn or run right
|
||||||
|
* Up: jump or climb up
|
||||||
|
* Down: crouch or climb down
|
||||||
|
* Down+Left/Right: hop
|
||||||
|
* Shift: pick up things
|
||||||
|
* Shift+Left/Right: careful step
|
||||||
|
* Home or Up+Left: jump left
|
||||||
|
* Page Up or Up+Right: jump right
|
||||||
|
* Up while running: running jump
|
||||||
|
* Shift while falling: grab onto ledge
|
||||||
|
|
||||||
|
* Left/Right: walk (advance or retreat)
|
||||||
|
* Shift: strike (attack)
|
||||||
|
* Up: block (defend)
|
||||||
|
* Down: put sword away; press Shift to draw your sword again.
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
* Esc: Pause game.
|
||||||
|
* Space: Show how much time is left.
|
||||||
|
* Ctrl+A: Restart level.
|
||||||
|
|
||||||
|
* Ctrl+R: Return to intro.
|
||||||
|
* Ctrl+S: Sound on/off.
|
||||||
|
* Ctrl+M: Music on/off.
|
||||||
|
* Ctrl+V: Show version of SprPoP.
|
||||||
|
* Ctrl+Q: Quit game.
|
||||||
|
|
||||||
|
* F6: Quicksave: Save the exact state of the game.
|
||||||
|
* F9: Quickload: Load what the last quicksave saved.
|
||||||
|
* F12: Save a screenshot to the screenshots folder.
|
||||||
|
* Backspace: Display the in-game menu. (Esc will also display the menu by default, but you can turn that off.)
|
||||||
|
|
||||||
|
* Shift+L: Go to next level.
|
||||||
|
* -: Decrease remaining time by one minute.
|
||||||
|
* +: Increase remaining time by one minute.
|
||||||
|
* R: Resurrect kid.
|
||||||
|
* K: Kill guard.
|
||||||
|
* Shift+I: Flip the screen upside down.
|
||||||
|
* Shift+W: Slow falling.
|
||||||
|
* Shift+S: Restore a lost hit-point. (Like a small red potion.)
|
||||||
|
* Shift+T: Give more hit-points. (Like a big red potion.)
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
* H: Look at the room to the left.
|
||||||
|
* J: Look at the room to the right.
|
||||||
|
* U: Look at the room above.
|
||||||
|
* N: Look at the room below.
|
||||||
|
* Ctrl+B: Go back to the room where the prince is. (Undo H,J,U,N.)
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
* [: Shift kid 1 pixel to the left.
|
||||||
|
* ]: Shift kid 1 pixel to the right.
|
||||||
|
* T: Toggle display of timer (remaining minutes:seconds:ticks). Also shows the total elapsed ticks during playback.
|
||||||
|
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../../..)
|
||||||
EXAMPLE := roomtest
|
EXAMPLE := roomtest
|
||||||
|
HDD_IMG := $(CURDIR)/build/hdd/roomtest.chd
|
||||||
|
|
||||||
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
||||||
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
||||||
MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи)
|
MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи)
|
||||||
@@ -88,7 +90,7 @@ EXTRA_FLAGS ?= --gfx 256 -I $(CURDIR)/../poc/res/bg --max-allocs $(ALLOCS) \
|
|||||||
# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает.
|
# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает.
|
||||||
# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте
|
# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте
|
||||||
# MAME = время фазы в растровых строках.
|
# MAME = время фазы в растровых строках.
|
||||||
PROF ?= 1
|
PROF ?= 0
|
||||||
PROF_FLAGS := -DPROF_BORDER=$(PROF)
|
PROF_FLAGS := -DPROF_BORDER=$(PROF)
|
||||||
# Стартовый уровень (отладка): make LEVEL=9 — начать сразу с девятого.
|
# Стартовый уровень (отладка): make LEVEL=9 — начать сразу с девятого.
|
||||||
# Дефолт держим на отлаживаемом сейчас уровне; для «настоящей» игры — LEVEL=1.
|
# Дефолт держим на отлаживаемом сейчас уровне; для «настоящей» игры — LEVEL=1.
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
# Точка входа для следующей сессии (обновлено 2026-08-12, ночь)
|
|
||||||
|
|
||||||
Файл для старта с чистого контекста: где всё стоит, что делать первым, какие
|
|
||||||
грабли уже собраны. Читать целиком — он короткий. Дальше по ссылкам:
|
|
||||||
`TASKS_OPEN.md` (доска), `BUGS_OPEN.md` (открытые баги), `CLAUDE.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. СЛЕДУЮЩАЯ ЗАДАЧА: smoke-прогон уровня 9 и регресс 1-8
|
|
||||||
|
|
||||||
Переворот (зелье инверсии) **доведён и принят пользователем 2026-08-12**:
|
|
||||||
фон, персонажи, клинок, полоса HP, факелы, окклюзия передних граней и
|
|
||||||
переходы между комнатами работают зеркально; чит `U` и зелёное зелье идут
|
|
||||||
одним путём. Осталось пройти сам уровень 9 живьём и убедиться, что на
|
|
||||||
уровнях 1-8 ничего не поехало (весь слой фона теперь спрашивает `pop_upside`).
|
|
||||||
|
|
||||||
Что смотреть на прогоне в первую очередь:
|
|
||||||
|
|
||||||
- **вис на кромке и спуск в перевёрнутом виде** — это новый срез `clip_char`
|
|
||||||
(он режет кадр СНИЗУ вместо верха); тело должно уходить за кладку, а не
|
|
||||||
ложиться поверх неё;
|
|
||||||
- **бой со стражем перевёрнутым** — у соперника свой зеркальный набор
|
|
||||||
(`g*_v.atl`), а тень рисуется атласами Кида, отдельных файлов ей не надо;
|
|
||||||
- **зелье в другой комнате** — переворот перерисовывает не комнату, а чистит
|
|
||||||
факелы и отражает страницу; проверить, что после смены комнаты и возврата
|
|
||||||
фон остаётся чистым.
|
|
||||||
|
|
||||||
Открытый риск (пока не проявился, но проверить осознанно): имена зеркальных
|
|
||||||
атласов `kid10_v.atl`…`kid27_v.atl` — **9 символов до точки, а DSS работает
|
|
||||||
по 8.3**. Если часть кадров Кида в перевёрнутом виде рисуется обычными
|
|
||||||
(неперевёрнутыми) — это оно; лечится переименованием в `pop_pack_kid.py`
|
|
||||||
(например `k10v.atl`) плюс те же имена в `pop_kboot.c`.
|
|
||||||
|
|
||||||
Дальше по плану — уровни 10/11 (по анализу нового в них нет) и
|
|
||||||
[MEM-COLD2](TASKS_OPEN.md#mem-cold2) п.3, если снова понадобится место.
|
|
||||||
|
|
||||||
Ощущенческий хвост прежний — [GRAB-KBD-TIMING](BUGS_OPEN.md#grab-kbd-timing),
|
|
||||||
разбирается после готовности всех уровней (решение пользователя).
|
|
||||||
|
|
||||||
## 2. Состояние репозитория
|
|
||||||
|
|
||||||
Всё закоммичено, `main` чист. `tests-host`: **6 наборов** проходят
|
|
||||||
(`[char] 65`, `[geom] 3144`, `[grab] 55`, `[mouse] 17`, `[phys] 1727`,
|
|
||||||
`[wall] 1`).
|
|
||||||
|
|
||||||
Коммиты 2026-08-12 (сверху — свежие):
|
|
||||||
|
|
||||||
| хеш | что |
|
|
||||||
|---|---|
|
|
||||||
| `7f5c99b` | чистка факелов при перевороте — в СТАРОЙ системе координат |
|
|
||||||
| `c9aa6d9` | переворот: не перерисовывать комнату, стирать только запечённое пламя |
|
|
||||||
| `948d8f0` | переворот: перерисовка вместо отражения, переключение на границе кадра, `clip_char` |
|
|
||||||
| `5b6664f` | зеркальные кадры персонажей — готовыми файлами `*_v.atl` |
|
|
||||||
| `c4dd2c7` | отсев тайлов по fore-окну — в ЛОГИЧЕСКИХ координатах |
|
|
||||||
| `7551906` | L9-INVERT II.4: персонажи рисуются перевёрнутыми |
|
|
||||||
| `055d6d7` | чтение файлов нельзя выносить в банк (код банка сам живёт в W3) |
|
|
||||||
| `147cb18` | резидент: `pop_kid`/`pop_guard`/`main` расколоты по частоте вызова |
|
|
||||||
| `5d31ed0` | резидент: `pop_redraw` + холодная половина `pop_level` в банки |
|
|
||||||
| `f541c0a` | клип поля при перевороте (мусор в бортах) |
|
|
||||||
|
|
||||||
Уровни 1-8 проходятся, уровень 9 ждёт прогона.
|
|
||||||
|
|
||||||
## 3. Раскладка кода после разгрузки резидента
|
|
||||||
|
|
||||||
Куча выросла **902 → 5795 Б** (`_CODE` 25 628 → 20 569). Кандидатов выбирали
|
|
||||||
не по размеру, а по тому, КТО зовёт: если единственный вызывающий уже в
|
|
||||||
банке, код едет к нему и трамплин не появляется вовсе.
|
|
||||||
|
|
||||||
| банк | модули |
|
|
||||||
|---|---|
|
|
||||||
| 1 | `guards.c` |
|
|
||||||
| 2 | `pop_bg.c` |
|
|
||||||
| 3 | `pop_map.c` |
|
|
||||||
| 4 | `pop_cdraw.c` + **`pop_kdraw.c`** (клинок, блит по id — прямые вызовы) |
|
|
||||||
| 5 | `pop_ctrl.c` |
|
|
||||||
| 6 | `pop_trob.c` |
|
|
||||||
| 7 | `pop_room.c` + **`pop_redraw.c`** |
|
|
||||||
| 8 | `roomtest_cold.c` + **`pop_level_cold.c`** + **`pop_kboot.c`** + **`pop_guard_cold.c`** |
|
|
||||||
|
|
||||||
Внутренние контракты: `_pop_level.h`, `_pop_kid.h`, `_pop_kdraw.h`. В них же
|
|
||||||
записаны два правила, которые линкер НЕ проверяет:
|
|
||||||
|
|
||||||
1. **Банковый модуль не маппит W3** — он в этом окне и исполняется (через W0
|
|
||||||
можно). Поэтому `pop_level_read_file` и `pop_kid_data_load` остались в
|
|
||||||
резиденте: первый же вызов из банка улетал в halt по мусору.
|
|
||||||
2. **Прямые вызовы между модулями одного банка** корректны только пока они
|
|
||||||
действительно в одном банке; меняешь раскладку в Makefile — проверь пары.
|
|
||||||
|
|
||||||
## 4. Переворот (L9-INVERT) — как он устроен
|
|
||||||
|
|
||||||
- **Флаги:** `pop_upside_want` (пишут зелье, смерть Кида, чит `U`) и
|
|
||||||
`pop_upside` (читают все слои). Переключение — ОДНО место, начало кадра,
|
|
||||||
вместе с `pop_flip_screen`. Оригиналу этого не нужно (он рисует в offscreen
|
|
||||||
неперевёрнутым и зеркалит на выводе) — расхождение записано в
|
|
||||||
`../docs/impl_diff.md`.
|
|
||||||
- **Сам переворот:** чистка запечённого пламени (`pop_torch_wipe`, 0..4
|
|
||||||
тайла) + отражение страницы акселератором + прямая копия во вторую.
|
|
||||||
**904 247 тактов = 2.1 кадра.** Вариант с полной перерисовкой комнаты
|
|
||||||
(как `redraw_screen` в оригинале) стоил 6 245 477 = 14.5 кадра — отвергнут.
|
|
||||||
- **Спрайты персонажей:** column-major, вертикальное зеркало для них не
|
|
||||||
бесплатно, поэтому второй набор файлов делает упаковщик (`vflip_cols` в
|
|
||||||
`pop_pack_kid.py`). Загрузка одной страницы с диска — 970 003 такта,
|
|
||||||
разворот той же страницы в рантайме — 12 645 582; поэтому файлы.
|
|
||||||
`pop_vflip_load_all()` идемпотентна и зовётся откуда угодно (сейчас — старт
|
|
||||||
и смена уровня, если это `POP_UPSIDE_LEVEL`; задел под загрузку в интро).
|
|
||||||
- **Координаты:** у окна fore-клипа ДВЕ пары границ — экранная
|
|
||||||
(`pop_t_fclip_y0/y1`, по ней режется блит) и логическая
|
|
||||||
(`pop_t_fclip_ly0/ly1`, по ней идут ВСЕ отсевы тайлов). Смешать их — и
|
|
||||||
передние грани перестают перекрывать персонажа.
|
|
||||||
|
|
||||||
## 5. Состояние окружения
|
|
||||||
|
|
||||||
- **MAME запущена с мостом**, на HDD — roomtest с `LEVEL=9`. Стартовый
|
|
||||||
уровень теперь **параметр сборки**: `make LEVEL=9` (дефолт 9; для «настоящей»
|
|
||||||
игры — `LEVEL=1`), константа объявлена в `pop_tune.h`.
|
|
||||||
- **ПРАВИЛО ПОЛЬЗОВАТЕЛЯ: перед сборкой образа — `make clean`, потом `make`,
|
|
||||||
потом `make hdd`** (memory `mame_build_clean_rule`). После пересборки
|
|
||||||
образа MAME **обязана** пройти полный рестарт (memory
|
|
||||||
`mame_hdd_rebuild_restart`): остановка через `exit` в дебаггере, затем
|
|
||||||
`run_bridge.sh`.
|
|
||||||
- **Тайминги моста** (правило пользователя, длиннее не ждать; они же в
|
|
||||||
`docs/mame-autotest.md` §10): старт `run_bridge.sh` → **6 с** → `go` →
|
|
||||||
**8 с** → `keyseq d:{ENTER}` + `keyseq roomtest{ENTER}` → **5 с** →
|
|
||||||
программа работает. Весь цикл перезапуска ~20 секунд.
|
|
||||||
- **MCP-инструменты `mame-z80` работают** (в отличие от прошлой сессии):
|
|
||||||
`press_key`, `screenshot`, `read_logical_memory`, `debugger_command`.
|
|
||||||
Профилирование — `bpset <addr>` + `print totalcycles`, затем `out` и снова
|
|
||||||
`print totalcycles`; кадр = 430 000 тактов.
|
|
||||||
- Адреса состояния смещаются после КАЖДОЙ пересборки — брать из
|
|
||||||
`.sprinter-cc-roomtest/roomtest.map` заново. Банковые функции лежат по
|
|
||||||
`0x0008XXXX`; для брейкпоинта брать младшие 16 бит (`0xC912` и т.п.).
|
|
||||||
- **SDLPoP собран с отладочной информацией** (`-O0 -g3`).
|
|
||||||
- **НЕ ПРИБРАНО:** в `SDLPoP/src/seg008.c` диагностический `fprintf` с меткой
|
|
||||||
`DBGMIRROR` в начале `add_objtable`.
|
|
||||||
|
|
||||||
## 6. Остальное открытое
|
|
||||||
|
|
||||||
- **[FORE-DUP](BUGS_OPEN.md#fore-dup)** — передний слой тайла рисуется дважды
|
|
||||||
при перекрытии объектов. Картинку не портит, тратит такты. Сначала
|
|
||||||
замерить, потом чинить.
|
|
||||||
- **TORCH-ANIM-RIGHT** — под запечённым пламенем могут застыть пики/меч/зелье
|
|
||||||
справа от факела.
|
|
||||||
- **[GATE-FORE-KID](BUGS_OPEN.md#gate-fore-kid)** — Кид в проёме ворот виден
|
|
||||||
поверх решётки; портирован только шовный случай.
|
|
||||||
- Долгие: `BUG-SPIKE-1`, `BUG-CHOMP-JUMP-1`, `L3-COLOR`, `L1-SPEED`, `TUNE-1`.
|
|
||||||
- **Вид тени (OR+XOR) отложен** до готовности всех уровней —
|
|
||||||
[`../docs/shadow_render.md`](../docs/shadow_render.md).
|
|
||||||
|
|
||||||
## 7. Грабли, собранные 2026-08-12 (вечер/ночь)
|
|
||||||
|
|
||||||
- **`return 1;` из ветки у SDCC 4.5 может не положить единицу в A** — функция
|
|
||||||
возвращает мусор. Годами маскировался (в A случайно оставался ненулевой
|
|
||||||
байт) и вылез ровно на `__banked`. Репро — `docs/bugs/sdcc-z80-ret-const-lost/`,
|
|
||||||
обход — один выход через переменную. Аудит всех `.asm` roomtest: других
|
|
||||||
мест нет.
|
|
||||||
- **Банковый модуль не может маппить W3** (см. §3). Симптом — halt по мусору
|
|
||||||
в 0xC4xx на первом же вызове.
|
|
||||||
- **Логические и экранные координаты при перевороте — разные системы.** Три
|
|
||||||
бага подряд из одного корня: отсев тайлов по fore-окну, `clip_char`, чистка
|
|
||||||
факелов. Правило: всё, что считается ИЗ РЯДА комнаты (`63*row + …`), —
|
|
||||||
логическое; всё, что режет блит, — экранное.
|
|
||||||
- **Пламя факела ЗАПЕКАЕТСЯ в ОЗУ-копию** (heal'а у него нет, следующий кадр
|
|
||||||
накрывает предыдущий непрозрачно). Любое отражение/копирование страницы
|
|
||||||
тащит его с собой — перед такими операциями звать `pop_torch_wipe`.
|
|
||||||
- **`pop_upside` переключать в середине кадра нельзя**: остаток кадра
|
|
||||||
рисуется зеркально поверх ещё неперевёрнутой картинки, и запечённые
|
|
||||||
транзиенты остаются в комнате навсегда.
|
|
||||||
- **Перенос константы между файлами — потерянное значение.** При выносе
|
|
||||||
`FIRST_LEVEL` в `pop_tune.h` записал дефолт 1, и игра молча стартовала с
|
|
||||||
первого уровня вместо девятого; заметил пользователь. Теперь это параметр
|
|
||||||
сборки (`make LEVEL=N`).
|
|
||||||
- **`make -C tests-host` не пересобирает по изменению заголовков** — при
|
|
||||||
правке прототипов (особенно `__banked`) обязателен `make -C tests-host
|
|
||||||
clean`, иначе стаб и вызывающий разъезжаются по ABI и тест врёт.
|
|
||||||
|
|
||||||
## Грабли 2026-08-10/11
|
|
||||||
|
|
||||||
- **Не оценивать железо по своей же memory-заметке.** Дважды поправлял
|
|
||||||
пользователь: accel умеет не только копирование (есть AND/OR/XOR) и умеет
|
|
||||||
работать с обычной RAM, а не только с видео (`docs/part2/accelerator_doc.txt`:
|
|
||||||
«supports data blocks transfer of RAM and Video-RAM», не поддерживает только
|
|
||||||
ROM и FastRAM). Проверять по драйверу MAME и докам, а не по памяти.
|
|
||||||
- **lldb через FIFO — плохая идея.** Работает надёжно: `fprintf(stderr, …)`
|
|
||||||
прямо в SDLPoP, пересборка (секунды) и чтение stdout.
|
|
||||||
- **Доска отставала от кода на три задачи.** Перед планированием — сверять
|
|
||||||
`TASKS_OPEN.md` с кодом грепом, а не верить записи.
|
|
||||||
- Диапазон `obj_x` = **−416..695** (посчитан из `kid_data.bin`).
|
|
||||||
|
Before Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 426 KiB |
|
Before Width: | Height: | Size: 532 KiB |
|
Before Width: | Height: | Size: 413 KiB |
|
Before Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 825 KiB |
|
Before Width: | Height: | Size: 825 KiB |
|
Before Width: | Height: | Size: 825 KiB |
@@ -1,6 +0,0 @@
|
|||||||
Уровень 5, комната 2 - с разбега чуть раньше - иногда попадаем в стену (некорректное отображение падения)
|
|
||||||
|
|
||||||
Уровень 7, комната 9 - падает плита -1,1 - звук потери HP у Кида (причем раз даже когда Кид просто бъет по плите стоя на 0,0)
|
|
||||||
|
|
||||||
/// Уровень 10, комната 1 - когда Кид стоя на кнопке 1,8 роняет плиту 0,8 на кнопку 1,8 - кнопка ломается (превращается в щебень)
|
|
||||||
/// но двери которые она должна открыть остаются закрытыми
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# ===========================================================================
|
||||||
|
# SprPoP — .gitignore приложения
|
||||||
|
# ===========================================================================
|
||||||
|
# Пока SprPoP лежит внутри репозитория C-Compiler, git читает этот файл как
|
||||||
|
# правила ДЛЯ ЭТОГО ПОДДЕРЕВА, и он дополняет корневой .gitignore тулчейна.
|
||||||
|
# Когда SprPoP выделят в самостоятельный проект, файл станет КОРНЕВЫМ и
|
||||||
|
# продолжит работать как есть: все пути написаны от корня SprPoP и ничего
|
||||||
|
# не знают ни про applications/, ни про раскладку тулчейна.
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ЧУЖИЕ ДАННЫЕ — в репозиторий не идут никогда
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# assets/orig/ — оригинальные данные игры (копирайт). В репозитории вместо
|
||||||
|
# них лежит ЗНАНИЕ, откуда их взять: tools/fetch_orig.py (`make fetch`)
|
||||||
|
# качает SDLPoP и записи саундтрека, MSDOS/ пользователь кладёт сам (он
|
||||||
|
# необязателен). Скачанные архивы кэшируются в assets/orig/.cache/.
|
||||||
|
#
|
||||||
|
# Шаблон со ЗВЁЗДОЧКОЙ, а не `assets/orig/`: git не заходит внутрь
|
||||||
|
# игнорируемого каталога, и исключение ниже иначе не сработало бы, а
|
||||||
|
# README.md — единственное, что отсюда ОБЯЗАНО быть в репозитории: без
|
||||||
|
# него в чистом клоне не написано, откуда брать данные.
|
||||||
|
assets/orig/*
|
||||||
|
!assets/orig/README.md
|
||||||
|
|
||||||
|
# Уровни res20xx.bin ложатся на диск ПРЯМО из оригинала, конвертации нет —
|
||||||
|
# то есть это тот же копирайтный материал, только в раскладке диска.
|
||||||
|
# Остальное содержимое assets/packed/ версионируется намеренно: без него из
|
||||||
|
# чистого клона не собрать игру, а исходников (assets/orig/) может не быть.
|
||||||
|
assets/packed/LEVELS/
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ВЫХОД СБОРКИ
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# build/ — .exe, раскладка диска, hdd/, промежуточные атласы, stamp'ы.
|
||||||
|
# Правило без слэша спереди намеренно: под ним и tests/host/build/.
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Промежуточный каталог sprinter-cc.
|
||||||
|
.sprinter-cc-*/
|
||||||
|
|
||||||
|
# Артефакты компиляции, которые SDCC кладёт рядом с целью — в корень
|
||||||
|
# приложения. Только корень: единственный версионируемый ассемблерный
|
||||||
|
# файл (tests/host/bank_stub.s) лежит глубже и под правило не попадает.
|
||||||
|
/*.exe
|
||||||
|
/*.asm
|
||||||
|
/*.lst
|
||||||
|
/*.lk
|
||||||
|
/*.ihx
|
||||||
|
/*.noi
|
||||||
|
/*.sym
|
||||||
|
/*.map
|
||||||
|
/*.rel
|
||||||
|
/*.cdb
|
||||||
|
/*.mem
|
||||||
|
/*.rst
|
||||||
|
|
||||||
|
# Хостовые тесты и утилиты собираются нативным cc.
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.dSYM/
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ОС / редакторы / локальные настройки
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
.DS_Store
|
||||||
|
._*
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.kilo/
|
||||||
|
.claude/
|
||||||
@@ -17,8 +17,9 @@ sprinter-cc / libc / libbgi. Действуют правила корневог
|
|||||||
## Главное правило: SDLPoP — источник истины. Сначала читай, потом кодь
|
## Главное правило: SDLPoP — источник истины. Сначала читай, потом кодь
|
||||||
|
|
||||||
**`assets/orig/SDLPoP/src/` (github.com/NagyD/SDLPoP, GPLv3) — ЕДИНСТВЕННЫЙ
|
**`assets/orig/SDLPoP/src/` (github.com/NagyD/SDLPoP, GPLv3) — ЕДИНСТВЕННЫЙ
|
||||||
авторитетный источник того, как оригинальный движок это делает.** Правило
|
авторитетный источник того, как оригинальный движок это делает.** В
|
||||||
без исключений:
|
репозитории его нет: заполняется `make fetch` (`make fetch-check` — что уже
|
||||||
|
на месте). Правило без исключений:
|
||||||
|
|
||||||
1. **Перед реализацией ЛЮБОЙ функции** (движение, коллизия, окклюзия,
|
1. **Перед реализацией ЛЮБОЙ функции** (движение, коллизия, окклюзия,
|
||||||
падение, loose-полы, стражники, отрисовка, тайминги, любые числовые
|
падение, loose-полы, стражники, отрисовка, тайминги, любые числовые
|
||||||
@@ -69,6 +70,8 @@ make собрать build/sprpop.exe и разложить build
|
|||||||
make hdd + образ build/hdd/sprpop.chd
|
make hdd + образ build/hdd/sprpop.chd
|
||||||
make mame-link однократно: подставить образ в MAME (см. ниже)
|
make mame-link однократно: подставить образ в MAME (см. ниже)
|
||||||
make -C tests/host модульные тесты движка под ucsim_z80 (секунды, без MAME)
|
make -C tests/host модульные тесты движка под ucsim_z80 (секунды, без MAME)
|
||||||
|
make test-tools тесты упаковщиков на хосте (форматы, которые читает Z80)
|
||||||
|
make fetch скачать внешние данные в assets/orig/ (SDLPoP + музыка)
|
||||||
make resources перегенерировать ресурсы из assets/orig/
|
make resources перегенерировать ресурсы из assets/orig/
|
||||||
make music-mp3 музыка из другого набора (flac|mp3|ogg|mt32)
|
make music-mp3 музыка из другого набора (flac|mp3|ogg|mt32)
|
||||||
make clean снести build/ (ассеты не трогает)
|
make clean снести build/ (ассеты не трогает)
|
||||||
@@ -93,13 +96,19 @@ make distclean clean + снести assets/packed/ (вернуть — mak
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `src/` | рукописный C: главный цикл `sprpop.c`, холодная половина `sprpop_cold.c`, движок `pop_*.c` |
|
| `src/` | рукописный C: главный цикл `sprpop.c`, холодная половина `sprpop_cold.c`, движок `pop_*.c` |
|
||||||
| `gen/` | генерируемые заголовки (индексы архивов, таблицы кадров, шрифт, палитры). **Руками не править** — их печатают упаковщики; лежат в репозитории, потому что без них `src/` не собрать |
|
| `gen/` | генерируемые заголовки (индексы архивов, таблицы кадров, шрифт, палитры). **Руками не править** — их печатают упаковщики; лежат в репозитории, потому что без них `src/` не собрать |
|
||||||
| `assets/orig/` | оригинальные данные (SDLPoP, MSDOS, записи музыки). Вне репозитория — копирайт; что и откуда взять, написано в `assets/orig/README.md` |
|
| `assets/orig/` | оригинальные данные (SDLPoP, записи музыки; MSDOS — необязательно). Вне репозитория — копирайт; вместо них в репозитории лежит `tools/fetch_orig.py`, который их качает (`make fetch`). Правила — в `assets/orig/README.md` |
|
||||||
| `assets/packed/` | то, что ложится на диск игры, уже в раскладке диска (`BG/`, `KID/`, …). В репозитории — иначе из чистого клона не собрать |
|
| `assets/packed/` | то, что ложится на диск игры, уже в раскладке диска (`BG/`, `KID/`, …). В репозитории — иначе из чистого клона не собрать |
|
||||||
| `tools/` | конверторы ресурсов. Раскладку путей знает ОДИН файл — `tools/paths.py`; менять пути нужно там, а не в отдельных упаковщиках |
|
| `tools/` | конверторы ресурсов. Раскладку путей знает ОДИН файл — `tools/paths.py`; менять пути нужно там, а не в отдельных упаковщиках. Адреса ВНЕШНИХ источников — так же в одном: `tools/fetch_orig.py` |
|
||||||
| `docs/` | планы, доски, справочники; `docs/PoP/` — форматы ресурсов оригинала |
|
| `docs/` | планы, доски, справочники; `docs/PoP/` — форматы ресурсов оригинала |
|
||||||
| `tests/host/` | модульные тесты движка под ucsim_z80 |
|
| `tests/host/` | модульные тесты движка под ucsim_z80 |
|
||||||
| `build/` | выход: `sprpop.exe`, каталоги ресурсов, `hdd/`, промежуточные `atl/` |
|
| `build/` | выход: `sprpop.exe`, каталоги ресурсов, `hdd/`, промежуточные `atl/` |
|
||||||
|
|
||||||
|
Звуковые ЭФФЕКТЫ по умолчанию берутся из SDLPoP (`SND_SRC=sdlpop`) — сборка
|
||||||
|
обязана работать без оригинального дистрибутива DOS. У кого лежит
|
||||||
|
`assets/orig/MSDOS/`, включает его набор явно (`make SND_SRC=msdos`): там
|
||||||
|
оцифровка полнее — в SDLPoP пуст звук 48 `spiked` (насаживание на пики).
|
||||||
|
Разбор — `docs/sound_plan.md`.
|
||||||
|
|
||||||
Музыка собирается из одного из четырёх наборов записей
|
Музыка собирается из одного из четырёх наборов записей
|
||||||
(`assets/orig/PoP1_DOS_music/`): `flac` по умолчанию, плюс `mp3`, `ogg` и
|
(`assets/orig/PoP1_DOS_music/`): `flac` по умолчанию, плюс `mp3`, `ogg` и
|
||||||
`mt32` (исполнение Roland MT-32 — звучит иначе, длина треков другая). Набор
|
`mt32` (исполнение Roland MT-32 — звучит иначе, длина треков другая). Набор
|
||||||
@@ -153,6 +162,9 @@ Makefile склеивает архивы прямо в `assets/packed/<КАТА
|
|||||||
| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. |
|
| `pop_kid.c/.h` | Анимация/движение: интерпретатор seqtbl `play_seq` + frame_table (`seg006`), окна `Char` (loadkid/loadshad), атласы Кида. |
|
||||||
| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`): спрайт, clip_char, брызги, клинок, heal; полоса HP и палитры соперника. Там же ПРОПУСК неизменившегося кадра (`pop_char_skip_mask`). |
|
| `pop_cdraw.c/.h` | ОТРИСОВКА персонажей — одна на всех Char (порт `add_kid_to_objtable`/`add_guard_to_objtable`): спрайт, clip_char, брызги, клинок, heal; полоса HP и палитры соперника. Там же ПРОПУСК неизменившегося кадра (`pop_char_skip_mask`). |
|
||||||
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
|
| `pop_ctrl.c/.h` | Управление: диспетчер `control()` (`seg005`) + ввод `read_user_control` (`seg006`) поверх `<kbd_raw.h>`. |
|
||||||
|
| `pop_sfx.c` + `pop_sfx_cold.c` | Звук: насос CBL в резиденте + холодная загрузка набора. Раскладка эффектов НЕ компилируется в EXE — читается с диска (`SND/snd.idx`, формат `PSI1`); см. `docs/sound_plan.md` §10. |
|
||||||
|
| `pop_music.c` | Музыка (банк 9): загрузка треков, кольцо для финальной темы. Длины и длительности — из `MUS/mus.idx` (EMM-страница + accessor `pop_music_info`), в EXE их нет; см. `docs/sound_plan.md` §11. |
|
||||||
|
| `pop_snd_tbl.h` + `pop_snd_data.c` | Тип записи набора и инварианты (рукописный заголовок) + резидентные `pop_snd_tbl`/`pop_snd_page`/`pop_snd_pages`. Размер записи 5 байт — часть дискового контракта, проверяется статически. |
|
||||||
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
|
| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). |
|
||||||
|
|
||||||
## Банки: две мины, которых компилятор не видит
|
## Банки: две мины, которых компилятор не видит
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ MEMORY ?= huge
|
|||||||
#
|
#
|
||||||
# ВАЖНО: сравнивать занятость банков можно только при ОДНОМ ALLOCS — иначе
|
# ВАЖНО: сравнивать занятость банков можно только при ОДНОМ ALLOCS — иначе
|
||||||
# сравниваются не правки, а уровни оптимизации.
|
# сравниваются не правки, а уровни оптимизации.
|
||||||
ALLOCS ?= 6000
|
ALLOCS ?= 10000
|
||||||
# Build info не хранится в C: ID и дата извлекаются из текущего commit.
|
# Build info не хранится в C: ID и дата извлекаются из текущего commit.
|
||||||
BUILD_ID ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo dev)
|
BUILD_ID ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo dev)
|
||||||
BUILD_DATE ?= $(shell git log -1 --format=%cs 2>/dev/null || echo unknown)
|
BUILD_DATE ?= $(shell git log -1 --format=%cs 2>/dev/null || echo unknown)
|
||||||
@@ -91,7 +91,7 @@ BANK9_SRCS := pop_menu.c pop_status.c pop_music.c pop_app.c pop_timer.c \
|
|||||||
# стоит 51 мс, так что трамплин банк→банк на их фоне не существует.
|
# стоит 51 мс, так что трамплин банк→банк на их фоне не существует.
|
||||||
# Отдельный банк для этого не нужен: в десятом лежал один pop_pal.c на
|
# Отдельный банк для этого не нужен: в десятом лежал один pop_pal.c на
|
||||||
# 485 байт, то есть 3% страницы.
|
# 485 байт, то есть 3% страницы.
|
||||||
BANK10_SRCS := pop_pal.c pop_config.c pop_hof.c
|
BANK10_SRCS := pop_pal.c pop_config.c pop_hof.c pop_path_bank.c
|
||||||
BANK11_SRCS := pop_cutscene.c pop_intro.c
|
BANK11_SRCS := pop_cutscene.c pop_intro.c
|
||||||
BANK_NUMS := 1 2 3 4 5 6 7 8 9 10 11
|
BANK_NUMS := 1 2 3 4 5 6 7 8 9 10 11
|
||||||
BANKED_SRCS := $(foreach n,$(BANK_NUMS),$(BANK$(n)_SRCS))
|
BANKED_SRCS := $(foreach n,$(BANK_NUMS),$(BANK$(n)_SRCS))
|
||||||
@@ -100,6 +100,7 @@ BANK_FLAGS := $(foreach n,$(BANK_NUMS),\
|
|||||||
|
|
||||||
# Модули резидента (кроме sprpop.c — его подставляет app.mk).
|
# Модули резидента (кроме sprpop.c — его подставляет app.mk).
|
||||||
EXTRA_SRCS := $(addprefix $(SRC_DIR)/,pop_vflip.c pop_pace.c pop_sfx.c pop_state.c \
|
EXTRA_SRCS := $(addprefix $(SRC_DIR)/,pop_vflip.c pop_pace.c pop_sfx.c pop_state.c \
|
||||||
|
pop_snd_data.c \
|
||||||
pop_draw.c pop_tile.c pop_kid.c pop_level.c pop_geom.c pop_guard.c \
|
pop_draw.c pop_tile.c pop_kid.c pop_level.c pop_geom.c pop_guard.c \
|
||||||
pop_qsave_io.c)
|
pop_qsave_io.c)
|
||||||
|
|
||||||
@@ -166,7 +167,10 @@ SHADOW_ATL := $(foreach n,$(KID_N),$(ATL)/shadow/sk$(n).atl) \
|
|||||||
# Звук: оригинальная оцифровка на частоте CBL (10 937,5 Гц, 8 бит моно).
|
# Звук: оригинальная оцифровка на частоте CBL (10 937,5 Гц, 8 бит моно).
|
||||||
# Число файлов задаёт упаковщик (POP_SND_PAGES в gen/pop_sound_tbl.h) —
|
# Число файлов задаёт упаковщик (POP_SND_PAGES в gen/pop_sound_tbl.h) —
|
||||||
# список ОБЯЗАН совпадать: недостающий файл это молча пропавшие эффекты.
|
# список ОБЯЗАН совпадать: недостающий файл это молча пропавшие эффекты.
|
||||||
SND_ATL := $(foreach n,0 1 2 3 4 5 6 7 8 9,$(ATL)/sound/s$(n).bin)
|
# Страниц звука СТАТИЧЕСКИ НЕ ПОСЧИТАТЬ: их число зависит от источника
|
||||||
|
# (SDLPoP даёт 9, MSDOS — 10, см. SND_SRC). Поэтому списка s0..sN здесь нет,
|
||||||
|
# а архив зависит от stamp'а упаковщика и берёт страницы из каталога уже
|
||||||
|
# после того, как тот отработал (см. правило SND/snd.arc ниже).
|
||||||
TITLE_ATL := $(foreach s,0 1 2 3,$(foreach p,0 1 2 3 4,$(ATL)/title/t$(s)_$(p).atl))
|
TITLE_ATL := $(foreach s,0 1 2 3,$(foreach p,0 1 2 3 4,$(ATL)/title/t$(s)_$(p).atl))
|
||||||
# PV/INTRO: story и стадии режутся на полосы; у настоящего PV intro есть ещё
|
# PV/INTRO: story и стадии режутся на полосы; у настоящего PV intro есть ещё
|
||||||
# чистый фон b0_* и шесть <=16K actor-атласов — они остаются в EMM на время
|
# чистый фон b0_* и шесть <=16K actor-атласов — они остаются в EMM на время
|
||||||
@@ -189,6 +193,15 @@ MUS_IDS := 24 25 26 27 28 29 30 32 33 35 36 37 39 40 41 43 50 52 53 54 55 56
|
|||||||
# Формат входит в имя stamp'а, поэтому смена MUSIC_FMT сама по себе делает
|
# Формат входит в имя stamp'а, поэтому смена MUSIC_FMT сама по себе делает
|
||||||
# музыку устаревшей — принудительная пересборка не нужна.
|
# музыку устаревшей — принудительная пересборка не нужна.
|
||||||
MUSIC_FMT ?= flac
|
MUSIC_FMT ?= flac
|
||||||
|
# Источник оцифрованных ЭФФЕКТОВ (digisnd1..3): sdlpop (умолчание) или msdos.
|
||||||
|
# Умолчание — SDLPoP, потому что он выкачивается автоматически (`make fetch`)
|
||||||
|
# и сборка обязана работать вообще без оригинального дистрибутива DOS.
|
||||||
|
# Наборы НЕ одинаковы: в MSDOS оцифровка полнее (в SDLPoP `spiked` (48)
|
||||||
|
# фактически пустой, `sword_vs_sword` (10) короче — разбор в
|
||||||
|
# docs/sound_plan.md). У кого дистрибутив есть — `make SND_SRC=msdos`.
|
||||||
|
# Источник входит в имя stamp'а, поэтому смена SND_SRC сама делает звук
|
||||||
|
# устаревшим, как и смена MUSIC_FMT.
|
||||||
|
SND_SRC ?= sdlpop
|
||||||
# res2000 — attract-demo; res2001..res2014 — штатная игра. res2015 (copy
|
# res2000 — attract-demo; res2001..res2014 — штатная игра. res2015 (copy
|
||||||
# protection) намеренно не пакуется и не существует в маршруте загрузчика.
|
# protection) намеренно не пакуется и не существует в маршруте загрузчика.
|
||||||
LVL_NUMS := 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14
|
LVL_NUMS := 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14
|
||||||
@@ -209,8 +222,8 @@ DISK := README_E.TXT README_R.TXT \
|
|||||||
BG/bg.arc BG/pop_tile.pal BG/pal_tile.pal \
|
BG/bg.arc BG/pop_tile.pal BG/pal_tile.pal \
|
||||||
KID/kid.arc KID/kid.pal KID/kid.ani \
|
KID/kid.arc KID/kid.pal KID/kid.ani \
|
||||||
GUARD/guard.arc SKEL/skel.arc VIZIER/vizier.arc SHADOW/shadow.arc \
|
GUARD/guard.arc SKEL/skel.arc VIZIER/vizier.arc SHADOW/shadow.arc \
|
||||||
SND/snd.arc \
|
SND/snd.arc SND/snd.idx \
|
||||||
$(foreach i,$(MUS_IDS),MUS/m$(i).bin) \
|
$(foreach i,$(MUS_IDS),MUS/m$(i).bin) MUS/mus.idx \
|
||||||
FONT/font.atl \
|
FONT/font.atl \
|
||||||
TITLE/title.arc TITLE/title.pal \
|
TITLE/title.arc TITLE/title.pal \
|
||||||
PV/pv.arc PV/story.pal PV/pv.pal \
|
PV/pv.arc PV/story.pal PV/pv.pal \
|
||||||
@@ -220,16 +233,22 @@ DISK := README_E.TXT README_R.TXT \
|
|||||||
# список — это и зависимость exe, и то, что умеет чинить себя само.
|
# список — это и зависимость exe, и то, что умеет чинить себя само.
|
||||||
GEN_ARC_H := bg_arc.h kid_arc.h guard_arc.h skel_arc.h vizier_arc.h \
|
GEN_ARC_H := bg_arc.h kid_arc.h guard_arc.h skel_arc.h vizier_arc.h \
|
||||||
shadow_arc.h title_arc.h pv_arc.h
|
shadow_arc.h title_arc.h pv_arc.h
|
||||||
GEN_H := $(addprefix gen/,$(GEN_ARC_H) pop_bg_atlas.h kid_atlas.h kid_data.h \
|
# pop_sound_tbl.h здесь БОЛЬШЕ НЕТ: раскладка эффектов уехала на диск
|
||||||
pop_guard_pal.h pop_shadow_atlas.h pop_sound_tbl.h \
|
# (SND/snd.idx), и EXE от звукового набора не зависит — пару
|
||||||
pop_music_tbl.h pop_music_ticks.h pop_font.h)
|
# snd.idx + snd.arc можно менять без перекомпиляции.
|
||||||
|
# Ни звукового, ни музыкальных заголовков здесь БОЛЬШЕ НЕТ: раскладка
|
||||||
|
# эффектов и длины треков уехали на диск (SND/snd.idx, MUS/mus.idx), и EXE
|
||||||
|
# не зависит ни от звукового набора, ни от набора записей.
|
||||||
|
GEN_H := $(addprefix gen/,$(GEN_ARC_H) pop_bg_atlas.h kid_data.h \
|
||||||
|
pop_guard_pal.h pop_shadow_atlas.h pop_font.h)
|
||||||
|
|
||||||
PACKED_FILES := $(addprefix $(PACKED)/,$(DISK))
|
PACKED_FILES := $(addprefix $(PACKED)/,$(DISK))
|
||||||
STAGED := $(addprefix $(BUILD_DIR)/,$(DISK))
|
STAGED := $(addprefix $(BUILD_DIR)/,$(DISK))
|
||||||
|
|
||||||
EXTRA_DATA := $(STAGED)
|
EXTRA_DATA := $(STAGED)
|
||||||
# Запись вида КАТАЛОГ:файл кладёт файл в подкаталог образа, голый путь — в
|
# Запись вида КАТАЛОГ:файл кладёт файл в подкаталог HDD_DEST_DIR, голый путь —
|
||||||
# корень. Проверка на слэш ОБЯЗАТЕЛЬНА: без неё `README_E.TXT` превращался
|
# непосредственно в HDD_DEST_DIR. Проверка на слэш ОБЯЗАТЕЛЬНА: без неё
|
||||||
|
# `README_E.TXT` превращался
|
||||||
# в `README_E.TXT:build/README_E.TXT`, то есть упаковщик заводил каталог с
|
# в `README_E.TXT:build/README_E.TXT`, то есть упаковщик заводил каталог с
|
||||||
# именем файла и клал файл внутрь него (поймано пользователем на готовом
|
# именем файла и клал файл внутрь него (поймано пользователем на готовом
|
||||||
# образе).
|
# образе).
|
||||||
@@ -237,6 +256,9 @@ HDD_PACK_ARGS := $(BUILD_DIR)/$(EXAMPLE).exe \
|
|||||||
$(foreach f,$(DISK),$(if $(findstring /,$(f)),\
|
$(foreach f,$(DISK),$(if $(findstring /,$(f)),\
|
||||||
$(word 1,$(subst /, ,$(f))):$(BUILD_DIR)/$(f),\
|
$(word 1,$(subst /, ,$(f))):$(BUILD_DIR)/$(f),\
|
||||||
$(BUILD_DIR)/$(f)))
|
$(BUILD_DIR)/$(f)))
|
||||||
|
# Всё дерево SprPoP лежит рядом с EXE, но не меняет дефолт глобального
|
||||||
|
# упаковщика: остальные приложения по-прежнему попадают в корень HDD.
|
||||||
|
HDD_DEST_DIR := games/sprpop
|
||||||
# Образ живёт в build/, а не в mame/v306/IMG — приложение автономно. Связь с
|
# Образ живёт в build/, а не в mame/v306/IMG — приложение автономно. Связь с
|
||||||
# MAME — одна символьная ссылка, ставится однократно: make mame-link.
|
# MAME — одна символьная ссылка, ставится однократно: make mame-link.
|
||||||
HDD_IMG := $(CURDIR)/$(BUILD_DIR)/hdd/$(EXAMPLE).chd
|
HDD_IMG := $(CURDIR)/$(BUILD_DIR)/hdd/$(EXAMPLE).chd
|
||||||
@@ -339,6 +361,28 @@ $(STAMP)/$(1): $(2) | $(STAMP) check-orig
|
|||||||
@touch $$@
|
@touch $$@
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
# ВАРИАНТЫ РЕСУРСА (источник звука SND_SRC, набор записей MUSIC_FMT). Оба
|
||||||
|
# варианта пишут в ОДИН выход — build/atl/sound, build/atl/music, — поэтому
|
||||||
|
# одного stamp'а на вариант мало: вернувшись к прежнему варианту, make видит
|
||||||
|
# его stamp свежим и не делает НИЧЕГО, а в выходе лежит чужое (поймано на
|
||||||
|
# sdlpop -> msdos -> sdlpop: заголовок остался с 10 страницами вместо 9).
|
||||||
|
# Лечится файлом с именем текущего варианта: он переписывается только при
|
||||||
|
# смене (cmp), а его свежесть делает stamp устаревшим.
|
||||||
|
.PHONY: variant-check
|
||||||
|
variant-check: ;
|
||||||
|
# СМЕНА ВАРИАНТА СНОСИТ STAMP'Ы СВОЕГО СЕМЕЙСТВА, а не полагается на то,
|
||||||
|
# что make заметит свежесть variant-файла. Причина — секундная
|
||||||
|
# гранулярность mtime: упаковка укладывается в одну секунду с записью
|
||||||
|
# variant, и stamp того же имени считается актуальным. Возврат к прежнему
|
||||||
|
# варианту (`sdlpop -> msdos -> sdlpop`) тогда ТИХО оставлял чужой набор.
|
||||||
|
# Нет файла — правило обязано выполниться, и время тут ни при чём.
|
||||||
|
define variant_rule
|
||||||
|
$(STAMP)/$(1).variant: variant-check | $(STAMP)
|
||||||
|
@echo "$(2)" | cmp -s - $$@ || { echo "$(2)" > $$@; rm -f $(STAMP)/$(1)-*; }
|
||||||
|
endef
|
||||||
|
$(eval $(call variant_rule,sound,$(SND_SRC)))
|
||||||
|
$(eval $(call variant_rule,music,$(MUSIC_FMT)))
|
||||||
|
|
||||||
$(eval $(call stamp_rule,bg,$(TOOLS)/pop_pack_bg.py $(TOOLS)/render_room.py $(TOOLS)/paths.py,pop_pack_bg.py dungeon && $(PY) pop_pack_bg.py palace))
|
$(eval $(call stamp_rule,bg,$(TOOLS)/pop_pack_bg.py $(TOOLS)/render_room.py $(TOOLS)/paths.py,pop_pack_bg.py dungeon && $(PY) pop_pack_bg.py palace))
|
||||||
$(eval $(call stamp_rule,kid,$(TOOLS)/pop_pack_kid.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/paths.py,pop_pack_kid.py))
|
$(eval $(call stamp_rule,kid,$(TOOLS)/pop_pack_kid.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/paths.py,pop_pack_kid.py))
|
||||||
$(eval $(call stamp_rule,kid-bin,$(TOOLS)/pop_extract_kid_data.py $(TOOLS)/paths.py,pop_extract_kid_data.py))
|
$(eval $(call stamp_rule,kid-bin,$(TOOLS)/pop_extract_kid_data.py $(TOOLS)/paths.py,pop_extract_kid_data.py))
|
||||||
@@ -346,8 +390,25 @@ $(eval $(call stamp_rule,guard,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_
|
|||||||
$(eval $(call stamp_rule,skel,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py SKEL))
|
$(eval $(call stamp_rule,skel,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py SKEL))
|
||||||
$(eval $(call stamp_rule,vizier,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py VIZIER))
|
$(eval $(call stamp_rule,vizier,$(TOOLS)/pop_pack_guard.py $(TOOLS)/paths.py,pop_pack_guard.py VIZIER))
|
||||||
$(eval $(call stamp_rule,shadow,$(TOOLS)/pop_pack_shadow.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/pop_pack_kid.py $(TOOLS)/paths.py,pop_pack_shadow.py))
|
$(eval $(call stamp_rule,shadow,$(TOOLS)/pop_pack_shadow.py $(TOOLS)/pop_pack_bg.py $(TOOLS)/pop_pack_kid.py $(TOOLS)/paths.py,pop_pack_shadow.py))
|
||||||
$(eval $(call stamp_rule,sound,$(TOOLS)/pop_pack_sound.py $(TOOLS)/paths.py,pop_pack_sound.py))
|
# ЗВУК — ОСОБЫЙ СЛУЧАЙ: упаковка, сборка архива и копия индекса делаются
|
||||||
$(eval $(call stamp_rule,music-$(MUSIC_FMT),$(TOOLS)/pop_pack_music.py $(TOOLS)/paths.py,pop_pack_music.py --format $(MUSIC_FMT) $(MUS_IDS)))
|
# ОДНИМ рецептом, а не тремя правилами через таймстемпы.
|
||||||
|
#
|
||||||
|
# Почему. Упаковщик и копирование укладываются в одну секунду, а make
|
||||||
|
# сравнивает mtime с секундной гранулярностью: цель, сделанная в ту же
|
||||||
|
# секунду, что и её зависимость, считается актуальной. При смене SND_SRC
|
||||||
|
# это давало ТИХО НЕВЕРНЫЙ результат — `sdlpop -> msdos -> sdlpop`
|
||||||
|
# перепаковывал build/atl, но в assets/packed оставался прежний набор
|
||||||
|
# (10 страниц вместо 9; поймано 2026-08-31, с паузой в секунду не
|
||||||
|
# воспроизводится). Внутри одного рецепта сравнивать нечего.
|
||||||
|
$(STAMP)/sound-$(SND_SRC): $(TOOLS)/pop_pack_sound.py $(TOOLS)/pop_idx.py \
|
||||||
|
$(TOOLS)/paths.py $(TOOLS)/pop_pack_arc.py \
|
||||||
|
$(STAMP)/sound.variant | $(STAMP) check-orig
|
||||||
|
cd $(TOOLS) && $(PY) pop_pack_sound.py --source $(SND_SRC)
|
||||||
|
@mkdir -p $(PACKED)/SND
|
||||||
|
$(ARC) $(PACKED)/SND/snd.arc $$(ls $(ATL)/sound/s*.bin | sort -V)
|
||||||
|
@cp $(ATL)/sound/snd.idx $(PACKED)/SND/snd.idx
|
||||||
|
@touch $@
|
||||||
|
$(eval $(call stamp_rule,music-$(MUSIC_FMT),$(TOOLS)/pop_pack_music.py $(TOOLS)/pop_idx.py $(TOOLS)/paths.py $(STAMP)/music.variant,pop_pack_music.py --format $(MUSIC_FMT) $(MUS_IDS)))
|
||||||
$(eval $(call stamp_rule,font,$(TOOLS)/pop_extract_font.py $(TOOLS)/paths.py,pop_extract_font.py))
|
$(eval $(call stamp_rule,font,$(TOOLS)/pop_extract_font.py $(TOOLS)/paths.py,pop_extract_font.py))
|
||||||
$(eval $(call stamp_rule,title,$(TOOLS)/pop_pack_title.py $(TOOLS)/paths.py,pop_pack_title.py))
|
$(eval $(call stamp_rule,title,$(TOOLS)/pop_pack_title.py $(TOOLS)/paths.py,pop_pack_title.py))
|
||||||
$(eval $(call stamp_rule,pv,$(TOOLS)/pop_pack_intro.py $(TOOLS)/paths.py,pop_pack_intro.py))
|
$(eval $(call stamp_rule,pv,$(TOOLS)/pop_pack_intro.py $(TOOLS)/paths.py,pop_pack_intro.py))
|
||||||
@@ -361,14 +422,13 @@ $(1): $(STAMP)/$(2)
|
|||||||
@test -f $$@ || { $(MAKE) GENERATE=1 -B $(STAMP)/$(2); test -f $$@; }
|
@test -f $$@ || { $(MAKE) GENERATE=1 -B $(STAMP)/$(2); test -f $$@; }
|
||||||
endef
|
endef
|
||||||
$(eval $(call from_stamp,$(BG_ATL) $(ATL)/bg/pop_tile.pal $(ATL)/bg/pal_tile.pal gen/pop_bg_atlas.h,bg))
|
$(eval $(call from_stamp,$(BG_ATL) $(ATL)/bg/pop_tile.pal $(ATL)/bg/pal_tile.pal gen/pop_bg_atlas.h,bg))
|
||||||
$(eval $(call from_stamp,$(KID_ATL) $(ATL)/kid/kid.pal gen/kid_atlas.h,kid))
|
$(eval $(call from_stamp,$(KID_ATL) $(ATL)/kid/kid.pal,kid))
|
||||||
$(eval $(call from_stamp,$(ATL)/kid/kid.ani gen/kid_data.h,kid-bin))
|
$(eval $(call from_stamp,$(ATL)/kid/kid.ani gen/kid_data.h,kid-bin))
|
||||||
$(eval $(call from_stamp,$(GUARD_ATL) gen/pop_guard_pal.h,guard))
|
$(eval $(call from_stamp,$(GUARD_ATL) gen/pop_guard_pal.h,guard))
|
||||||
$(eval $(call from_stamp,$(SKEL_ATL),skel))
|
$(eval $(call from_stamp,$(SKEL_ATL),skel))
|
||||||
$(eval $(call from_stamp,$(VIZIER_ATL),vizier))
|
$(eval $(call from_stamp,$(VIZIER_ATL),vizier))
|
||||||
$(eval $(call from_stamp,$(SHADOW_ATL) gen/pop_shadow_atlas.h,shadow))
|
$(eval $(call from_stamp,$(SHADOW_ATL) gen/pop_shadow_atlas.h,shadow))
|
||||||
$(eval $(call from_stamp,$(SND_ATL) gen/pop_sound_tbl.h,sound))
|
$(eval $(call from_stamp,$(foreach i,$(MUS_IDS),$(ATL)/music/m$(i).bin) $(ATL)/music/mus.idx,music-$(MUSIC_FMT)))
|
||||||
$(eval $(call from_stamp,$(foreach i,$(MUS_IDS),$(ATL)/music/m$(i).bin) gen/pop_music_tbl.h gen/pop_music_ticks.h,music-$(MUSIC_FMT)))
|
|
||||||
$(eval $(call from_stamp,$(ATL)/font/font.atl gen/pop_font.h,font))
|
$(eval $(call from_stamp,$(ATL)/font/font.atl gen/pop_font.h,font))
|
||||||
$(eval $(call from_stamp,$(TITLE_ATL) $(ATL)/title/title.pal,title))
|
$(eval $(call from_stamp,$(TITLE_ATL) $(ATL)/title/title.pal,title))
|
||||||
$(eval $(call from_stamp,$(PV_ATL) $(ATL)/pv/story.pal $(ATL)/pv/pv.pal,pv))
|
$(eval $(call from_stamp,$(PV_ATL) $(ATL)/pv/story.pal $(ATL)/pv/pv.pal,pv))
|
||||||
@@ -392,9 +452,15 @@ $(eval $(call arc_rule,TITLE,title,title,$(TITLE_ATL),TITLE))
|
|||||||
$(eval $(call arc_rule,PV,pv,pv,$(PV_ATL),PV))
|
$(eval $(call arc_rule,PV,pv,pv,$(PV_ATL),PV))
|
||||||
# У звука своего *_arc.h нет: содержимое адресуется таблицей pop_sound_tbl.h,
|
# У звука своего *_arc.h нет: содержимое адресуется таблицей pop_sound_tbl.h,
|
||||||
# которую печатает сам упаковщик звука.
|
# которую печатает сам упаковщик звука.
|
||||||
$(PACKED)/SND/snd.arc: $(SND_ATL) $(TOOLS)/pop_pack_arc.py
|
# Зависимость на stamp, а не на список страниц: сколько их будет, известно
|
||||||
@mkdir -p $(dir $@)
|
# только после запуска упаковщика (источник задаёт число). Порядок страниц
|
||||||
$(ARC) $@ $(SND_ATL)
|
# в архиве обязан быть ЧИСЛОВЫМ — pop_snd_tbl адресует их индексом, а голый
|
||||||
|
# ls даёт s10 перед s2. sort -V, а не `-t s -k n`: буква s есть и в пути.
|
||||||
|
# Пара snd.arc + snd.idx согласована по построению: её делает ОДИН рецепт
|
||||||
|
# stamp'а выше (см. там же, почему не раздельными правилами). Здесь
|
||||||
|
# только восстановление, если файл снесли руками.
|
||||||
|
$(PACKED)/SND/snd.arc $(PACKED)/SND/snd.idx: $(STAMP)/sound-$(SND_SRC)
|
||||||
|
@test -f $@ || { $(MAKE) GENERATE=1 -B $(STAMP)/sound-$(SND_SRC); test -f $@; }
|
||||||
|
|
||||||
# Файлы, которые ложатся на диск как есть, — простое копирование из staging.
|
# Файлы, которые ложатся на диск как есть, — простое копирование из staging.
|
||||||
define copy_rule
|
define copy_rule
|
||||||
@@ -411,10 +477,12 @@ $(eval $(call copy_rule,TITLE/title.pal,$(ATL)/title/title.pal))
|
|||||||
$(eval $(call copy_rule,PV/story.pal,$(ATL)/pv/story.pal))
|
$(eval $(call copy_rule,PV/story.pal,$(ATL)/pv/story.pal))
|
||||||
$(eval $(call copy_rule,PV/pv.pal,$(ATL)/pv/pv.pal))
|
$(eval $(call copy_rule,PV/pv.pal,$(ATL)/pv/pv.pal))
|
||||||
$(foreach i,$(MUS_IDS),$(eval $(call copy_rule,MUS/m$(i).bin,$(ATL)/music/m$(i).bin)))
|
$(foreach i,$(MUS_IDS),$(eval $(call copy_rule,MUS/m$(i).bin,$(ATL)/music/m$(i).bin)))
|
||||||
|
$(eval $(call copy_rule,MUS/mus.idx,$(ATL)/music/mus.idx))
|
||||||
# Уровни идут на диск ПРЯМО из оригинала: формат res20xx.bin наш загрузчик
|
# Уровни идут на диск ПРЯМО из оригинала: формат res20xx.bin наш загрузчик
|
||||||
# читает как есть, упаковщика для них нет. check-orig здесь тоже order-only —
|
# читает как есть, упаковщика для них нет. check-orig здесь тоже order-only —
|
||||||
# без оригиналов взять уровни неоткуда, и сказать об этом надо внятно.
|
# без оригиналов взять уровни неоткуда, и сказать об этом надо внятно.
|
||||||
LVL_ORIG := assets/orig/SDLPoP/data/LEVELS
|
LVL_ORIG := assets/orig/SDLPoP/data/LEVELS
|
||||||
|
SND_ORIG_MSDOS := assets/orig/MSDOS
|
||||||
define lvl_rule
|
define lvl_rule
|
||||||
$(PACKED)/LEVELS/res20$(1).bin: $(LVL_ORIG)/res20$(1).bin | check-orig
|
$(PACKED)/LEVELS/res20$(1).bin: $(LVL_ORIG)/res20$(1).bin | check-orig
|
||||||
@mkdir -p $$(dir $$@)
|
@mkdir -p $$(dir $$@)
|
||||||
@@ -443,8 +511,8 @@ resources-bg: $(PACKED)/BG/bg.arc $(PACKED)/BG/pop_tile.pal $(PACKED)/BG/pal
|
|||||||
resources-kid: $(PACKED)/KID/kid.arc $(PACKED)/KID/kid.pal $(PACKED)/KID/kid.ani
|
resources-kid: $(PACKED)/KID/kid.arc $(PACKED)/KID/kid.pal $(PACKED)/KID/kid.ani
|
||||||
resources-actors: $(PACKED)/GUARD/guard.arc $(PACKED)/SKEL/skel.arc \
|
resources-actors: $(PACKED)/GUARD/guard.arc $(PACKED)/SKEL/skel.arc \
|
||||||
$(PACKED)/VIZIER/vizier.arc $(PACKED)/SHADOW/shadow.arc
|
$(PACKED)/VIZIER/vizier.arc $(PACKED)/SHADOW/shadow.arc
|
||||||
resources-sound: $(PACKED)/SND/snd.arc
|
resources-sound: $(PACKED)/SND/snd.arc $(PACKED)/SND/snd.idx
|
||||||
resources-music: $(foreach i,$(MUS_IDS),$(PACKED)/MUS/m$(i).bin)
|
resources-music: $(foreach i,$(MUS_IDS),$(PACKED)/MUS/m$(i).bin) $(PACKED)/MUS/mus.idx
|
||||||
resources-font: $(PACKED)/FONT/font.atl
|
resources-font: $(PACKED)/FONT/font.atl
|
||||||
resources-title: $(PACKED)/TITLE/title.arc $(PACKED)/TITLE/title.pal
|
resources-title: $(PACKED)/TITLE/title.arc $(PACKED)/TITLE/title.pal
|
||||||
resources-pv: $(PACKED)/PV/pv.arc $(PACKED)/PV/story.pal $(PACKED)/PV/pv.pal
|
resources-pv: $(PACKED)/PV/pv.arc $(PACKED)/PV/story.pal $(PACKED)/PV/pv.pal
|
||||||
@@ -455,8 +523,12 @@ resources: resources-bg resources-kid resources-actors resources-sound \
|
|||||||
|
|
||||||
check-orig:
|
check-orig:
|
||||||
@test -d $(LVL_ORIG) || { \
|
@test -d $(LVL_ORIG) || { \
|
||||||
echo "SprPoP: нет assets/orig/ — генерировать ресурсы не из чего." >&2; \
|
echo "SprPoP: нет assets/orig/SDLPoP — генерировать ресурсы не из чего." >&2; \
|
||||||
echo " Что и откуда положить, написано в assets/orig/README.md" >&2; false; }
|
echo " Скачать: make fetch (подробности — assets/orig/README.md)" >&2; false; }
|
||||||
|
@test "$(SND_SRC)" != msdos || test -d $(SND_ORIG_MSDOS) || { \
|
||||||
|
echo "SprPoP: SND_SRC=msdos, но нет $(SND_ORIG_MSDOS)." >&2; \
|
||||||
|
echo " Дистрибутив DOS не качается — положить руками, либо собирать" >&2; \
|
||||||
|
echo " со звуком из SDLPoP: make SND_SRC=sdlpop" >&2; false; }
|
||||||
|
|
||||||
else
|
else
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
@@ -506,6 +578,33 @@ $(MUSIC_TARGETS):
|
|||||||
@$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) resources-music
|
@$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) resources-music
|
||||||
@$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) stage
|
@$(MAKE) MUSIC_FMT=$(patsubst music-%,%,$@) stage
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Внешние исходные данные (assets/orig/)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# В репозитории чужих данных нет — есть tools/fetch_orig.py, который знает,
|
||||||
|
# откуда их взять, и таблица источников внутри него. Качаем только то,
|
||||||
|
# чего нет; `FETCH_ARGS=--force` перекачивает заново, `FETCH_ARGS=--ref v1.23`
|
||||||
|
# берёт конкретный тег SDLPoP.
|
||||||
|
# make fetch SDLPoP + записи музыки
|
||||||
|
# make fetch-sdlpop только SDLPoP (data/ + src/-эталон)
|
||||||
|
# make fetch-music только записи саундтрека (43 МБ)
|
||||||
|
# make fetch-check что уже на месте
|
||||||
|
# make fetch-list таблица источников
|
||||||
|
# MSDOS/ не качается — это копирайтный дистрибутив; он НЕОБЯЗАТЕЛЕН, всё
|
||||||
|
# нужное (уровни, оцифровка) есть в SDLPoP. См. SND_SRC выше.
|
||||||
|
FETCH := $(PY) $(TOOLS)/fetch_orig.py
|
||||||
|
fetch: ; $(FETCH) all $(FETCH_ARGS)
|
||||||
|
fetch-sdlpop: ; $(FETCH) sdlpop $(FETCH_ARGS)
|
||||||
|
fetch-music: ; $(FETCH) music $(FETCH_ARGS)
|
||||||
|
fetch-check: ; @$(FETCH) --check
|
||||||
|
fetch-list: ; @$(FETCH) --list
|
||||||
|
FETCH_TARGETS := fetch fetch-sdlpop fetch-music fetch-check fetch-list
|
||||||
|
|
||||||
|
# Тесты инструментов (хост, питон): форматы, которые читает Z80. Секунды,
|
||||||
|
# ни эмулятора, ни оригиналов не нужно.
|
||||||
|
test-tools:
|
||||||
|
@cd $(CURDIR) && $(PY) -m unittest discover -s tools/tests
|
||||||
|
|
||||||
# Перелинковка приложения без пересборки библиотек и ресурсов.
|
# Перелинковка приложения без пересборки библиотек и ресурсов.
|
||||||
relink:
|
relink:
|
||||||
$(MAKE) -W Makefile $(EXE)
|
$(MAKE) -W Makefile $(EXE)
|
||||||
@@ -525,4 +624,4 @@ distclean: clean
|
|||||||
.PHONY: stage check-orig resources resources-rebuild resources-bg resources-kid \
|
.PHONY: stage check-orig resources resources-rebuild resources-bg resources-kid \
|
||||||
resources-actors resources-sound resources-music resources-font \
|
resources-actors resources-sound resources-music resources-font \
|
||||||
resources-title resources-pv resources-levels $(MUSIC_TARGETS) \
|
resources-title resources-pv resources-levels $(MUSIC_TARGETS) \
|
||||||
relink distclean mame-link
|
$(FETCH_TARGETS) test-tools relink distclean mame-link
|
||||||
|
|||||||
@@ -13,12 +13,19 @@ make # build/sprpop.exe + ресурсы
|
|||||||
make hdd # + образ build/hdd/sprpop.chd
|
make hdd # + образ build/hdd/sprpop.chd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Чужих данных в репозитории нет — есть адреса, откуда их взять. Чтобы
|
||||||
|
перегенерировать ресурсы (или получить эталон механики), сначала:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make fetch # SDLPoP + записи саундтрека DOS-версии в assets/orig/
|
||||||
|
```
|
||||||
|
|
||||||
## Что где
|
## Что где
|
||||||
|
|
||||||
```
|
```
|
||||||
src/ рукописный C: главный цикл sprpop.c + движок pop_*.c
|
src/ рукописный C: главный цикл sprpop.c + движок pop_*.c
|
||||||
gen/ генерируемые заголовки — руками не править
|
gen/ генерируемые заголовки — руками не править
|
||||||
assets/orig/ оригинальные данные игры (вне репозитория, см. там README)
|
assets/orig/ оригинальные данные игры (вне репозитория, качает `make fetch`)
|
||||||
assets/packed/ то, что ложится на диск игры, в раскладке диска
|
assets/packed/ то, что ложится на диск игры, в раскладке диска
|
||||||
tools/ конверторы ресурсов; все пути — в tools/paths.py
|
tools/ конверторы ресурсов; все пути — в tools/paths.py
|
||||||
docs/ планы, доски задач и багов, справочники (начинать с docs/README.md)
|
docs/ планы, доски задач и багов, справочники (начинать с docs/README.md)
|
||||||
@@ -33,9 +40,12 @@ build/ выход: sprpop.exe, каталоги ресурсов, hdd/,
|
|||||||
| `make` | собрать `build/sprpop.exe` и разложить ресурсы в `build/` |
|
| `make` | собрать `build/sprpop.exe` и разложить ресурсы в `build/` |
|
||||||
| `make hdd` | + образ жёсткого диска `build/hdd/sprpop.chd` |
|
| `make hdd` | + образ жёсткого диска `build/hdd/sprpop.chd` |
|
||||||
| `make mame-link` | однократно подставить образ в MAME (символьная ссылка на `IMG/test_hdd.chd`) |
|
| `make mame-link` | однократно подставить образ в MAME (символьная ссылка на `IMG/test_hdd.chd`) |
|
||||||
|
| `make fetch` | скачать внешние исходные данные в `assets/orig/`: `fetch-sdlpop`, `fetch-music`, `fetch-check`, `fetch-list` |
|
||||||
| `make resources` | перегенерировать ресурсы из `assets/orig/` |
|
| `make resources` | перегенерировать ресурсы из `assets/orig/` |
|
||||||
| `make music-mp3` | музыка из другого набора записей: `music-flac` (умолчание), `music-mp3`, `music-ogg`, `music-mt32` |
|
| `make music-mp3` | музыка из другого набора записей: `music-flac` (умолчание), `music-mp3`, `music-ogg`, `music-mt32` |
|
||||||
|
| `make SND_SRC=msdos` | оцифровка эффектов из дистрибутива DOS вместо SDLPoP (нужен свой `assets/orig/MSDOS/`) |
|
||||||
| `make -C tests/host` | модульные тесты движка (секунды, без эмулятора) |
|
| `make -C tests/host` | модульные тесты движка (секунды, без эмулятора) |
|
||||||
|
| `make test-tools` | тесты упаковщиков на хосте: форматы файлов, которые читает Z80 |
|
||||||
| `make clean` | снести `build/` (ассеты не трогает) |
|
| `make clean` | снести `build/` (ассеты не трогает) |
|
||||||
| `make distclean` | `clean` + снести упакованные ассеты `assets/packed/` |
|
| `make distclean` | `clean` + снести упакованные ассеты `assets/packed/` |
|
||||||
|
|
||||||
@@ -54,7 +64,14 @@ build/ выход: sprpop.exe, каталоги ресурсов, hdd/,
|
|||||||
|
|
||||||
`assets/orig/` нужен только чтобы ресурсы **перегенерировать** (`make
|
`assets/orig/` нужен только чтобы ресурсы **перегенерировать** (`make
|
||||||
resources`) и как **эталон механики**: любая правка поведения сверяется с
|
resources`) и как **эталон механики**: любая правка поведения сверяется с
|
||||||
`assets/orig/SDLPoP/src/` до кодинга, см. `CLAUDE.md`.
|
`assets/orig/SDLPoP/src/` до кодинга, см. `CLAUDE.md`. Заполняется он одной
|
||||||
|
командой `make fetch` — качается SDLPoP (ресурсы + исходники-эталон) и записи
|
||||||
|
саундтрека; адрес архива музыки скрипт берёт из самого SDLPoP.
|
||||||
|
|
||||||
|
Оригинальный дистрибутив DOS (`assets/orig/MSDOS/`) **не нужен**: и уровни, и
|
||||||
|
оцифровка звука берутся из SDLPoP. Он даёт ровно одно — более полный набор
|
||||||
|
эффектов (в SDLPoP пуст звук `spiked`, 48); у кого дистрибутив есть, включает
|
||||||
|
его явно: `make SND_SRC=msdos`. Подробности — `assets/orig/README.md`.
|
||||||
|
|
||||||
## Происхождение
|
## Происхождение
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# assets/orig — оригинальные данные
|
||||||
|
|
||||||
|
Здесь лежат **исходные** данные Prince of Persia, из которых `make resources`
|
||||||
|
делает ресурсы Sprinter-версии. В репозиторий этот каталог не идёт —
|
||||||
|
материал копирайтный (см. `.gitignore`). Вместо данных в репозитории лежит
|
||||||
|
**знание, откуда они берутся**: `tools/fetch_orig.py` и таблица источников
|
||||||
|
внутри него.
|
||||||
|
|
||||||
|
## Как заполнить
|
||||||
|
|
||||||
|
```
|
||||||
|
make fetch # SDLPoP + записи саундтрека (всё, что качается)
|
||||||
|
make fetch-check # что уже на месте
|
||||||
|
make fetch-list # таблица источников с адресами
|
||||||
|
```
|
||||||
|
|
||||||
|
Тонкости:
|
||||||
|
|
||||||
|
```
|
||||||
|
make fetch-sdlpop только SDLPoP
|
||||||
|
make fetch-music только музыка (43 МБ)
|
||||||
|
make FETCH_ARGS=--force fetch перекачать заново
|
||||||
|
make FETCH_ARGS="--ref v1.23" fetch-sdlpop конкретный тег SDLPoP
|
||||||
|
```
|
||||||
|
|
||||||
|
Скачанные архивы кэшируются в `assets/orig/.cache/` — повторный `make fetch`
|
||||||
|
сеть не трогает. Версия SDLPoP записывается в `assets/orig/SDLPoP/.fetch.json`
|
||||||
|
(ветка/тег, sha, дата), и следующий `make fetch` по ней понимает, последняя
|
||||||
|
у нас версия или нет.
|
||||||
|
|
||||||
|
Собрать игру без `assets/orig/` **можно**: упакованные ресурсы лежат в
|
||||||
|
`../packed/`, а генерируемые заголовки — в `../../gen/`. `assets/orig` нужен
|
||||||
|
только чтобы ресурсы **перегенерировать**.
|
||||||
|
|
||||||
|
Исключение — `LEVELS`: файлы `res20xx.bin` ложатся на диск игры как есть, без
|
||||||
|
конвертации, поэтому `assets/packed/LEVELS/` тоже вне репозитория и заполняется
|
||||||
|
из `orig` командой `make resources-levels`.
|
||||||
|
|
||||||
|
## Что и откуда
|
||||||
|
|
||||||
|
| Каталог | Что это | Где взять |
|
||||||
|
|---|---|---|
|
||||||
|
| `SDLPoP/data/` | Распакованные ресурсы DOS-версии: PNG-спрайты (`VDUNGEON`, `VPALACE`, `KID`, `GUARD`, `SKEL`, `VIZIER`, `SHADOW`, `TITLE`, `PV`, `PRINCE`), палитры, `LEVELS/res20xx.bin`, оцифровка `DIGISND*.DAT` | `make fetch-sdlpop` — <https://github.com/NagyD/SDLPoP> (GPLv3) |
|
||||||
|
| `SDLPoP/src/` | Исходники SDLPoP. Нужны не только упаковщикам (`menu.c` и `seg009.c` содержат встроенные шрифты, из `seg005/seg006` берутся таблицы кадров и `seqtbl`), но и как **эталон механики**: расхождение в поведении всегда сверяется с ними | тем же `make fetch-sdlpop` |
|
||||||
|
| `PoP1_DOS_music/` | Записи музыки DOS-версии в четырёх наборах: `flac/` (по умолчанию), `mp3/`, `ogg/`, `ogg_MT-32/` (исполнение Roland MT-32). Имена треков во всех наборах одинаковые; какой брать — задаёт `MUSIC_FMT` | `make fetch-music`. Адрес архива скрипт читает из самого SDLPoP (`ReadMe.amigaos4`, секция «AUDIO IS SLOW/AWFULL»), запасной — popot.org |
|
||||||
|
| `MSDOS/` | **Необязательно.** Оригинальная поставка PoP 1.0 для MS-DOS. Используется только `digisnd*.dat` со звуком, и только если попросить: `make SND_SRC=msdos` | Не качается — дистрибутив игры, положить руками |
|
||||||
|
|
||||||
|
## MSDOS: зачем он и почему без него живётся
|
||||||
|
|
||||||
|
По умолчанию **всё** берётся из SDLPoP — и уровни, и оцифровка звука. Так
|
||||||
|
сборка работает на чистой машине сразу после `make fetch`.
|
||||||
|
|
||||||
|
`MSDOS/` даёт ровно одно: более полный набор оцифрованных эффектов. Из 31
|
||||||
|
звука 28 совпадают побайтно, а три — нет, и все три лучше в дистрибутиве:
|
||||||
|
`spiked` (48, насаживание на пики) в наборе SDLPoP длиной 7 сэмплов, то есть
|
||||||
|
его нет; `sword_vs_sword` (10) короче; `sword_moving` (11) — другая запись.
|
||||||
|
Разбор — `docs/sound_plan.md`. Включается это явно:
|
||||||
|
|
||||||
|
```
|
||||||
|
make SND_SRC=msdos resources-sound # перепаковать звук из дистрибутива
|
||||||
|
make SND_SRC=msdos # и собрать с ним
|
||||||
|
```
|
||||||
|
|
||||||
|
Источник входит в имя stamp'а, поэтому переключение `SND_SRC` само делает
|
||||||
|
звук устаревшим — `-B` не нужен.
|
||||||
|
|
||||||
|
## Кто что читает
|
||||||
|
|
||||||
|
| Упаковщик | Читает |
|
||||||
|
|---|---|
|
||||||
|
| `pop_pack_bg.py`, `render_room.py` | `SDLPoP/data/{VDUNGEON,VPALACE,LEVELS}` |
|
||||||
|
| `pop_pack_kid.py` | `SDLPoP/data/KID` |
|
||||||
|
| `pop_pack_guard.py` | `SDLPoP/data/{GUARD,SKEL,VIZIER,PRINCE}` |
|
||||||
|
| `pop_pack_shadow.py` | `SDLPoP/data/{KID,SHADOW}` |
|
||||||
|
| `pop_pack_title.py`, `pop_pack_intro.py` | `SDLPoP/data/{TITLE,PV,PRINCE,KID}` |
|
||||||
|
| `pop_extract_font.py` | `SDLPoP/src/{menu.c,seg009.c}` |
|
||||||
|
| `pop_extract_kid_data.py` | `SDLPoP/src/` (таблицы кадров и `seqtbl`) |
|
||||||
|
| `pop_pack_sound.py` | `SDLPoP/data/DIGISND*.DAT` (`--source sdlpop`, умолчание) либо `MSDOS/digisnd*.dat` (`--source msdos`) |
|
||||||
|
| `pop_pack_music.py` | `PoP1_DOS_music/<набор>/` (`--format flac\|mp3\|ogg\|mt32`) |
|
||||||
|
|
||||||
|
Точная раскладка путей — в `tools/paths.py`; менять её нужно там, а не в
|
||||||
|
отдельных упаковщиках. Адреса внешних источников — в `tools/fetch_orig.py`.
|
||||||