diff options
| author | Valentin Popov <valentin@popov.link> | 2026-02-12 00:12:05 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-02-12 00:12:05 +0300 |
| commit | 041b1a6cb3159463fe81f4b2d18cb968d6f3fd87 (patch) | |
| tree | f7190906dbd235d8d97c0ac664c40014005d7b90 /docs/specs/fxid.md | |
| parent | 5035d022206bf9ace54a43b4d65abe0b9fc0f361 (diff) | |
| download | fparkan-041b1a6cb3159463fe81f4b2d18cb968d6f3fd87.tar.xz fparkan-041b1a6cb3159463fe81f4b2d18cb968d6f3fd87.zip | |
Добавлены спецификации для сетевой подсистемы, системы звука, загрузки ландшафта, интерфейса пользователя и пайплайна выполнения. Обновлен файл навигации mkdocs.yml для включения новых документов.
Diffstat (limited to 'docs/specs/fxid.md')
| -rw-r--r-- | docs/specs/fxid.md | 112 |
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` в этом наборе не встретился, но поддерживается парсером. + +--- + |
