aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/fxid.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/specs/fxid.md')
-rw-r--r--docs/specs/fxid.md112
1 files changed, 112 insertions, 0 deletions
diff --git a/docs/specs/fxid.md b/docs/specs/fxid.md
new file mode 100644
index 0000000..d4ff66d
--- /dev/null
+++ b/docs/specs/fxid.md
@@ -0,0 +1,112 @@
+# FXID
+
+Документ описывает контейнер ресурса эффекта и формат команд эффекта.
+
+---
+
+## 3.2. Контейнер ресурса эффекта
+
+Эффекты в игровых архивах хранятся как NRes‑entries типа:
+
+- `0x44495846` (`"FXID"`).
+
+Парсер эффекта находится в `Effect.dll!sub_10007650`.
+
+## 3.3. Формат payload эффекта
+
+### 3.3.1. Header (первые 60 байт)
+
+```c
+struct FxHeader60 {
+ uint32_t cmdCount; // +0x00
+ uint32_t globalFlags; // +0x04
+ float durationSec; // +0x08 (дальше умножается на 1000.0)
+ uint32_t unk0C; // +0x0C
+ uint32_t flags10; // +0x10 (используются биты 0x40 и 0x400)
+ uint8_t reserved[0x2C];// +0x14..+0x3B
+};
+```
+
+Поток команд начинается строго с `offset 0x3C`.
+
+### 3.3.2. Командный поток
+
+Каждая команда начинается с `uint32 cmdWord`, где:
+
+- `opcode = cmdWord & 0xFF`;
+- `enabled = (cmdWord >> 8) & 1` (копируется в `obj+4`).
+
+Размер команды зависит от opcode и прибавляется в **байтах** (`add edi, ...` в ASM):
+
+| Opcode | Размер записи |
+|--------|---------------|
+| 1 | 224 |
+| 2 | 148 |
+| 3 | 200 |
+| 4 | 204 |
+| 5 | 112 |
+| 6 | 4 |
+| 7 | 208 |
+| 8 | 248 |
+| 9 | 208 |
+| 10 | 208 |
+
+Никакого межкомандного выравнивания нет: следующая команда сразу после `size(opcode)`.
+
+## 3.4. Runtime-классы команд (vtable mapping)
+
+В `sub_10007650` для каждого opcode создаётся объект конкретного типа:
+
+- `op1` → `off_1001E78C`
+- `op2` → `off_1001F048`
+- `op3` → `off_1001E770`
+- `op4` → `off_1001E754`
+- `op5` → `off_1001E360`
+- `op6` → `off_1001E738`
+- `op7` → `off_1001E228`
+- `op8` → `off_1001E71C`
+- `op9` → `off_1001E700`
+- `op10` → `off_1001E24C`
+
+`flags10 & 0x400` включает глобальный runtime-флаг менеджера эффекта (`manager+0xA0`).
+
+## 3.5. Алгоритм загрузки эффекта (1:1)
+
+```c
+read header60
+ptr = data + 0x3C
+for i in 0..cmdCount-1:
+ op = ptr[0] & 0xFF
+ obj = new CommandClass(op)
+ obj->enabled = (ptr[0] >> 8) & 1
+ obj->raw = ptr
+ manager.attach(obj)
+ ptr += sizeByOpcode(op)
+```
+
+Ошибка формата:
+
+- неизвестный opcode;
+- выход за пределы буфера до обработки `cmdCount`;
+- непустой «хвост» после `cmdCount` команд (для строгого валидатора).
+
+## 3.6. Проверка на реальных данных
+
+Для `testdata/nres/effects.rlb` (923 entries):
+
+- `opcode` всегда в диапазоне `1..10`;
+- stream полностью покрывает payload без хвоста;
+- частоты opcode:
+ - `1: 618`
+ - `2: 517`
+ - `3: 1545`
+ - `4: 202`
+ - `5: 31`
+ - `7: 1161`
+ - `8: 237`
+ - `9: 266`
+ - `10: 160`
+ - `6` в этом наборе не встретился, но поддерживается парсером.
+
+---
+