31b82661eb
Порт 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>
105 lines
4.6 KiB
C
105 lines
4.6 KiB
C
/*
|
||
* pop_music_tbl.h — каталог музыкальных треков, СГЕНЕРИРОВАН.
|
||
* Источник: assets/orig/PoP1_DOS_music/flac (записи DOS-версии).
|
||
* Генератор: tools/pop_pack_music.py. Руками не править.
|
||
*
|
||
* Нумерация id — оригинала (SDLPoP data/music/names.txt). pages —
|
||
* сколько кусков m<id>_<nn>.bin лежит на диске, blocks — длина трека
|
||
* в порциях насоса по 128 байт (в байтах не влезло бы в uint16).
|
||
*
|
||
* Длительности реплик в тиках вынесены в pop_music_ticks.h: таблица
|
||
* ниже — 171 байт ДАННЫХ, и модулю, которому нужна одна константа,
|
||
* платить за неё копией таблицы незачем (поймано на pop_intro.c:
|
||
* +172 байта в банк 11).
|
||
*
|
||
* POP_MUS_TICKS_<id> — та же длина в ТИКАХ СЦЕНЫ (60 Гц оригинала).
|
||
* Нужна там, где сценарий ждёт окончания реплики: у оригинала это
|
||
* `do { frame(1) } while (check_sound_playing())`, у нас — пауза
|
||
* длиной в нашу запись. Считается ЗДЕСЬ, а не в C, по двум
|
||
* причинам: blocks*40 переполняет uint16 на won (9855 порций), и
|
||
* число обязано соответствовать НАБОРУ, которым собрали (flac и
|
||
* mt32 — разные исполнения разной длины).
|
||
*
|
||
* Отношение 40/57 — обратное к тому, которым шкала сцены переводит
|
||
* тики в порции насоса (57/40, см. pop_intro.c). Берём именно его,
|
||
* а не точное 10937,5/128/60, чтобы длина реплики и ход шкалы
|
||
* считались одной и той же арифметикой. Округление — к ближайшему
|
||
* (+28 перед делением): отбрасывание вниз теряло по тику на реплику.
|
||
*/
|
||
#ifndef POP_MUSIC_TBL_H
|
||
#define POP_MUSIC_TBL_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#define POP_MUS_COUNT 57
|
||
#define POP_MUS_BLOCK 128
|
||
#define POP_MUS_MAX_PAGES 78
|
||
|
||
|
||
typedef struct {
|
||
uint8_t pages; /* 0 — трека нет */
|
||
uint16_t blocks; /* длина в порциях по 128 байт */
|
||
} pop_mus_ent_t;
|
||
|
||
static const pop_mus_ent_t pop_mus_tbl[POP_MUS_COUNT] = {
|
||
{ 0, 0 }, /* 0 */
|
||
{ 0, 0 }, /* 1 */
|
||
{ 0, 0 }, /* 2 */
|
||
{ 0, 0 }, /* 3 */
|
||
{ 0, 0 }, /* 4 */
|
||
{ 0, 0 }, /* 5 */
|
||
{ 0, 0 }, /* 6 */
|
||
{ 0, 0 }, /* 7 */
|
||
{ 0, 0 }, /* 8 */
|
||
{ 0, 0 }, /* 9 */
|
||
{ 0, 0 }, /* 10 */
|
||
{ 0, 0 }, /* 11 */
|
||
{ 0, 0 }, /* 12 */
|
||
{ 0, 0 }, /* 13 */
|
||
{ 0, 0 }, /* 14 */
|
||
{ 0, 0 }, /* 15 */
|
||
{ 0, 0 }, /* 16 */
|
||
{ 0, 0 }, /* 17 */
|
||
{ 0, 0 }, /* 18 */
|
||
{ 0, 0 }, /* 19 */
|
||
{ 0, 0 }, /* 20 */
|
||
{ 0, 0 }, /* 21 */
|
||
{ 0, 0 }, /* 22 */
|
||
{ 0, 0 }, /* 23 */
|
||
{ 3, 305 }, /* 24 regular_death */
|
||
{ 2, 196 }, /* 25 find_mirror_and_find_shadow */
|
||
{ 9, 1132 }, /* 26 embrace */
|
||
{ 8, 915 }, /* 27 cutscene_before_2_4_6_12 */
|
||
{ 6, 656 }, /* 28 fight_death */
|
||
{ 4, 428 }, /* 29 meet_Jaffar */
|
||
{ 3, 299 }, /* 30 life_potion */
|
||
{ 0, 0 }, /* 31 */
|
||
{ 5, 624 }, /* 32 end_level_4_and_killed_by_shadow */
|
||
{ 2, 205 }, /* 33 potion */
|
||
{ 0, 0 }, /* 34 */
|
||
{ 7, 888 }, /* 35 cutscene_before_8_9 */
|
||
{ 10, 1256 }, /* 36 out_of_time */
|
||
{ 3, 280 }, /* 37 guard_death_and_obtaining_the_sword */
|
||
{ 0, 0 }, /* 38 */
|
||
{ 13, 1591 }, /* 39 floating */
|
||
{ 5, 596 }, /* 40 cutscene_not_much_time_left */
|
||
{ 9, 1043 }, /* 41 story_5_level_end_and_merge_with_shadow */
|
||
{ 0, 0 }, /* 42 */
|
||
{ 3, 285 }, /* 43 killed_Jaffar */
|
||
{ 0, 0 }, /* 44 */
|
||
{ 0, 0 }, /* 45 */
|
||
{ 0, 0 }, /* 46 */
|
||
{ 0, 0 }, /* 47 */
|
||
{ 0, 0 }, /* 48 */
|
||
{ 0, 0 }, /* 49 */
|
||
{ 10, 1184 }, /* 50 story_2_princess */
|
||
{ 0, 0 }, /* 51 */
|
||
{ 20, 2443 }, /* 52 story_4_Jaffar_leaves */
|
||
{ 11, 1403 }, /* 53 story_3_Jaffar_enters */
|
||
{ 17, 2064 }, /* 54 intro_theme */
|
||
{ 11, 1350 }, /* 55 story_1_absence */
|
||
{ 78, 9865 }, /* 56 won */
|
||
};
|
||
|
||
#endif /* POP_MUSIC_TBL_H */
|