aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/fxid.md
blob: d4ff66d078d78bf310744e988fbc063d79b635fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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` в этом наборе не встретился, но поддерживается парсером.

---