diff options
Diffstat (limited to 'docs/specs/fxid.md')
| -rw-r--r-- | docs/specs/fxid.md | 202 |
1 files changed, 0 insertions, 202 deletions
diff --git a/docs/specs/fxid.md b/docs/specs/fxid.md deleted file mode 100644 index f723e17..0000000 --- a/docs/specs/fxid.md +++ /dev/null @@ -1,202 +0,0 @@ -# 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 новый рендер) на фиксированных сценах. |
