Files
fparkan/docs/specs/object-registry.md
Valentin Popov 4ef08d0bf6 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.
2026-02-19 16:07:01 +04:00

6.6 KiB
Raw Blame History

Object Registry (objects.rlb)

objects.rlb - это не архив с готовыми мешами.
Это реестр игровых прототипов, который связывает логический идентификатор объекта (r_h_01, s_tree_04, fr_m_brige, ...) с набором реальных ресурсов в других архивах.

Документ описывает формат и runtime-контракт на высоком уровне, без привязки к внутренним именам/адресам из дизассемблера.

Связанные страницы:

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 состоит из массива фиксированных записей:

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-паритета.