Проваливающиеся полы в roomtest (порт SDLPoP seg007/seg008), проверено в MAME: - Падающий кусок (mob): правый край env-42 (w=26, доходит до mob_x+57) теперь полностью покрыт heal-коридором (MOB_W 56->64) — убран тёмный хвост-тень на полу 2,7 ПОСЛЕ падения. - Окклюзия правого куска во ВРЕМЯ падения: после отрисовки плиты перерисовывается сосед-пол draw_tile(row,col+1) в том же кадре — край прячется за полом 2,7 (порт redraw_at_cur_mob: set_redraw_full+1). - Тряска от сотрясения (knock): seqtbl-команды KNOCK_DOWN/UP в play_seq -> флаг knock -> do_knock(ряд) трясёт loose ряда из покоя (modif=0x80). KNOCK_DOWN в land-seq (приземление) и runcyc (footstep). - Фикс «бесконечной тряски» (дабл-буфер): при завершении тряски (0x84->0) перерисовать покойный кадр плиты на ОБЕИХ страницах (loose_rest, 2 кадра) — иначе на одной странице застревает дрожащий кадр правой грани (живёт в тайлах col и col+1) -> мерцание через флип. Инфраструктура/документация: - app.mk: цель `make hdd` (упаковка в D: для MCP-моста MAME). - docs: POP-DAT-FormatSpecifications.pdf/.txt как каноническая спецификация форматов ресурсов; ссылки в README/MSDOS_RESOURCE_FORMAT. - README.md/CLAUDE.md для applications/PoP и roomtest (правило «SDLPoP — источник истины», порядок слоёв, режим отладки freeze 1/2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 KiB
Формат ресурсов Prince of Persia (MS-DOS, каталог MSDOS/)
Документ описывает бинарный формат *.DAT-файлов ресурсов DOS-версии PoP.
Исходников для этой версии нет, поэтому всё, что ниже — результат
структурного (эмпирического) анализа реальных файлов из MSDOS/, а не чтения
кода. Уровень уверенности указан для каждого раздела. Все находки проверены
скриптами (Python), которые разбирают файл и валидируют согласованность
(например: смещение+размер последней записи таблицы точно совпадает с
началом самой таблицы — то есть данные и каталог стыкуются без дыр).
Основной источник спецификации формата — POP-DAT-FormatSpecifications.pdf
(и его текстовая конверсия POP-DAT-FormatSpecifications.txt в этой же папке,
для grep/цитирования): «Prince of Persia — Specifications of File Formats»,
Princed Development Team, 2008 — каноническая спецификация формата DAT v1.0,
на которой построен и SDLPoP, и Princed Resources. Разбирает контейнер, индекс,
чек-сумму, кодеки изображений (RLE / LZG), палитры, формат уровней (room
mapping, wall-drawing, room-linking, guards, start position, door events),
звук (digital waves / MIDI / PC speaker), бинарные файлы и Mac-варианты. При
любом расхождении между эмпирическими находками ниже и этим документом —
источником истины считать спецификацию (сверять §-номера: её §3.x).
Дополнительно как справка при реализации (порт на ZX Sprinter):
- SDLPoP (github.com/NagyD/SDLPoP, GPLv3) — open-source реализация
DOS-версии на основе дизассемблирования оригинального
PRINCE.EXE. Содержит рабочий код чтения.DAT-файлов и полный кодек изображений/уровней. Точные структуры (dat_table_typeи т.п.), процитированные ниже, получены через автоматический пересказ содержимого файла третьей стороной, а не через прямое чтение исходника — поэтому такие детали помечены как "требует сверки при реализации", в отличие от эмпирически подтверждённых байтовых оффсетов. - Princed Resources / PR (github.com/NagyD/PR, princed.org, GPLv2) — это
профильный инструмент именно для распаковки/запаковки
.DAT-ресурсов PoP (версии DAT 1 и 2), сделанный тем же автором. В его документации (doc/Dataformats.md) официально описаны экспортные форматы ресурсов — это подтверждает и уточняет часть находок ниже (см. §3–4), и является более надёжным источником, чем самостоятельная догадка по байтам.
Важная находка: репозиторий SDLPoP в папке data/ содержит не только
код движка, но и реальные ресурсы игры — как сырые .DAT-контейнеры, так
и уже распакованные поштучно файлы (PNG-кадры спрайтов, .pal-палитры,
.bin-дампы уровней), см. §7. Это готовый источник ассетов и одновременно
независимая проверка формата, описанного в этом документе.
1. Общий контейнер .DAT (уверенность: высокая, подтверждено на 28 файлах)
Каждый *.DAT-файл (кроме служебных config.dat/setup.dat, см. §5) — это
простой архив-контейнер: блок данных + оглавление (каталог ресурсов) в конце
файла.
1.1 Заголовок файла (6 байт, смещение 0x00)
| Смещение | Размер | Поле | Значение |
|---|---|---|---|
| 0x00 | 4 | tableOffset |
LE u32. Абсолютное смещение в файле, с которого начинается таблица оглавления. Совпадает с "концом данных". |
| 0x04 | 2 | tableSize |
LE u16. Размер таблицы оглавления в байтах. |
Инвариант, подтверждённый на всех 28 .dat-файлах в каталоге:
tableOffset + tableSize == размер файла (без исключений)
Данные ресурсов идут сразу после заголовка, начиная с байта 0x06, и
заканчиваются на tableOffset.
1.2 Таблица оглавления (по смещению tableOffset, длиной tableSize)
Таблица — плоский массив записей по 8 байт. Количество записей:
tableSize / 8 (округление вниз; в файле почти всегда остаётся 2 "лишних"
байта в хвосте таблицы — назначение не установлено, вероятно, служебное поле
инструмента-упаковщика или паддинг; на итоговый разбор не влияет).
Запись (8 байт):
| Смещение в записи | Размер | Поле | Описание |
|---|---|---|---|
| 0 | 2 | size |
LE u16 — размер данных ресурса в байтах |
| 2 | 2 | id |
LE u16 — идентификатор ресурса |
| 4 | 2 | offset |
LE u16 — абсолютное смещение данных ресурса в файле (не относительное!) |
| 6 | 2 | reserved |
во всех проверенных записях (сотни штук) всегда 0x0000 |
Проверено на levels.dat: 16 записей, id=2000..2015, и offset[i] + size[i] == offset[i+1] для всех соседних записей, а последняя запись заканчивается
ровно на tableOffset — то есть данные абсолютно плотно упакованы, без
пробелов, для этого файла. В других файлах (например guard.dat) между
записями изредка есть небольшие зазоры в несколько байт (вероятно, выравнивание
или "мёртвые" байты от инструмента-компоновщика) — не является нарушением
формата.
1.3 Диапазоны id по типам файлов (собрано эмпирически)
Похоже, что числовые ID образуют условные "пространства имён" по типу контента — вероятно, глобальные константы в оригинальном коде:
| Файл(ы) | Диапазон id |
Кол-во записей | Предполагаемое содержимое |
|---|---|---|---|
levels.dat |
2000–2015 | 16 | id=2000 — служебный блок (16 байт, см. §3); id=2001..2015 — 15 уровней |
guard.dat, fat.dat, skel.dat, shadow.dat |
750–784 (варьируется) | ~30–35 | id=751(750) — служебный блок; остальные — кадры анимации спрайта |
vizier.dat |
аналогично guard | — | кадры анимации визиря |
kid.dat |
~400+ | 220 | кадры анимации игрока (намного больше — герой умеет гораздо больше действий) |
guard1.dat, guard2.dat |
750 (1 запись) | 1 | вероятно, дополнительные/альтернативные кадры/варианты |
title.dat |
40–55 | 12 | картинки титульного экрана/логотипов |
cpalace.dat,epalace.dat,vpalace.dat,cdungeon.dat,edungeon.dat,vdungeon.dat |
200–1343 | 205–238 | фоновые тайлы дворца/подземелья, отдельно для CGA(c*)/EGA(e*)/VGA(v*) |
pv.dat |
800–981 | 103 | доп. графика (возможно, "Prince/Vizier" катсцены) |
digisnd1/2/3.dat |
10000+ | 20–44 | оцифрованный звук (Covox/Disney Sound Source) |
midisnd1/2.dat |
10024+ / аналог | 16 | General MIDI музыка |
mt32snd1/2.dat |
10000+ | 24/7 | музыка для Roland MT-32 |
ibm_snd1/2.dat |
10000+ | 44 | музыка/эффекты через PC-спикер |
prince.dat |
— (1 крупный ресурс) | — | MIDI-тема (вероятно, финальная тема "Принц"/титры — см. текстовые события "The Princess awaits") |
Во всех файлах первая (наименьшая по id) запись — маленький "служебный"
ресурс (6–44 байта), стоящий перед основным контентом. Скорее всего это
локальная мини-таблица/палитра/список ссылок для данного набора ресурсов —
по аналогии с тем, что у уровней id=2000 отдельно от самих уровней (см. §3).
2. Формат уровня (levels.dat, id=2001..2015) — уверенность: высокая
Каждая запись уровня имеет размер 2305 байт и по данным полностью
совпадает по объёму с уровнями из Apple II версии (01 POP Source/Levels/LEVELn
— ровно 2304 байта каждый, см. docs/APPLEII_RESOURCE_FORMAT.md).
Вывод: формат карты уровня в DOS-версии, судя по всему, унаследован практически без изменений от оригинального Apple II формата (Джордан Мехнер писал игру на 6502 и данные уровней переносились как есть), с добавлением одного лишнего байта в DOS-упаковке (2304+1=2305 — вероятно, контрольный байт/ маркер конца, добавленный DOS-упаковщиком ресурсов, а не часть игровых данных).
Практическое следствие: байтовая структура самого уровня (тайлы 3×10 на
экран, 24 экрана, таблицы стражников, дверей и т.д.) должна документироваться
один раз — по исходникам Apple II (см. соответствующий раздел), и напрямую
применяться к DOS levels.dat, отбросив 1 лишний байт в конце каждой записи.
Байтовые значения тайлов в дампе (в основном 0x00–0x39) визуально согласуются
с диапазоном небольших целых кодов тайлов, что для формата карты и ожидается.
Первая запись, id=2000, размер 16 байт — не уровень, а отдельный маленький
блок (возможно: количество уровней, начальный уровень, версия формата,
стартовые координаты игрока/охраны по умолчанию). Точное назначение не
установлено — требует сопоставления с дизассемблированным кодом загрузчика
уровней (в SDLPoP это, по всем признакам, отдельная процедура чтения
level ресурса).
Сверка с независимой распаковкой SDLPoP (data/LEVELS/): там лежат файлы
res2000.bin…res2015.bin (16 штук — количество совпадает). Байты
res2001.bin содержательно совпадают с тайловыми данными нашей записи
id=2001 (та же последовательность значений тайлов) — это подтверждает, что
нумерация id верна. Но есть нестыковка по размеру: у SDLPoP res2000.bin —
2305 байт (как и все остальные), тогда как в нашем локальном
levels.dat запись id=2000 — всего 16 байт. Скорее всего, это разные
релизы/сборки игры (см. §7 — размеры некоторых .dat у SDLPoP и у нас уже
отличались), и в версии SDLPoP маленький служебный блок либо отсутствует,
либо пронумерован иначе. Это не меняет сам формат контейнера, но означает,
что точную семантику 16-байтного блока id=2000 в нашей копии игры пока
нельзя проверить через данные SDLPoP — открытый вопрос.
2.1 Кросс-подтверждение по исходникам Apple II
Фоновый анализ исходников Apple II (см. docs/APPLEII_RESOURCE_FORMAT.md)
подтверждает и объясняет структуру уровня напрямую по коду. Уровень на Apple
II — дамп структуры blueprnt (EQ.S): BLUETYPE(720Б, 24 экрана×30 тайлов)
BLUESPEC(720Б) +LINKLOC(256Б) +LINKMAP(256Б) +MAP(96Б, граф соседних экранов) +INFO(256Б, метаданные/старт Кида/стражников) = ровно 2304 байта. Учитывая, что DOS-запись уровня — это ровно 2304+1 байт с байтовыми значениями тайлов, укладывающимися в диапазон 0–29 (id тайла) плюс служебные биты (аналогичноidmask=%00011111,reqmask=%00100000изEQ.S:484-486), можно с высокой уверенностью считать, что DOS-версия использует ту же самую раскладкуblueprnt, лишь с добавлением одного байта (вероятно, контрольной суммы) в конце DOS-упаковки. Это снимает необходимость отдельно реверсить формат уровня для DOS — таблица тайлов, enum id (0=space...29=archtop4), форматLINKLOC/LINKMAPиINFOиз Apple II документа применимы напрямую.
3. Графика (спрайты и фоновые тайлы) — уверенность: средняя/низкая
Файлы kid.dat, guard.dat, fat.dat, shadow.dat, skel.dat,
vizier.dat, title.dat, c/e/v-palace.dat, c/e/v-dungeon.dat, pv.dat
хранят по контейнерному формату (§1) множество мелких чанков (десятки—сотни
байт каждый).
Что подтверждено:
- Наборы
shadow.dat/kid.datиfat.dat/vizier.datсодержат побайтово идентичные фрагменты данных в начале файла — это ожидаемо: "Тень" (Shadow) визуально копирует анимацию Кида, а "Толстый страж" (Fat guard, пасхалка) переиспользует модель Визиря. Подтверждает, что персонажи одного "типа тела" используют общий набор геометрии/анимации. - Отдельные чанки не имеют очевидного унифицированного заголовка
(высота/ширина/палитра) фиксированного размера — попытка интерпретировать
первые байты чанка как
{height:u16, width:u16, flags:u16}не подтвердилась на реальных данных (получаются нереалистичные размеры для маленьких чанков). Вероятно, как и в Apple II версии (см.FRAMEDEF.S/SEQTABLE.S), геометрия кадра (ширина, высота, точка привязки) хранится отдельно от самих пиксельных данных — в таблицах внутриPRINCE.EXE, а не в.DAT-чанке. Сам чанк, вероятно, содержит только упакованные пиксельные данные (RLE/дельта-упаковка, по аналогии сUNPACK.Sв Apple II исходниках). - Точный алгоритм упаковки пикселей не восстановлен в рамках этого
анализа по сырым байтам — байт-в-байт разбор распаковщика без
дизассемблирования
PRINCE.EXEнадёжно не сделать. Но для практических целей это не требуется: см. §7 — в SDLPoP уже есть тот же самый набор изображений в готовом, распакованном виде (PNG), которым можно пользоваться напрямую как источником ассетов, не реализуя свой декодер.DAT-пикселей. Писать собственный декодер имеет смысл только если понадобится читать оригинальные.DAT"на лету" (например, для точной сверки контента именно нашей копии игры) — тогда ориентир — исходник SDLPoP (src/seg009.c).
4. Звук — уверенность: высокая (по структуре), низкая (по деталям кодека)
Обнаружено 4 параллельных набора звуковых ресурсов под разные звуковые устройства DOS-эпохи — типично для игр начала 1990-х с "звуковым меню":
| Файл | Устройство | Формат чанка |
|---|---|---|
midisnd1.dat, midisnd2.dat |
General MIDI / MPU-401 | каждый чанк = 2-байтовый LE-префикс длины + встроенный Standard MIDI File (MThd...MTrk...) |
mt32snd1.dat, mt32snd2.dat |
Roland MT-32/CM-32L | тот же формат: префикс длины + MThd/MTrk, с MT-32-специфичными SysEx (видны строки MT-32.mff, текстовые мета-события вроде "The Princess awaits") |
prince.dat |
(аналогично MIDI) | отдельный крупный музыкальный ресурс, тот же MIDI-контейнер — вероятно, финальная тема |
digisnd1/2/3.dat |
Covox / Disney Sound Source / Sound Blaster (оцифрованный звук) | чанк начинается с нескольких служебных байт, среди которых слово 0x2AF8 = 11000 — похоже на частоту дискретизации 11 кГц; далее — сырые 8-битные PCM-сэмплы (значения кластеризуются вокруг ~0x7A–0x90, типично для беззнакового 8-бит аудио, смещённого к середине шкалы) |
ibm_snd1.dat, ibm_snd2.dat |
PC Speaker | чанк — последовательность троек байт похожих на (длительность, делитель_частоты) — простой формат "бипера", отличный от MIDI |
Подтверждено разбором первых чанков в каждом файле (см. байтовые дампы,
проверялись скриптом). Точная семантика полей внутри digisnd/ibm_snd
(разрядность, порядок байт служебного заголовка) не выведена до конца — при
реализации порта достаточно распознавания по типу файла и (для MIDI-семейства)
можно напрямую воспроизводить встроенный Standard MIDI File, пропустив
2-байтовый префикс длины.
5. Готовые распакованные ассеты в SDLPoP (data/) — практический источник для порта
Репозиторий github.com/NagyD/SDLPoP содержит папку data/, где, помимо
самих .DAT-контейнеров, каждый ресурс продублирован в виде отдельно
распакованного файла, названного по его id из таблицы оглавления (§1.2).
Проверено через GitHub API (api.github.com/repos/NagyD/SDLPoP/contents/...):
Подпапка/файл в data/ |
Содержимое | Соответствие нашему разбору |
|---|---|---|
GUARD.DAT, GUARD1.DAT, GUARD2.DAT |
сырые .DAT |
размер побайтово совпадает с нашими локальными guard.dat/guard1.dat/guard2.dat (6950 / 117 / 117 байт) |
DIGISND1.DAT, MIDISND2.DAT и др. |
сырые .DAT |
размер не совпадает с нашими локальными файлами (48545 vs 50101, 18408 vs 18958) — другой релиз/сборка игры |
GUARD/res751.png … res784.png |
готовые PNG, по одному на кадр анимации, имя = res<id>.png |
id-диапазон (751-784) точно совпадает с нашим разбором guard.dat |
VPALACE/res200.pal, res201.png, res202.png, … |
палитра (JASC .pal) + PNG-кадры фонов дворца, VGA-вариант (256 цветов) |
id-диапазон (200+) совпадает с vpalace.dat |
LEVELS/res2000.bin … res2015.bin |
сырые дампы уровней по 2304-2305 байт | id совпадает с levels.dat; содержимое res2001.bin сверено побайтово с нашим id=2001 — тайловые данные совпадают |
KID/, PRINCE/, SHADOW/, SKEL/, VIZIER/, FAT/, TITLE/, VDUNGEON/, PV/, IBM_SND1/, IBM_SND2/, font/, music/ |
аналогичные наборы для остальных ресурсов | не проверялись по отдельности, но структура (папка на каждый .dat, файлы res<id>.ext) наблюдается одинаково |
Вывод: это данные из немного другого релиза DOS-версии, чем те, что
лежат у нас в MSDOS/ (см. расхождение в размере digisnd/midisnd), но
формат контейнера и нумерация id — те же самые. Практически это значит:
- Для получения играбельных PNG-спрайтов и VGA-фонов не нужно
реализовывать декодер сжатия пикселей — можно взять готовые файлы
data/<ИМЯ>/res<id>.pngнапрямую как исходный материал для конвертации под видеорежим ZX Sprinter (в т.ч.VPALACE/VDUNGEON— уже 256-цветный VGA-арт, что прямо отвечает на вопрос про полноцветность). - Если в проекте важно использовать именно ту версию контента, что в наших
MSDOS/*.dat(а не версию из SDLPoP) — распаковку своих файлов всё же придётся делать (кодек пикселей по-прежнему не восстановлен для сырых.DAT, см. §3), либо принять решение работать с версией SDLPoP как мастер-источником ассетов вместо своей.
6. Служебные не-ресурсные файлы
config.dat,setup.dat— 28 байт, не являются ресурсными контейнерами (не проходят проверку §1.1 — "размер" получается больше самого файла). Скорее всего простые бинарные структуры настроек (звук/видеорежим, выбранный на этапеSETUP.EXE/INSTALL.EXE), не связаны с игровым контентом.desktopd.cfg,setup.cfg— текстовые/бинарные конфиги DOS-инсталлятора, вне скоупа игровых ресурсов.PRINCE.EXE/PRINCE.REM— почти идентичны (отличие в единичных байтах в районе смещения ~0x4ED0), похоже на кряк/патч одного байта проверки — не относится к формату ресурсов.old-games.nfo— ASCII-арт NFO релиз-группы (old-games.ru), не игровые данные.
7. Итоговая таблица уверенности
| Раздел | Уверенность | Как подтверждено |
|---|---|---|
Контейнер .DAT (заголовок + таблица) |
Высокая | Проверено скриптом на всех 28 файлах, инвариант offset+size выполняется без исключений; независимо подтверждено именованием res<id>.* в SDLPoP data/ |
| ID-пространства ресурсов | Средняя-высокая | Наблюдение по диапазонам + сверка с res<id> именами файлов SDLPoP и побайтовым содержимым res2001.bin |
| Формат уровня = формату Apple II | Высокая (по размеру и содержимому), служебный блок id=2000 — открытый вопрос | Совпадение размера (2304 vs 2305), тайловые байты сходятся с res2001.bin из SDLPoP |
Формат изображений/спрайтов (сырой .DAT) |
Низкая-средняя | Контейнер подтверждён, кодек пикселей — нет; но практически закрыто наличием готовых PNG в SDLPoP data/ (§5) |
| Формат звука (тип контейнера) | Высокая для MIDI-семейств, средняя для digisnd/ibm_snd | Явные MIDI-сигнатуры MThd/MTrk видны в байтах |
Рекомендация для дальнейшей работы: для получения арт-ассетов (спрайты,
фоны, палитры) — использовать готовые распакованные файлы из
github.com/NagyD/SDLPoP/tree/master/data (§5), это быстрее и надёжнее
самостоятельной реализации декодера. Декодер сырого .DAT-формата
изображений и точную семантику служебных полей digisnd/ibm_snd
(§3, §4) стоит восстанавливать только если понадобится читать именно нашу
локальную копию MSDOS/*.dat "как есть" — тогда ориентир прежний: исходник
SDLPoP (src/seg009.c, src/data.c/data.h).