- Обновлены спецификации `runtime-pipeline`, `sound`, `terrain-map-loading`, `texture`, `ui` и `wear`. - Добавлены разделы о статусе покрытия и оставшихся задачах для достижения 100% завершенности. - Внесены уточнения по архитектурным ролям, минимальным контрактам и требованиям к toolchain для каждой подсистемы. - Уточнены форматы данных и правила взаимодействия между компонентами системы.
203 lines
8.0 KiB
Markdown
203 lines
8.0 KiB
Markdown
# FXID
|
||
|
||
`FXID` — бинарный формат эффекта в движке Parkan: Iron Strategy.
|
||
Эта страница задаёт контракт формата и исполнения на уровне, достаточном для 1:1 порта рендера/симуляции эффектов и для lossless-инструментов.
|
||
|
||
Связанные контейнеры: [NRes](nres.md), [RsLi](rsli.md).
|
||
|
||
## 1. Контейнер
|
||
|
||
- Тип ресурса в `NRes`: `0x44495846` (`FXID`).
|
||
- Значения `attr1/attr2/attr3` в типовых игровых данных стабильны, но при редактуре их нужно сохранять как есть.
|
||
|
||
## 2. Бинарный формат
|
||
|
||
Все значения little-endian.
|
||
|
||
### 2.1. Заголовок (60 байт)
|
||
|
||
```c
|
||
struct FxHeader60 {
|
||
uint32_t cmd_count; // 0x00
|
||
uint32_t time_mode; // 0x04
|
||
float duration_sec; // 0x08
|
||
float phase_jitter; // 0x0C
|
||
uint32_t flags; // 0x10
|
||
uint32_t settings_id; // 0x14
|
||
float rand_shift_x; // 0x18
|
||
float rand_shift_y; // 0x1C
|
||
float rand_shift_z; // 0x20
|
||
float pivot_x; // 0x24
|
||
float pivot_y; // 0x28
|
||
float pivot_z; // 0x2C
|
||
float scale_x; // 0x30
|
||
float scale_y; // 0x34
|
||
float scale_z; // 0x38
|
||
};
|
||
```
|
||
|
||
Поток команд начинается строго с `offset = 0x3C`.
|
||
|
||
### 2.2. Команда
|
||
|
||
Каждая команда:
|
||
|
||
1. `uint32 cmd_word`
|
||
2. body фиксированного размера, зависящего от `opcode`
|
||
|
||
Поля `cmd_word`:
|
||
|
||
- `opcode = cmd_word & 0xFF`
|
||
- `enabled = (cmd_word >> 8) & 1`
|
||
- `bits 9..31` нужно сохранять 1:1
|
||
|
||
Выравнивания между командами нет.
|
||
|
||
### 2.3. Размеры команд
|
||
|
||
| Opcode | Размер |
|
||
|---:|---:|
|
||
| 1 | 224 |
|
||
| 2 | 148 |
|
||
| 3 | 200 |
|
||
| 4 | 204 |
|
||
| 5 | 112 |
|
||
| 6 | 4 |
|
||
| 7 | 208 |
|
||
| 8 | 248 |
|
||
| 9 | 208 |
|
||
| 10 | 208 |
|
||
|
||
## 3. Смысл заголовка
|
||
|
||
- `cmd_count`: число команд в потоке.
|
||
- `time_mode`: способ вычисления текущего коэффициента эффекта.
|
||
- `duration_sec`: длительность (в рантайме переводится в миллисекунды).
|
||
- `phase_jitter`: амплитуда случайного фазового сдвига.
|
||
- `flags`: флаги поведения (видимость, альфа-модификаторы, режимы гейтинга).
|
||
- `settings_id`: индекс профиля/настроек эффекта.
|
||
- `rand_shift_*`: случайный пространственный сдвиг.
|
||
- `pivot_*`: локальная опора.
|
||
- `scale_*`: базовый масштаб инстанса эффекта.
|
||
|
||
## 4. Флаги заголовка
|
||
|
||
Практически важные биты:
|
||
|
||
- `0x0001`: случайный сдвиг фазы
|
||
- `0x0008`: случайный пространственный сдвиг (`rand_shift_*`)
|
||
- `0x0010`: ветки видимости/окклюзии
|
||
- `0x0020`: треугольный ремап альфы
|
||
- `0x0040`: инверсия исходного active-state
|
||
- `0x0080`, `0x0100`: фильтрация по времени суток
|
||
- `0x0200`: умножение альфы на нормализованное время жизни
|
||
- `0x0400`, `0x1000`: дополнительные биты состояния менеджера эффекта
|
||
- `0x0800`: дополнительный гейтинг
|
||
|
||
Неизвестные биты должны сохраняться без изменений.
|
||
|
||
## 5. `time_mode` (0..17)
|
||
|
||
База:
|
||
|
||
- `tn = (now - start) / (end - start)`
|
||
- `prev = предыдущая вычисленная альфа`
|
||
|
||
Поддерживаемые семейства режимов:
|
||
|
||
- константный режим;
|
||
- линейный (`tn`), обратный (`1-tn`), циклический (`fract(tn)`);
|
||
- режимы от внешних параметров мира/очереди;
|
||
- режимы на основе норм векторов состояния;
|
||
- режимы с ограничением вниз/вверх относительно `prev`.
|
||
|
||
После вычисления:
|
||
|
||
- при `flags & 0x0200` применяется `alpha *= tn`;
|
||
- при `flags & 0x0020` применяется triangular remap.
|
||
|
||
## 6. Resource-ссылки внутри команд
|
||
|
||
Для opcode `2/3/4/5/7/8/9/10` используется ссылка:
|
||
|
||
```c
|
||
struct ResourceRef64 {
|
||
char archive[32];
|
||
char name[32];
|
||
};
|
||
```
|
||
|
||
Контракт:
|
||
|
||
- строки ASCII, нуль-терминированные;
|
||
- сравнение имён регистронезависимое;
|
||
- обычно:
|
||
- `opcode 2`: `sounds.lib` + `*.wav`
|
||
- остальные: `material.lib` + имя материала/эффекта.
|
||
|
||
## 7. Runtime-контракт исполнения
|
||
|
||
На создании инстанса:
|
||
|
||
1. Заголовок копируется в runtime-состояние.
|
||
2. Вычисляется `end_time`.
|
||
3. Для каждой команды создаётся runtime-объект по `opcode`.
|
||
4. В объект копируется `enabled`.
|
||
5. Объект инициализируется контекстом эффекта.
|
||
|
||
На каждом кадре:
|
||
|
||
1. Вычисляется текущий коэффициент/альфа по `time_mode` и `flags`.
|
||
2. Выполняется update каждой команды.
|
||
3. Выполняется emit/render часть активных команд.
|
||
4. Применяются события Start/Stop/Restart.
|
||
|
||
## 8. Строгий парсер (рекомендуемый)
|
||
|
||
1. Проверить `len(payload) >= 60`.
|
||
2. Прочитать `cmd_count`.
|
||
3. Идти от `ptr = 0x3C`.
|
||
4. Для каждой команды:
|
||
- проверить `ptr + 4 <= len`;
|
||
- прочитать `opcode`;
|
||
- проверить, что `opcode` поддержан;
|
||
- проверить `ptr + size(opcode) <= len`;
|
||
- сдвинуть `ptr += size(opcode)`.
|
||
5. Проверить `ptr == len(payload)`.
|
||
|
||
## 9. Writer и редактор
|
||
|
||
Для lossless-совместимости:
|
||
|
||
- сохранять все неизвестные поля/биты;
|
||
- не менять фиксированные размеры команд;
|
||
- не добавлять padding;
|
||
- пересчитывать только `cmd_count` и размеры контейнера;
|
||
- сохранять порядок команд.
|
||
|
||
## 10. Что требуется для 1:1 переноса
|
||
|
||
1. Полная поддержка opcode `1..10`.
|
||
2. Точный контракт вычисления `time_mode` и `flags`.
|
||
3. Точное поведение `ResourceRef64`.
|
||
4. Повторяемый RNG и одинаковая политика плавающей точки.
|
||
|
||
## 11. Статус валидации
|
||
|
||
- Формальные инварианты FXID зафиксированы в `tools/msh_doc_validator.py` и `tools/fxid_abs100_audit.py`.
|
||
- На полном retail-корпусе `testdata/Parkan - Iron Strategy` проверено `923/923` FXID payload без ошибок.
|
||
|
||
## 12. Статус покрытия и что осталось до 100%
|
||
|
||
Закрыто:
|
||
|
||
1. Контейнер FXID, fixed-size командный поток, opcode-покрытие `1..10`.
|
||
2. Базовый runtime-контур исполнения эффекта.
|
||
3. Корпусная валидация формата на retail-данных.
|
||
|
||
Осталось:
|
||
|
||
1. Полная field-level семантика payload каждого opcode для авторинга новых эффектов «с нуля».
|
||
2. Формальная спецификация всех `time_mode` веток на уровне точных числовых формул и edge-case поведения.
|
||
3. Полный набор пиксельных parity-тестов FX (оригинал vs новый рендер) на фиксированных сценах.
|