Музыка: озвучена вся заставка, тайминги сведены со шкалой оригинала
Добавлены треки 54 (тема титров), 50/53/52 (три реплики сцены с принцессой и Джафаром) — заставка звучит целиком, как в оригинале. pop_music: два слота EMM. Реплики идут встык, паузы под загрузку нет, поэтому следующая читается в НЕ играющий слот, а play подменяет резидентную таблицу страниц. Своей копии таблицы слот не держит — её хранит блок EMM. Плюс постраничная загрузка (load_begin/load_step): страница стоит 33 мс, четверть логического кадра сцены, и подкачка посреди анимации не видна. Тайминги: все длительности сцен — в тиках оригинала (60 Гц), перевод в кадры луча делает POP_T60. Пока сцены были немыми, разбег в 20 % был незаметен; с музыкой реплика кончалась раньше картинки. Переведены титры, история, хвост после PV и пейсинг самой PV-сцены. Паузы, которые оригинал отмеряет концом сэмпла, теперь равны реальной длине наших записей: конец m50 на тике 846, вход Джафара 1046, уход 2073, конец сцены 2500 (было 1959). Fade перед PV сдвинут так, чтобы полная темнота наступала в тот же момент трека, что у SDLPoP: их fade длиннее нашего на 123 тика, наш полосовой переход длиннее на 16. Финальная сцена: убраны песочные часы — end_sequence_anim не трогает hourglass_state, а reset_cutscene его обнулил. Уровень пройден, время больше не идёт. Проверено в MAME: цепочка 54 -> 55 -> 50 -> 53 -> 52 отыгрывается целиком, интро доходит до демо-режима. Host-тесты: 15 наборов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,10 +18,18 @@
|
||||
#include "pop_sfx.h"
|
||||
|
||||
uint8_t pop_mus_page[POP_MUS_PAGES];
|
||||
static uint8_t mus_blk; /* блок EMM под трек */
|
||||
static uint8_t mus_pages; /* сколько страниц реально занято */
|
||||
static uint16_t mus_blocks; /* длина трека в порциях по 128 Б */
|
||||
static uint8_t mus_id; /* что загружено — для менеджера звука */
|
||||
|
||||
/* Два слота: играющий и подгружаемый. Своей таблицы страниц слот НЕ
|
||||
* держит — её и так хранит блок EMM, и mem_get_page отдаёт номер по
|
||||
* индексу. Резидентный pop_mus_page[] заполняется на pop_music_play, для
|
||||
* одного слота: 40 байт W2 у игры лишних нет. */
|
||||
#define MUS_SLOTS 2
|
||||
static uint8_t slot_blk[MUS_SLOTS]; /* блок EMM, 0 — слот пуст */
|
||||
static uint8_t slot_pages[MUS_SLOTS];
|
||||
static uint16_t slot_blocks[MUS_SLOTS];
|
||||
static uint8_t slot_id[MUS_SLOTS];
|
||||
static uint8_t mus_cur; /* слот, что играет/играл */
|
||||
static uint8_t mus_ready; /* слот+1, готовый к запуску */
|
||||
|
||||
/* "MUS\mNN.bin" — ровно 8.3, как требует DSS. */
|
||||
static void mus_name(char *buf, uint8_t id)
|
||||
@@ -34,6 +42,25 @@ static void mus_name(char *buf, uint8_t id)
|
||||
buf[11] = 0;
|
||||
}
|
||||
|
||||
/* ld_busy отдельно от ld_fd: инициализаторов у статиков в банке нет
|
||||
* (crt0 зануляет _DATA), а «нет открытого файла» — это не ноль. */
|
||||
static uint8_t ld_busy;
|
||||
static int ld_fd;
|
||||
static uint8_t ld_slot;
|
||||
static uint8_t ld_next; /* следующая страница к чтению */
|
||||
static uint8_t ld_pages;
|
||||
static uint8_t ld_id;
|
||||
|
||||
static void slot_release(uint8_t n)
|
||||
{
|
||||
if (slot_blk[n]) {
|
||||
mem_free_block(slot_blk[n]);
|
||||
slot_blk[n] = 0;
|
||||
}
|
||||
slot_pages[n] = 0;
|
||||
slot_blocks[n] = 0;
|
||||
}
|
||||
|
||||
void pop_music_stop(void) __banked
|
||||
{
|
||||
pop_mus_left = 0;
|
||||
@@ -41,63 +68,118 @@ void pop_music_stop(void) __banked
|
||||
|
||||
void pop_music_free(void) __banked
|
||||
{
|
||||
if (ld_busy) { close(ld_fd); ld_busy = 0; }
|
||||
pop_mus_left = 0;
|
||||
if (mus_blk) {
|
||||
mem_free_block(mus_blk);
|
||||
mus_blk = 0;
|
||||
}
|
||||
mus_pages = 0;
|
||||
mus_blocks = 0;
|
||||
slot_release(0);
|
||||
slot_release(1);
|
||||
mus_ready = 0;
|
||||
}
|
||||
|
||||
int8_t pop_music_load(uint8_t id) __banked
|
||||
/* --- загрузка ---------------------------------------------------------
|
||||
*
|
||||
* ПОСТРАНИЧНО, А НЕ ЦЕЛИКОМ. Реплики сцены с принцессой идут встык, и
|
||||
* следующую надо читать, пока звучит текущая. Трек — до 320 КБ, целиком
|
||||
* это 0,7 с диска: на 7,5 кадрах в секунду сцена встала бы на пять кадров.
|
||||
* Одна страница стоит 33 мс — меньше четверти кадра, и подкачка растворя-
|
||||
* ется в анимации. Кто может позволить себе паузу (титры, чёрный экран
|
||||
* между сценами), зовёт pop_music_load — тот же цикл, но целиком. */
|
||||
static void load_abort(void)
|
||||
{
|
||||
if (ld_busy) close(ld_fd);
|
||||
ld_busy = 0;
|
||||
slot_release(ld_slot);
|
||||
}
|
||||
|
||||
int8_t pop_music_load_begin(uint8_t id) __banked
|
||||
{
|
||||
char name[12];
|
||||
uint8_t i, pages;
|
||||
int fd;
|
||||
uint8_t pages, n;
|
||||
|
||||
if (ld_busy) load_abort();
|
||||
if (id >= POP_MUS_COUNT) return -1;
|
||||
pages = pop_mus_tbl[id].pages;
|
||||
if (!pages || pages > POP_MUS_PAGES) return -1;
|
||||
|
||||
pop_music_free();
|
||||
/* Грузим ВСЕГДА в не текущий слот: играющий трек читает свои страницы
|
||||
* из прерывания, и отобрать их у него нельзя. */
|
||||
n = (uint8_t)(mus_cur ^ 1);
|
||||
slot_release(n);
|
||||
mus_name(name, id);
|
||||
/* Трек лежит ОДНИМ файлом и читается порциями по странице: `open` стоит
|
||||
* дороже самого чтения (51,4 мс против 32,6 мс на 16 КБ), и платить его
|
||||
* на каждую страницу — чистые потери. Тот же путь понадобится финалу
|
||||
* `won`, который в память целиком не влезает. */
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
mus_blk = mem_alloc_pages(pages);
|
||||
if (!mus_blk) { close(fd); return -1; }
|
||||
for (i = 0; i < pages; i++) {
|
||||
pop_mus_page[i] = mem_get_page(mus_blk, i);
|
||||
/* Последняя страница короче — читаем сколько есть; ошибку ловим по
|
||||
* отрицательному коду, а не по «не 16384». */
|
||||
if (!pop_mus_page[i] ||
|
||||
bank_read_page(fd, pop_mus_page[i], 0, 16384) < 0) {
|
||||
close(fd);
|
||||
pop_music_free();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
mus_pages = pages;
|
||||
mus_blocks = pop_mus_tbl[id].blocks;
|
||||
mus_id = id;
|
||||
/* Трек лежит ОДНИМ файлом, и fd остаётся открытым до последней
|
||||
* страницы: `open` стоит дороже самого чтения (51,4 мс против 32,6 мс
|
||||
* на 16 КБ), и платить его на каждую страницу — чистые потери. */
|
||||
ld_fd = open(name, O_RDONLY);
|
||||
if (ld_fd < 0) return -1;
|
||||
slot_blk[n] = mem_alloc_pages(pages);
|
||||
if (!slot_blk[n]) { close(ld_fd); return -1; }
|
||||
ld_busy = 1;
|
||||
ld_slot = n;
|
||||
ld_next = 0;
|
||||
ld_pages = pages;
|
||||
ld_id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t pop_music_load_step(void) __banked
|
||||
{
|
||||
uint8_t p;
|
||||
|
||||
if (!ld_busy) return 1; /* нечего грузить */
|
||||
p = mem_get_page(slot_blk[ld_slot], ld_next);
|
||||
/* Последняя страница короче — читаем сколько есть; ошибку ловим по
|
||||
* отрицательному коду, а не по «не 16384». */
|
||||
if (!p || bank_read_page(ld_fd, p, 0, 16384) < 0) {
|
||||
load_abort();
|
||||
return -1;
|
||||
}
|
||||
if (++ld_next < ld_pages) return 0;
|
||||
|
||||
close(ld_fd);
|
||||
ld_busy = 0;
|
||||
slot_pages[ld_slot] = ld_pages;
|
||||
slot_blocks[ld_slot] = pop_mus_tbl[ld_id].blocks;
|
||||
slot_id[ld_slot] = ld_id;
|
||||
mus_ready = (uint8_t)(ld_slot + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t pop_music_loading(void) __banked
|
||||
{
|
||||
return ld_busy;
|
||||
}
|
||||
|
||||
int8_t pop_music_load(uint8_t id) __banked
|
||||
{
|
||||
int8_t r;
|
||||
|
||||
if (pop_music_load_begin(id) != 0) return -1;
|
||||
do {
|
||||
r = pop_music_load_step();
|
||||
} while (r == 0);
|
||||
return (r < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
void pop_music_play(void) __banked
|
||||
{
|
||||
if (!mus_pages) return;
|
||||
uint8_t n, i;
|
||||
|
||||
if (!mus_ready) return;
|
||||
n = (uint8_t)(mus_ready - 1);
|
||||
/* Насос обязан замолчать ДО подмены таблицы страниц: иначе он успеет
|
||||
* взять порцию по новому индексу из старого курсора. */
|
||||
pop_mus_left = 0;
|
||||
for (i = 0; i < slot_pages[n]; i++)
|
||||
pop_mus_page[i] = mem_get_page(slot_blk[n], i);
|
||||
if (n != mus_cur) slot_release(mus_cur);
|
||||
mus_cur = n;
|
||||
mus_ready = 0;
|
||||
/* Порядок важен: сначала позиция, потом длина. Насос стартует ровно
|
||||
* с той порции, где left стал ненулевым, и промежуточное состояние
|
||||
* «играем, но курсор от прошлого трека» не возникает. */
|
||||
pop_mus_id = mus_id; /* до left: менеджер спросит уже готовое */
|
||||
pop_mus_id = slot_id[n]; /* до left: менеджер спросит готовое */
|
||||
pop_mus_pg = 0;
|
||||
pop_mus_ptr = 0;
|
||||
pop_mus_left = mus_blocks;
|
||||
pop_mus_left = slot_blocks[n];
|
||||
}
|
||||
|
||||
uint8_t pop_music_busy(void) __banked
|
||||
|
||||
Reference in New Issue
Block a user