aboutsummaryrefslogtreecommitdiff
path: root/docs/reference
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-06-22 00:58:51 +0300
committerValentin Popov <valentin@popov.link>2026-06-22 00:58:51 +0300
commit78fc5f1debf1395d5df0bab7cc0dde54351205cb (patch)
treeef8f7c72a183723fcbea0b2d1fefd7c28ca7bc18 /docs/reference
parent50c2cf4686b53ebd2b76318223096660e92305a4 (diff)
downloadfparkan-78fc5f1debf1395d5df0bab7cc0dde54351205cb.tar.xz
fparkan-78fc5f1debf1395d5df0bab7cc0dde54351205cb.zip
docs: rewrite MkDocs documentation
Diffstat (limited to 'docs/reference')
-rw-r--r--docs/reference/materials.md69
-rw-r--r--docs/reference/msh.md82
-rw-r--r--docs/reference/nres.md61
-rw-r--r--docs/reference/render-frame.md56
-rw-r--r--docs/reference/rsli.md69
-rw-r--r--docs/reference/texm.md67
-rw-r--r--docs/reference/tma.md64
7 files changed, 468 insertions, 0 deletions
diff --git a/docs/reference/materials.md b/docs/reference/materials.md
new file mode 100644
index 0000000..8146a2c
--- /dev/null
+++ b/docs/reference/materials.md
@@ -0,0 +1,69 @@
+# WEAR и MAT0
+
+MSH batch хранит только `material_index`. WEAR переводит этот индекс в имя
+материала, а MAT0 по этому имени описывает phases, parameters и texture
+references.
+
+```text
+Batch20.material_index
+ -> WEAR row
+ -> MAT0 entry
+ -> active phase
+ -> textureName
+```
+
+## WEAR
+
+WEAR -- текстовый ресурс type ID `0x52414557`, обычно `*.wea` рядом с моделью.
+
+```text
+<wearCount>
+<legacyId> <materialName>
+...
+
+[empty line]
+[LIGHTMAPS
+<lightmapCount>
+<legacyId> <lightmapName>
+...]
+```
+
+`legacyId` сохраняется, но выбор выполняется по позиции строки и имени. Между
+основной таблицей и `LIGHTMAPS` нужен пустой разделитель.
+
+## MAT0
+
+MAT0 имеет type ID `0x3054414D`, обычно расположен в `Material.lib`. `attr1`
+содержит runtime flags, `attr2` -- версию payload.
+
+```c
+#pragma pack(push, 1)
+struct Mat0PrefixV4Plus {
+ uint16_t phase_count;
+ uint16_t animation_block_count;
+ uint8_t metadata_a;
+ uint8_t metadata_b;
+ uint32_t metadata_c_raw;
+ uint32_t metadata_d_raw;
+};
+
+struct Phase34 {
+ uint8_t parameters[18];
+ char texture_name[16];
+};
+#pragma pack(pop)
+```
+
+Versioned fields читаются только если версия их содержит. Для старых версий
+используются runtime defaults, а raw values сохраняются.
+
+## Fallback
+
+Material resolve:
+
+1. имя из WEAR;
+2. `DEFAULT`;
+3. entry с индексом 0.
+
+Пустое texture name означает намеренно нетекстурированную поверхность. Lightmap
+fallback отдельный: отсутствующий lightmap даёт slot `-1`.
diff --git a/docs/reference/msh.md b/docs/reference/msh.md
new file mode 100644
index 0000000..25aaad2
--- /dev/null
+++ b/docs/reference/msh.md
@@ -0,0 +1,82 @@
+# MSH
+
+Файл `*.msh` является NRes-контейнером. Geometry, узлы, slots, batches,
+animation и служебные streams лежат в entries с разными `type_id`.
+
+## Entry map
+
+```text
+type 1 nodes and slot selection
+type 2 header 0x8C + Slot68 records
+type 3 positions float3
+type 4 packed normals
+type 5 packed UV0
+type 6 index buffer u16
+type 7 triangle descriptors
+type 8 animation keys
+type 9 service stream
+type 10 strings and node names
+type 13 Batch20 records
+type 15 auxiliary stream
+type 17 auxiliary data
+type 18 rare stream
+type 19 animation frame map
+type 20 rare auxiliary table
+```
+
+Reader ищет entries по type, но сохраняет исходный порядок для roundtrip.
+
+## Node and slot selection
+
+Type 1 обычно состоит из records по 38 bytes:
+
+```c
+struct Node38 {
+ uint16_t hdr0;
+ uint16_t parent_or_link;
+ uint16_t anim_map_start;
+ uint16_t fallback_key;
+ uint16_t slot_index[15];
+};
+```
+
+`slot_index[lod * 5 + group]` выбирает geometry slot. `0xFFFF` означает
+отсутствие геометрии для комбинации LOD/group.
+
+## Slot and batch
+
+Type 2 содержит header `0x8C`, затем `Slot68`:
+
+```c
+struct Slot68 {
+ uint16_t tri_start;
+ uint16_t tri_count;
+ uint16_t batch_start;
+ uint16_t batch_count;
+ float aabb_min[3];
+ float aabb_max[3];
+ float sphere_center[3];
+ float sphere_radius;
+ uint32_t opaque[5];
+};
+```
+
+Type 13 задаёт draw ranges:
+
+```c
+#pragma pack(push, 1)
+struct Batch20 {
+ uint16_t batch_flags;
+ uint16_t material_index;
+ uint16_t opaque4;
+ uint16_t opaque6;
+ uint16_t index_count;
+ uint32_t index_start;
+ uint16_t opaque14;
+ uint32_t base_vertex;
+};
+#pragma pack(pop)
+```
+
+Index check выполняется как `base_vertex + index < vertex_count` для всего
+используемого slice.
diff --git a/docs/reference/nres.md b/docs/reference/nres.md
new file mode 100644
index 0000000..3b8384f
--- /dev/null
+++ b/docs/reference/nres.md
@@ -0,0 +1,61 @@
+# NRes
+
+`NRes` -- основной контейнер ресурсов Iron3D. Он используется как внешний
+архив и как внутренний контейнер модели `*.msh`.
+
+```text
+[Header: 16 bytes]
+[Data region: payload with alignment]
+[Directory: entry_count * 64 bytes]
+```
+
+## Header
+
+```c
+struct NResHeader16 {
+ char magic[4]; // "NRes"
+ uint32_t version; // 0x00000100
+ int32_t entry_count; // >= 0
+ uint32_t total_size; // equals file size
+};
+```
+
+`directory_offset = total_size - entry_count * 64`. Reader проверяет отсутствие
+переполнений, `directory_offset >= 16` и точное окончание каталога на
+`total_size`.
+
+## Entry
+
+```c
+#pragma pack(push, 1)
+struct NResEntry64 {
+ uint32_t type_id;
+ uint32_t attr1;
+ uint32_t attr2;
+ uint32_t size;
+ uint32_t attr3;
+ char name[36];
+ uint32_t data_offset;
+ uint32_t sort_index;
+};
+#pragma pack(pop)
+```
+
+Имя содержит bounded C-string до 35 полезных bytes. `sort_index` задаёт
+отображение из sorted position в original entry index. В строгом режиме все
+`sort_index` образуют перестановку `0..N-1`.
+
+## Data region
+
+Payload каждой записи лежит после header и до начала каталога. Игровые архивы
+выравнивают следующий payload до 8 bytes нулями, но reader не должен требовать
+плотного покрытия data region.
+
+Различаются:
+
+- active payload -- диапазон, на который указывает entry;
+- gap/padding -- bytes между активными диапазонами;
+- unindexed preserved region -- произвольные bytes, не принадлежащие entry.
+
+Lossless editor сохраняет все три категории. Compact writer может исключить
+unindexed regions только при явной операции repack.
diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md
new file mode 100644
index 0000000..6f0975c
--- /dev/null
+++ b/docs/reference/render-frame.md
@@ -0,0 +1,56 @@
+# Render frame
+
+Кадр является последней стадией цикла, а не самостоятельной функцией renderer-а.
+До draw calls уже накоплен input, рассчитан tick, применены отложенные операции,
+выбрана камера и обновлён 3D sound listener.
+
+## Frame skeleton
+
+```text
+system messages and input
+ -> simulation calculation
+ -> deferred object operations
+ -> animation and transforms
+ -> camera and sound listener
+ -> visibility and render queues
+ -> materials and draw passes
+ -> renderer completion
+ -> end-of-render callbacks and UI
+```
+
+В `World3D::stdRenderGame` доказан крупный порядок: camera передаётся Terrain,
+настраиваются viewport/matrices, вызываются renderer boundary slots,
+устанавливается `in_render`, выполняется traversal мира, закрывается world/shade
+pass, вызывается renderer completion, снимается `in_render`, рассылается
+end-of-render.
+
+## Draw item
+
+Подготовленный draw item содержит:
+
+- node world matrix;
+- batch flags and index range;
+- WEAR material handle;
+- MAT0 active phase and coefficients;
+- texture handle;
+- optional lightmap handle;
+- render phase and sorting key;
+- legacy pipeline state.
+
+Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase
+или texture cache посреди прохода не должно менять уже собранную очередь.
+
+## Parity risks
+
+- x87 precision and rounding;
+- scalar/SIMD `g_FastProc` differences;
+- object, batch and transparent primitive order;
+- depth, cull, alpha test and blend transitions;
+- mip-skip, palette and Page coordinates;
+- material fallback and phase selection;
+- RNG sequence for FX and atmosphere;
+- device capability fallback;
+- simulation time quantization.
+
+Для отладки нужен deterministic frame capture: camera state, visible object IDs,
+draw-item list, pipeline keys, matrices и hashes промежуточных buffers.
diff --git a/docs/reference/rsli.md b/docs/reference/rsli.md
new file mode 100644
index 0000000..a28aa1d
--- /dev/null
+++ b/docs/reference/rsli.md
@@ -0,0 +1,69 @@
+# RsLi
+
+`RsLi` -- библиотечный архив Iron3D с каталогом в начале файла и payloads после
+него.
+
+```text
+[Header: 32 bytes]
+[Entry table: entry_count * 32 bytes]
+[Payloads]
+[optional trailer]
+```
+
+## Header fields
+
+```text
++0x00 char[2] "NL"
++0x02 u8 reserved
++0x03 u8 version = 1
++0x04 i16 entry_count
++0x0E u16 presorted_flag = 0xABBA
++0x14 u32 xor_seed
+```
+
+Остальные bytes сохраняются без нормализации.
+
+## Entry
+
+```c
+struct RsLiEntry32 {
+ char name[12];
+ uint8_t service[4];
+ int16_t flags;
+ int16_t sort_to_original;
+ uint32_t unpacked_size;
+ uint32_t data_offset_raw;
+ uint32_t packed_size;
+};
+```
+
+Имя обычно хранится в uppercase ASCII. `sort_to_original` связывает sorted
+position с исходной записью.
+
+## Table transform
+
+Entry table проходит обратимое потоковое XOR-преобразование. Начальное
+состояние берётся из младших 16 bits `xor_seed` и продолжается через всю
+таблицу, не сбрасываясь на границе записи.
+
+## Storage methods
+
+```text
+0x000 raw block
+0x020 byte transform only
+0x040 LZSS
+0x060 transform + LZSS
+0x080 adaptive Huffman + LZSS
+0x0A0 transform + adaptive Huffman + LZSS
+0x100 raw Deflate
+```
+
+После любого пути должно получиться ровно `unpacked_size` bytes. Методы
+`0x080` и `0x0A0` подтверждены decoder-кодом, но не живыми payload демоверсии
+или обеих частей.
+
+## Compatibility quirk
+
+`sprites.lib::INTERF8.TEX` объявляет Deflate range на один byte дальше EOF.
+Совместимый reader допускает `packed_size - 1` только для этого именованного
+случая. Строгий режим сообщает `deflate_eof_plus_one`.
diff --git a/docs/reference/texm.md b/docs/reference/texm.md
new file mode 100644
index 0000000..db4321e
--- /dev/null
+++ b/docs/reference/texm.md
@@ -0,0 +1,67 @@
+# Texm
+
+`Texm` -- основной формат изображений Iron3D. Payload содержит header,
+необязательную палитру, mip chain и иногда `Page` chunk.
+
+```c
+struct TexmHeader32 {
+ uint32_t magic; // 'Texm'
+ uint32_t width;
+ uint32_t height;
+ uint32_t mip_count;
+ uint32_t flags4;
+ uint32_t flags5;
+ uint32_t unknown6;
+ uint32_t format;
+};
+```
+
+## Pixel formats
+
+```text
+0 Indexed8 + palette 256 * 4 bytes
+565 R5 G6 B5
+556 R5 G5 B6
+4444 A4 R4 G4 B4
+88 L8 A8
+888 RGB8 in four-byte element
+8888 A8 R8 G8 B8
+```
+
+Короткие каналы расширяются до 8 bits повторением значимых bits. Для 888
+служебный четвёртый byte сохраняется при roundtrip.
+
+## Layout
+
+```text
+TexmHeader32
+[palette 1024 bytes, only for format 0]
+level 0 pixels
+level 1 pixels
+...
+level mip_count-1 pixels
+[optional Page chunk]
+```
+
+Размер mip level вычисляется через `max(1, width >> i)` и
+`max(1, height >> i)`. Parser суммирует размеры с проверкой переполнения до
+чтения данных.
+
+## Page chunk
+
+```c
+struct PageHeader8 {
+ uint32_t magic; // 'Page'
+ uint32_t rect_count;
+};
+
+struct PageRect8 {
+ int16_t x;
+ int16_t width;
+ int16_t y;
+ int16_t height;
+};
+```
+
+Chunk обязан иметь размер `8 + rect_count * 8`. Rectangles находятся в pixel
+space базового mip и масштабируются после mip-skip.
diff --git a/docs/reference/tma.md b/docs/reference/tma.md
new file mode 100644
index 0000000..30ee495
--- /dev/null
+++ b/docs/reference/tma.md
@@ -0,0 +1,64 @@
+# TMA
+
+`data.tma` -- основное описание расстановки и логической конфигурации миссии.
+Файл перечисляет paths, clans, objects, свойства, ссылку на ландшафт и extras.
+
+## String primitive
+
+```c
+struct LpString {
+ uint32_t byte_length;
+ uint8_t bytes[byte_length];
+};
+```
+
+Reader продвигается ровно на `4 + byte_length`. Завершающий NUL не является
+обязательной частью framing. Для человекочитаемого вида используется legacy
+ANSI/CP1251 view, но исходные bytes сохраняются.
+
+## Top level
+
+```text
+u32 format_version
+u32 path_count
+PathRecord paths[path_count]
+u32 clan_section_version
+u32 clan_count
+ClanRecord clans[clan_count]
+u32 object_section_version
+u32 object_count
+PlacedObject objects[object_count]
+LpString land_path
+u32 mission_flag
+LpString description_raw
+u32 extra_section_version
+u32 extra_count
+ExtraRecord28 extras[extra_count]
+```
+
+Все 60 TMA Частей 1 и 2 проходят parser до точного EOF. Версии стабильны:
+верхний уровень `1`, clan section `6`, object section `10`, property schema
+`1`, trailing section `1`.
+
+## PlacedObject
+
+```text
+u32 raw_kind
+u32 class_or_flags
+LpString resource_name
+u32 raw_after_resource
+u32 identity_or_clan_raw
+f32 position[3]
+f32 orientation[3]
+f32 scale[3]
+LpString instance_name
+u32 raw_after_name
+i32 link0
+i32 link1
+u32 property_schema_version
+u32 property_count
+Property properties[property_count]
+```
+
+`Property` состоит из четырёх raw `u32` и имени. Typed views разрешены только
+для доказанных property names и consumers.