diff options
| author | Valentin Popov <valentin@popov.link> | 2026-02-19 15:07:01 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-02-19 15:07:01 +0300 |
| commit | 4ef08d0bf6366b0bc8ccb6357b794937411f74cc (patch) | |
| tree | d733ac655029b10e466c450be25c63e156f40773 /docs/specs | |
| parent | 598137ed132d95a3e3bf9b95e9e27286cc2186ac (diff) | |
| download | fparkan-4ef08d0bf6366b0bc8ccb6357b794937411f74cc.tar.xz fparkan-4ef08d0bf6366b0bc8ccb6357b794937411f74cc.zip | |
feat: add terrain-core, tma, and unitdat crates with parsing functionality
- Introduced `terrain-core` crate for loading and processing terrain mesh data.
- Added `tma` crate for parsing mission files, including footer and object records.
- Created `unitdat` crate for reading unit data files with validation of structure.
- Implemented error handling and tests for all new crates.
- Documented object registry format and rendering pipeline in specifications.
Diffstat (limited to 'docs/specs')
| -rw-r--r-- | docs/specs/object-registry.md | 145 | ||||
| -rw-r--r-- | docs/specs/render.md | 13 |
2 files changed, 158 insertions, 0 deletions
diff --git a/docs/specs/object-registry.md b/docs/specs/object-registry.md new file mode 100644 index 0000000..0e6e2dd --- /dev/null +++ b/docs/specs/object-registry.md @@ -0,0 +1,145 @@ +# Object Registry (`objects.rlb`) + +`objects.rlb` - это не архив с готовыми мешами. +Это реестр игровых прототипов, который связывает логический идентификатор объекта (`r_h_01`, `s_tree_04`, `fr_m_brige`, ...) с набором реальных ресурсов в других архивах. + +Документ описывает формат и runtime-контракт на высоком уровне, без привязки к внутренним именам/адресам из дизассемблера. + +Связанные страницы: + +- [Missions](missions.md) +- [NRes](nres.md) +- [MSH core](msh-core.md) +- [Wear (`WEAR`)](wear.md) +- [Material (`MAT0`)](material.md) +- [Render pipeline](render.md) + +## 1. Роль в пайплайне + +При загрузке миссии движок работает так: + +1. Из `data.tma` получает `resource_name` объекта: + - либо прямой ключ (`s_tree_04`); + - либо путь к `*.dat` (например `UNITS\\UNITS\\HERO\\tut1_p.dat`). +2. Для `*.dat` читает заголовок и получает: + - `archive_name` (в retail-корпусе всегда `objects.rlb`); + - `model_key` (например `R_H_02`). +3. В `objects.rlb` по ключу (`model_key`/`resource_name`) ищет запись прототипа. +4. Из записи прототипа резолвит фактический `*.msh` и архив, где лежит геометрия. +5. Дальше запускается стандартная цепочка: + `MSH -> WEAR -> MAT0 -> Texm`. + +## 2. Контейнер + +`objects.rlb` сам является обычным `NRes`-архивом. + +Практические наблюдения на retail-корпусе: + +- формат заголовка/каталога полностью совпадает с `NRes`; +- payload каждой записи прототипа кратен `64` байтам; +- имя entry в каталоге - это логический ключ объекта (например `r_h_01`, `s_tree_04`). + +## 3. Формат payload записи прототипа + +Payload состоит из массива фиксированных записей: + +```c +struct ObjectRef64 { + char archive_name[32]; // C-строка (CP1251/ASCII) + char resource_name[32]; // C-строка (CP1251/ASCII) +} +``` + +Интерпретация: + +- `archive_name`: архив-источник (`bases.rlb`, `static.rlb`, `fortif.rlb`, `effects.rlb`, ...). +- `resource_name`: имя ресурса в этом архиве (`*.msh`, `*.wea`, `*.cpt`, `*.ctl`, `*.bas`, ...). + +Важно: + +- после первого `NUL` в 32-байтовом поле могут встречаться служебные байты; для runtime-резолва используется только C-строка до первого `NUL`; +- неизвестные хвостовые байты должны сохраняться 1:1 при writer/roundtrip-редактировании. + +## 4. Runtime-резолв геометрии + +Канонический порядок выбора меша: + +1. Найти запись прототипа по ключу в `objects.rlb`. +2. Прочитать список `ObjectRef64`. +3. Если есть ссылка на `*.msh`: + - взять первую валидную ссылку; + - открыть указанный архив; + - загрузить этот `*.msh`. +4. Если `*.msh` нет, но есть `*.bas`: + - взять stem от `*.bas` (`fr_m_brige.bas` -> `fr_m_brige`); + - искать `<stem>.msh` в том же архиве (`fortif.rlb`). +5. Если нет ни `*.msh`, ни `*.bas`, объект трактуется как не-геометрический (пример: солнечный/системный объект) и в 3D-проход не попадает. + +## 5. Типовые примеры + +`r_h_01`: + +- `bases.rlb :: r_h_01.msh` +- `bases.rlb :: r_h_01.wea` +- `bases.rlb :: r_h_01.cpt` +- ... + +`s_tree_04`: + +- `static.rlb :: s_tree_0_04.msh` +- `static.rlb :: s_tree_0_04.wea` +- ... + +`fr_m_brige`: + +- прямого `*.msh` в записи нет; +- есть `fortif.rlb :: fr_m_brige.bas`; +- меш резолвится как `fortif.rlb :: fr_m_brige.msh`. + +`sun_01`: + +- ссылки на `*.sun`/effect-ресурсы; +- 3D-меш отсутствует. + +## 6. Инварианты для reader/writer + +Reader: + +- payload записи прототипа должен быть кратен `64`; +- каждая запись читается как две независимые C-строки фиксированной длины; +- поиск в архивах должен быть case-insensitive по ASCII. + +Writer/editor: + +- сохранять порядок `ObjectRef64` без перестановок; +- сохранять неизвестные служебные байты полей 1:1; +- не нормализовать имена, если это не требуется задачей. + +## 7. Валидация + +Проверено на retail-корпусе `testdata/Parkan - Iron Strategy`: + +- все `590` записей `objects.rlb` имеют payload, кратный `64`; +- `554` записей имеют прямую ссылку на `*.msh`; +- `34` записи используют ветку через `*.bas`; +- `2` записи не содержат геометрии (системные/sun). + +Интеграционные тесты в Rust подтверждают резолв: + +- `r_h_01 -> bases.rlb :: r_h_01.msh` +- `s_tree_04 -> static.rlb :: s_tree_0_04.msh` +- `fr_m_brige -> fortif.rlb :: fr_m_brige.msh` + +## 8. Статус покрытия и что осталось до 100% + +Закрыто: + +1. Формат payload записи прототипа (`ObjectRef64`) и правила чтения. +2. Runtime-алгоритм выбора меша (`*.msh` напрямую и fallback через `*.bas`). +3. Корпусная проверка структуры и интеграционные тесты резолва. + +Осталось: + +1. Полная field-level семантика служебных байтов после `NUL` в `resource_name[32]`. +2. Формальная семантика всех категорий ссылок (`*.ctl`, `*.cpt`, `*.ndp`, `*.sun`) в терминах систем движка (не только render-пути). +3. Writer-спецификация уровня "authoring new prototype from scratch" с гарантией runtime-паритета. diff --git a/docs/specs/render.md b/docs/specs/render.md index 06feaef..ccc941b 100644 --- a/docs/specs/render.md +++ b/docs/specs/render.md @@ -167,3 +167,16 @@ void RenderFrame(Scene* scene, Camera* cam, float dt) { 1. Полный pixel-parity контур с эталонными кадрами оригинального рендера по набору моделей/сцен. 2. Формализация всех render-state деталей (точные blend/depth/cull/state transitions) для гарантии 1:1 в каждом draw-pass. 3. Полный coverage-пакет по динамическим веткам (FX-heavy кадры, сложные material-режимы, lightmap-комбинации). + +## 12. Object registry bridge (`objects.rlb`) + +Для миссионного/юнитного рендера критично учитывать промежуточный слой прототипов: + +1. `TMA`/`*.dat` обычно дают не прямой `*.msh`, а ключ прототипа. +2. Ключ резолвится через `objects.rlb` (реестр ссылок на реальные архивы ресурсов). +3. Только после этого выполняется стандартный путь: + `MSH -> WEAR -> MAT0 -> Texm`. + +Детальная спецификация этого шага вынесена в отдельную страницу: + +- [Object registry (`objects.rlb`)](object-registry.md) |
