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