aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/object-registry.md
blob: 0e6e2dde3621d09a5b8d0cc6fc9d279a27afc19a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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-паритета.