aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-19 15:07:01 +0300
committerValentin Popov <valentin@popov.link>2026-02-19 15:07:01 +0300
commit4ef08d0bf6366b0bc8ccb6357b794937411f74cc (patch)
treed733ac655029b10e466c450be25c63e156f40773 /docs
parent598137ed132d95a3e3bf9b95e9e27286cc2186ac (diff)
downloadfparkan-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')
-rw-r--r--docs/specs/object-registry.md145
-rw-r--r--docs/specs/render.md13
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)