QuickSave F6/F9 в roomtest; bank_load_file/bank_save_file/gfx_w0_page_prepare; sprinter-cc: авто n_banks

roomtest:
- QuickSave/QuickLoad (F6/F9): снапшот 'POPQ' v3 в POP.SAV/POP.BAK на HDD,
  транзакционная запись (POP.NEW -> rename, откат при ошибке), XOR-контрольная
  сумма payload'а; сериализация всех игровых переменных через W0-примитивы
  pop_qs_*; pop_qsave_process() на границе кадра вне Char-окон
- pop_qsave_restore_room(): полная перезагрузка комнаты после загрузки
  (карта/края/швы, сброс bake-кэша, перерисовка обеих страниц, инвалидация
  кэшей спрайтов и HP)
- сериализаторы в pop_map/pop_loose_mob/pop_trob/pop_guard_ai
  (+ восстановление инвариантов: mobs_live, trob_drawn, redraw)
- immortal-чит 2 уровня: уровень 2 поглощает только малый урон Kid'а

libc/libbgi:
- bank_load_file()/bank_save_file() — резидентное файловое I/O в банк,
  без правила W3 (путь читается до переключения страницы)
- gfx_w0_page_prepare(page) — подготовка W0-окна (IRQ/NMI-стабы) одной
  функцией; atlas_load.c и roomtest переведены на новые примитивы;
  ручные ISR-стабы удалены

sprinter-cc / сборка:
- --bank N=FILE.c: автогенерация n_banks (_n_banks_auto.c), ручные
  const n_banks удалены из тестов
- roomtest/app.mk: ресурсы через stamp-файлы (.resource-stamps/) — один
  запуск упаковщика на группу вместо N под -B; HDD_PACK_ARGS
This commit is contained in:
2026-08-22 11:53:10 +03:00
parent b6699b3aef
commit f4b4852d51
57 changed files with 1610 additions and 301 deletions
+15 -13
View File
@@ -180,12 +180,14 @@ SDLPoP, сохраняются.
---
## 4. Куда писать снимок: файл, а не EMM-страница
## 4. Куда писать снимок: HDD-файл, а не EMM-страница
Рекомендация: **основной путь — файл `QUICKSAVE.SAV`; EMM-страница —
необязательный второй слот.**
Решение: **один основной слот `POP.SAV` на HDD; предыдущая
валидная запись хранится в `POP.BAK`.** EMM-слота нет: программа
работает только с HDD, а главный сценарий QuickSave обязан переживать
перезапуск игры.
> Пересмотрено 2026-08-17 по вопросу пользователя «почему EMM, а не файл».
> Пересмотрено 2026-08-21 по вопросу пользователя «почему EMM, а не файл».
> Первая редакция плана рекомендовала EMM — это была ошибка: она взвешивала
> скорость и недооценивала главный сценарий использования. Разбор оставлен
> целиком, потому что довод переносится и на другие «положить в память
@@ -214,10 +216,8 @@ MAME, обязательный после каждой пересборки об
`_fd_guard` в libc и так стоит), а «не нужен путь и права» — экономия одной
строки.
Что остаётся за EMM: мгновенный слот для «переиграть» без обращения к диску.
Делается тем же сериализатором и добавляется, если понадобится. Поэтому
обход состояния писать сразу так, чтобы «куда» было параметром — как у
SDLPoP через `process_func`.
Обход состояния всё равно писать с абстракцией чтения/записи, как у SDLPoP
через `process_func`, но второй EMM-слот в scope не входит.
**Проверить ДО кодинга:** пишется ли `test_hdd.chd` из-под MAME. Если образ
только на чтение, файловый путь упрётся в это на первом же шаге и порядок
@@ -234,6 +234,7 @@ roomtest.
+5 pop_current_level 1 Б
+6 длина полезной части 2 Б (контроль, что обход совпал)
+8 ... поля встык, ОДИН порядок на запись и на чтение ...
.. checksum 2 Б (заголовок + payload)
```
Версия проверяется первой; несовпадение — отказ, как в SDLPoP. Никаких
@@ -282,13 +283,12 @@ static void qs_walk(qs_io_t io) /* io = запись или чтение */
| шаг | что | критерий готовности |
|---|---|---|
| **QS1** | Аксессоры/сериализаторы для `static`-состояния банковых модулей: `pop_trob.c` (`room_modif`, `room_seen`, `trobs`, `trob_seed`), `pop_room.c` (`mobs_live`), страница уровня (чтение `fg`) | хост-тест `tests-host/t_qsave.c`: обход туда-обратно на синтетическом состоянии даёт байт-в-байт исходное |
| **QS0** | Проверить, что `D:` пишется из-под MAME (пробный файл из roomtest) | файл создался и читается обратно после рестарта программы |
| **QS2** | Ядро: `qs_walk` + запись/чтение файла `QUICKSAVE.SAV`, магия и версия, отказ при несовпадении | сохранение и загрузка **в той же комнате, без движения** — картинка и состояние не изменились |
| **QS1** | Аксессоры/сериализаторы для `static`-состояния банковых модулей: `pop_trob.c` (`room_modif`, `room_seen`, `trobs`, `trob_seed`), `pop_room.c` (`mobs_live`), страница уровня (чтение `fg`) | хост-тест `tests-host/t_qsave.c`: обход туда-обратно на синтетическом состоянии даёт байт-в-байт исходное |
| **QS2** | Ядро: `qs_walk` + `POP.SAV`, магия/версия/checksum, безопасная замена с предыдущей валидной копией в `POP.BAK` | сохранение и загрузка **в той же комнате, без движения**; порча SAV не портит BAK |
| **QS3** | Восстановление отрисовки (§6), включая обе страницы дабл-буфера | загрузка после перехода в другую комнату; нет мерцания через кадр |
| **QS4** | Клавиши **F6/F9** (или свободные из `pop_cheat.h`) через `<kbd_raw.h>`, флаги `need_quick_save/load`, обработка **между кадрами** | загрузка посреди боя/падения не ломает `play_seq` |
| **QS5** | Загрузка с **другого уровня** (перезагрузка уровня и атласов) | сохранить на ур. 2, уйти на ур. 12, загрузить — тайлсет и стражи верные |
| **QS6** | Опционально: второй слот в EMM-странице тем же сериализатором | мгновенное «переиграть» без обращения к диску |
Порядок не переставлять: QS0 первым (он может изменить весь план), QS3 без
QS2 нечего проверять, а QS5 обязан идти после QS3 — иначе смена тайлсета
@@ -320,5 +320,7 @@ QS2 нечего проверять, а QS5 обязан идти после QS3
5. **Дабл-буфер.** Самый вероятный источник «почти работает»: забыть вторую
страницу. Симптом — мерцание через кадр
(см. `roomtest/CLAUDE.md`, раздел про дабл-буфер).
6. **Открытый вопрос:** нужен ли снимок в файле вообще, или EMM-страницы
достаточно. Решать после QS3, по факту использования.
6. **Транзакция SAV/BAK.** До кодинга проверить на DSS семантику
rename/replace. Если атомарная замена не гарантирована, писать через
`POP.NEW`, проверять его после close и не удалять единственную валидную копию
до завершения новой.