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` в этом наборе не встретился, но поддерживается парсером.
---
|