aboutsummaryrefslogtreecommitdiff
path: root/docs/specs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-10 00:58:16 +0300
committerValentin Popov <valentin@popov.link>2026-02-10 00:58:16 +0300
commitef9323772446e25a8d3dc5ee0e3954f921bb0074 (patch)
treec9d07c273662b7adf84a1f4bbfcfa9b2f5c3d99f /docs/specs
parent58a896221f896e6858cd6906410714b96318a6eb (diff)
downloadfparkan-ef9323772446e25a8d3dc5ee0e3954f921bb0074.tar.xz
fparkan-ef9323772446e25a8d3dc5ee0e3954f921bb0074.zip
Add .gitignore for Python and project-specific files; implement archive roundtrip validator
- Updated .gitignore to include common Python artifacts and project-specific files. - Added `archive_roundtrip_validator.py` script for validating NRes and RsLi formats against real game data. - Created README.md for the tools directory, detailing usage and supported signatures. - Enhanced nres.md with practical nuances and empirical checks for game data.
Diffstat (limited to 'docs/specs')
-rw-r--r--docs/specs/nres.md23
1 files changed, 18 insertions, 5 deletions
diff --git a/docs/specs/nres.md b/docs/specs/nres.md
index 15cff63..52f4f79 100644
--- a/docs/specs/nres.md
+++ b/docs/specs/nres.md
@@ -298,6 +298,8 @@ def decrypt_rs_entries(encrypted_data: bytes, seed: int) -> bytes:
`rsGetInfo` возвращает именно `unpacked_size` (то, сколько байт выдаст `rsLoad`).
+Практический нюанс для метода `0x100` (Deflate): в реальных игровых данных встречается запись, где `packed_size` указывает на диапазон до `EOF + 1`. Поток успешно декодируется и без последнего байта; это похоже на lookahead-поведение декодера.
+
## 2.7. Опциональный трейлер медиа (6 байт)
При открытии с флагом `a2 & 2`:
@@ -385,8 +387,8 @@ for i in range(N): # N = unpacked_size (для 0x20) или pack
Если бит = 0 (ссылка):
- Прочитать 2 байта: low_byte, high_byte
- - offset = low_byte | ((high_byte & 0x0F) << 8) // 12 бит
- - length = ((high_byte >> 4) & 0x0F) + 3 // 4 бита + 3
+ - offset = low_byte | ((high_byte & 0xF0) << 4) // 12 бит
+ - length = (high_byte & 0x0F) + 3 // 4 бита + 3
- Скопировать length байт из ring_buffer[offset...]:
для j от 0 до length-1:
byte = ring_buffer[(offset + j) & 0xFFF]
@@ -402,10 +404,10 @@ for i in range(N): # N = unpacked_size (для 0x20) или pack
```
Байт 0 (low): OOOOOOOO (биты [7:0] смещения)
-Байт 1 (high): LLLLOOOO L = длина − 3, O = биты [11:8] смещения
+Байт 1 (high): OOOOLLLL O = биты [11:8] смещения, L = длина − 3
-offset = low | ((high & 0x0F) << 8) // Диапазон: 0–4095
-length = (high >> 4) + 3 // Диапазон: 3–18
+offset = low | ((high & 0xF0) << 4) // Диапазон: 0–4095
+length = (high & 0x0F) + 3 // Диапазон: 3–18
```
## 3.3. LZSS с адаптивным кодированием Хаффмана (метод 0x80)
@@ -703,3 +705,14 @@ struct RsLibEntry { // 64 байта (16 DWORD)
- **Заголовок RsLi**: seed — **4 байта** (DWORD) по смещению 20, но используются только младшие 2 байта (`lo = byte[0]`, `hi = byte[1]`).
- **Запись RsLi**: sort_to_original[i] — **2 байта** (int16) по смещению 18 записи.
- **Данные при комбинированном XOR+LZSS**: seed — **4 байта** (DWORD) из поля по смещению 20 записи, но опять используются только 2 байта.
+
+## 6.7. Эмпирическая проверка на данных игры
+
+- Найдено архивов по сигнатуре: **122** (`NRes`: 120, `RsLi`: 2).
+- Выполнен полный roundtrip `unpack -> pack -> byte-compare`: **122/122** архивов совпали побайтно.
+- Для `RsLi` в проверенном наборе встретились методы: `0x040` и `0x100`.
+
+Подтверждённые нюансы:
+
+- Для LZSS (метод `0x040`) рабочая раскладка нибблов в ссылке: `OOOO LLLL`, а не `LLLL OOOO`.
+- Для Deflate (метод `0x100`) возможен случай `packed_size == фактический_конец + 1` на последней записи файла.