Commit Graph

4 Commits

Author SHA1 Message Date
snark13 7377bf6a2c SprPoP: заставка и музыка сразу после запуска, а не через несколько секунд
От запуска до проявления титула проходило ~3 с чёрного экрана в тишине:
pop_boot читал ВСЕ ресурсы игры до первого пикселя, а pop_title_show перед
fade_in вычитывал целиком трек заставки (248 КБ).

Старт разбит на три фазы (sprpop_cold.c):
- pop_boot() — настройки, POP.CFG, графика, чёрный экран, FONT.ATL;
- pop_boot_sound() — snd.idx + ОДНА страница snd.arc, индекс музыки, CBL;
- pop_boot_rest() — остальные страницы звука, kid.ani, Тень, атласы Кида;
  идемпотентно, между кусками доливает трек.

pop_title_show теперь: fade_in -> pop_boot_sound -> pop_music_stream(54) ->
pop_boot_rest под стоящим титулом.  Порядок «сначала fade_in, потом тема»
сохранён как у show_title (seg000.c:1981).  Первый такт заставки укорочен
на фактически потраченное время (часы насоса 85,4 Гц -> кадры луча
сдвигами), иначе сцена уехала бы относительно музыки.  title_wait доливает
трек по полстраницы за кадр.

Музыка играет с ПЕРВОЙ страницы: pop_music_stream уже был (игровые
джинглы), кольцо won не понадобилось — m54 это 16 страниц из 20 доступных.

Звук поднимается в два приёма (pop_sfx_init_begin/finish): насосу для
тишины нужен ровно один блок, и упаковщик обязан класть его первым блоком
страницы 0 (tools/pop_idx.py) — значит для открытия CBL хватает ОДНОЙ
страницы (33 мс вместо 440).  Пока набор неполон, pop_sfx_play пропускает
эффекты; заявки на музыку проходят.

Четыре загрузки из boot УБРАНЫ, а не отложены: атлас фона, страж, уровень 1
и pop_pal_game_load — их и так делает pop_level_switch на входе в любой
уровень (а pop_new_game_load ещё и палитру перед ним), и оба маршрута в
игру, LEVEL_LOAD и DEMO, упираются в него.  Это ~0,77 с на ровном месте.
Так же устроен оригинал: init_game_main до заставки читает только меч,
пламя, звуки и палитры, а chtab_5/6/7 грузит load_lev_spr на старте уровня.
Страховка на случай пропуска заставки в первую секунду — идемпотентный
pop_boot_rest() в начале pop_new_game_load и pop_level_switch.

FONT.ATL остаётся в минимальной фазе, и это не про шрифт: снимок палитры
физически лежит в хвосте его страницы (pop_ui.c), а вся машинерия яркости
начинается с `if (!font_ready) return`.  С отложенным шрифтом заставка
возникала разом на полной яркости — поймано пользователем.

Проверено в MAME: титул виден сразу и проявляется полосами, pop_mus_left
убывает (трек стримится), pop_snd_pages=10 (набор дочитался), цепочка
титул -> интро -> пропуск -> уровень 1 работает.  Банк 8: 95,5 % (732 Б).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DSYUpuaQpKr48kBav2iiV4
2026-09-02 11:30:53 +03:00
snark13 2349481b86 SprPoP: звуковые эффекты без перелинковки — раскладка уехала на диск
Часть II плана music_runtime_index_plan.md (SI0..SI4).  gen/pop_sound_tbl.h
БОЛЬШЕ НЕ ГЕНЕРИРУЕТСЯ: раскладка набора читается из SND/snd.idx (формат
PSI1, писатель и разборщик — tools/pop_idx.py, 22 теста в make test-tools).
Один и тот же sprpop.exe работает с набором SDLPoP (9 страниц) и MSDOS
(10) — sha256 бинарника при смене набора не меняется.

Заодно умолчание источника эффектов переведено на SDLPoP (SND_SRC=sdlpop):
сборка обязана работать без оригинального дистрибутива DOS.  У кого он
есть, включает лучший набор явно — make SND_SRC=msdos (там полнее
оцифровка: в SDLPoP звук 48 spiked пустой).

Устройство: pop_snd_tbl/pop_snd_page/pop_snd_pages — резидентные данные
(pop_snd_data.c), тип и инварианты — рукописный pop_snd_tbl.h.  Записи
читаются ОДНИМ read прямо в таблицу, поэтому sizeof(pop_snd_ent_t) == 5
стало частью дискового контракта: проверяется статически и полем размера
записи в заголовке.  POP_SND_PAGES как compile-time размер набора исчез —
вместо него POP_SND_MAX_PAGES (вместимость, 16) и runtime pop_snd_pages.

Цена: таблица переехала из _CODE в _DATA, суммарный резидент почти не
изменился (куча 239 -> 229 Б); банк 8 +601 Б на чтение и валидацию.

Валидация не доверяет файлу: заголовок целиком плюс каждая запись
(страница, смещение, кратность блоку, непересечение с блоком тишины,
выход за последнюю страницу).  Последнее считается В БЛОКАХ — байтовый
адрес конца не влезает в uint16, а 32-битная арифметика на Z80 дорога.

НЕТ ИНДЕКСА — ЭФФЕКТОВ НЕТ, НО МУЗЫКА ИГРАЕТ.  Первая версия просто
возвращала ошибку, и игра становилась непроходимой: тишину льёт первый
блок набора, без набора CBL не открывался, а с ним вставала музыка (её
блоки считает тот же насос) — заставка ждала конца трека вечно.  Теперь
поднимается пустой набор с блоком тишины.  Заливается ровно 128 байт и
под DI: gfx_w0_page_prepare ставит в страницу IRQ-стабы, и заливка всей
страницы затирала их — первое же прерывание давало чёрный экран.

Грабли сборки: смена SND_SRC тихо давала неверный результат
(sdlpop -> msdos -> sdlpop оставлял чужой набор в assets/packed).  Причина
не в логике, а в секундной гранулярности mtime.  Лечение убирает время из
решения: смена варианта сносит stamp'ы своего семейства, а упаковка,
сборка архива и копия индекса делаются одним рецептом.  То же получила и
музыка (MUSIC_FMT).

Проверено в MAME: таблица в памяти совпадает с файлом из образа побайтово;
один EXE поднимает оба набора; отладочный --order reverse (30 из 31
записей отличаются от штатных) звучит правильно; битый индекс выключает
эффекты, не роняя игру; без индекса PV-сцена проходит с музыкой; Ctrl+S
работает в обоих режимах.  Разбор — docs/sound_plan.md §10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
2026-08-31 16:14:53 +03:00
snark13 ea8efdb0fd SprPoP: обобщить HDD-сборку и очистить метаданные
Добавить общий каталог назначения для HDD и удалить локальную копию упаковщика.\n\nУбрать устаревшие generated-имена ресурсов, выводить число страниц Kid из kid.arc и ограничить звуковую таблицу горячим модулем.\n\nЗафиксировать планы runtime-индексов музыки и PCM-эффектов.
2026-08-30 16:16:06 +03:00
snark13 31b82661eb SprPoP: автономное приложение, выделенное из roomtest
Порт PoP переехал в applications/SprPoP — приложение, которое собирается
само: код, оригинальные данные, конверторы ресурсов и сборка внутри одной
папки.  Наружу знает единственный путь — корень тулчейна (SPRINTER_ROOT,
по умолчанию ../..).  applications/PoP/roomtest ЗАМОРОЖЕНА и остаётся
архивом закрытых задач, багов и исполненных планов.

Скопировано из applications/PoP/roomtest@4b74478.  Перенос проверен
побайтово: собранный sprpop.exe совпал с roomtest.exe того же коммита,
все 39 дисковых ресурсов и все 16 генерируемых заголовков — тоже, host-
тесты зелёные (15/15).

Раскладка:
  src/           рукописный C (roomtest.c -> sprpop.c)
  gen/           генерируемые заголовки, в репозитории
  assets/orig/   оригинальные данные игры, вне репозитория (копирайт)
  assets/packed/ то, что ложится на диск, в раскладке диска
  tools/         конверторы; все пути — в одном tools/paths.py
  build/         выход: exe, каталоги ресурсов, hdd/, промежуточные atl/

Сборка ресурсов: assets/packed и gen — версионируемые ВХОДЫ, а не то, что
пересчитывается каждым make.  Автоматика построена на ОТСУТСТВИИ файла, а
не на таймстемпах: git не хранит времена, и в свежем клоне сравнение по
времени превращалось бы в лотерею.  Недостающий ресурс или заголовок
чинится сам, рекурсивным вызовом в ветку генерации.

Музыка собирается из любого из четырёх наборов записей (make music-mp3,
music-mt32, ...); набор входит в имя stamp'а, поэтому смена набора сама
делает музыку устаревшей.  Длины реплик больше не захардкожены: упаковщик
печатает их в gen/pop_music_ticks.h, и шкала сцены выражена через них —
иначе mt32 (реплики на 6% длиннее) молча ломал катсцену.

Тулчейн: в app.mk два обратносовместимых крючка (SRC_DIR/BUILD_DIR),
HDD_IMG стал ?=; команда сборки roomtest не изменилась.  Корневой
make host-tests переключён на SprPoP.

Подгонка тайминга катсцены с принцессой (PV_MAGIC_LEAD): сцена
render-bound и идёт ~49 тиков/с вместо 60, из-за чего кода реплики
приходила раньше молнии.  Это обход, а не лечение; разбор с замерами —
docs/BUGS_OPEN.md, записи SND-PACE-DEAD, PV-RENDER-BOUND, MUS-LEFT-TEAR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 12:12:28 +03:00