aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/rsli.md
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-06-22 00:58:51 +0300
committerValentin Popov <valentin@popov.link>2026-06-22 00:58:51 +0300
commit78fc5f1debf1395d5df0bab7cc0dde54351205cb (patch)
treeef8f7c72a183723fcbea0b2d1fefd7c28ca7bc18 /docs/specs/rsli.md
parent50c2cf4686b53ebd2b76318223096660e92305a4 (diff)
downloadfparkan-78fc5f1debf1395d5df0bab7cc0dde54351205cb.tar.xz
fparkan-78fc5f1debf1395d5df0bab7cc0dde54351205cb.zip
docs: rewrite MkDocs documentation
Diffstat (limited to 'docs/specs/rsli.md')
-rw-r--r--docs/specs/rsli.md227
1 files changed, 0 insertions, 227 deletions
diff --git a/docs/specs/rsli.md b/docs/specs/rsli.md
deleted file mode 100644
index 239b3ff..0000000
--- a/docs/specs/rsli.md
+++ /dev/null
@@ -1,227 +0,0 @@
-# RsLi
-
-`RsLi` — библиотечный контейнер ресурсов движка Parkan: Iron Strategy с зашифрованной таблицей записей и несколькими методами упаковки данных.
-
-Страница описывает формат и runtime-контракт в высокоуровневом виде, без ссылок на внутренние адреса/функции дизассемблера.
-
-Связанная страница:
-
-- [NRes](nres.md)
-
-## 1. Общая структура файла
-
-```text
-[Header: 32]
-[Entry table: entry_count * 32, XOR-encrypted]
-[Packed payloads]
-[Optional trailer: "AO" + overlay:u32]
-```
-
-В отличие от `NRes`, таблица записей у `RsLi` расположена в начале файла.
-
-## 2. Заголовок (32 байта)
-
-Все значения little-endian.
-
-| Offset | Size | Type | Поле |
-|---:|---:|---|---|
-| 0 | 2 | char[2] | `NL` (магия) |
-| 2 | 1 | u8 | зарезервировано, в retail = `0` |
-| 3 | 1 | u8 | версия, в retail = `1` |
-| 4 | 2 | i16 | `entry_count` (должен быть `>= 0`) |
-| 14 | 2 | u16 | `presorted_flag` (`0xABBA` = таблица сортировки уже задана) |
-| 20 | 4 | u32 | `xor_seed` |
-
-Остальные байты заголовка считаются служебными и должны сохраняться без нормализации.
-
-## 3. Таблица записей (после дешифровки)
-
-Таблица начинается с `offset = 32`, размер `entry_count * 32`.
-
-Каждая запись (32 байта):
-
-| Offset | Size | Type | Поле |
-|---:|---:|---|---|
-| 0 | 12 | char[12] | `name_raw` (обычно uppercase ASCII, NUL optional) |
-| 12 | 4 | bytes | служебный хвост, сохранять как есть |
-| 16 | 2 | i16 | `flags` |
-| 18 | 2 | i16 | `sort_to_original` |
-| 20 | 4 | u32 | `unpacked_size` |
-| 24 | 4 | u32 | `data_offset_raw` |
-| 28 | 4 | u32 | `packed_size` |
-
-### 3.1. Метод упаковки
-
-`method = flags & 0x1E0`
-
-Поддерживаемые значения:
-
-| Маска | Метод |
-|---:|---|
-| `0x000` | без сжатия |
-| `0x020` | XOR only |
-| `0x040` | LZSS |
-| `0x060` | XOR + LZSS |
-| `0x080` | LZSS + адаптивный Huffman |
-| `0x0A0` | XOR + LZSS + адаптивный Huffman |
-| `0x100` | raw Deflate (RFC1951) |
-
-Другие значения считаются неподдерживаемыми.
-
-## 4. XOR-дешифрование таблицы и данных
-
-Для таблицы и XOR-методов payload используется один и тот же потоковый XOR-алгоритм.
-
-Ключ:
-
-- `key16 = xor_seed & 0xFFFF` (используются только младшие 16 бит seed).
-
-Состояние:
-
-```text
-lo = key16 & 0xFF
-hi = key16 >> 8
-```
-
-Для каждого байта:
-
-```text
-lo = hi XOR ((lo << 1) mod 256)
-out = in XOR lo
-hi = lo XOR (hi >> 1)
-```
-
-## 5. `sort_to_original` и поиск по имени
-
-### 5.1. Режим `presorted_flag == 0xABBA`
-
-`sort_to_original` обязан быть перестановкой `0..entry_count-1` без дубликатов.
-
-### 5.2. Режим без presorted-флага
-
-Слой загрузки строит `sort_to_original` самостоятельно:
-
-- сортирует индексы по `strcmp`-порядку имен (байтовое сравнение);
-- записывает эту перестановку в lookup-таблицу.
-
-### 5.3. Поиск
-
-Поиск выполняется бинарным поиском по lookup-таблице:
-
-1. запрос переводится в uppercase ASCII;
-2. на шаге бинарного поиска используется индекс `sort_to_original[mid]`;
-3. сравнение имен — bytewise (`strcmp`-логика).
-
-Fail-safe:
-
-- при невалидном индексе lookup-таблицы выполняется линейный fallback.
-
-## 6. AO-трейлер и media overlay
-
-Опциональный трейлер в конце файла:
-
-```text
-"AO" + overlay:u32
-```
-
-Если трейлер присутствует:
-
-- эффективный offset payload: `effective_offset = data_offset_raw + overlay`.
-
-Ограничение:
-
-- `overlay <= file_size`.
-
-## 7. Декодирование payload по методам
-
-## 7.1. Без сжатия (`0x000`)
-
-Берутся первые `unpacked_size` байт из packed-диапазона.
-
-## 7.2. XOR only (`0x020`)
-
-XOR-дешифрование первых `unpacked_size` байт.
-
-## 7.3. LZSS (`0x040`, `0x060`)
-
-Параметры:
-
-- ring buffer: `4096` байт;
-- начальное заполнение ring: `0x20`;
-- стартовый указатель ring: `0xFEE`;
-- control-биты читаются LSB-first.
-
-Правила:
-
-- `bit=1`: literal byte;
-- `bit=0`: ссылка из 2 байт
- `offset = low | ((high & 0xF0) << 4)`
- `length = (high & 0x0F) + 3`.
-
-Для `0x060` XOR применяется на лету к packed-потоку до LZSS-декодирования.
-
-## 7.4. LZSS + адаптивный Huffman (`0x080`, `0x0A0`)
-
-Параметры:
-
-- `N=4096`, `F=60`, `THRESHOLD=2`;
-- адаптивное дерево Huffman обновляется по мере декодирования.
-
-Для `0x0A0` XOR применяется на лету к битовому потоку до Huffman/LZSS-декодирования.
-
-## 7.5. Deflate (`0x100`)
-
-Используется raw Deflate-поток (RFC1951).
-
-Важно:
-
-- zlib-обертка (`RFC1950`) не принимается.
-
-## 8. Quirk: Deflate EOF+1
-
-На retail-корпусе встречается один подтвержденный случай, где:
-
-- `effective_offset + packed_size == file_size + 1`.
-
-Совместимое поведение:
-
-- для метода `0x100` допустить чтение `packed_size - 1` байт (если включен режим совместимости);
-- в строгом режиме считать это ошибкой.
-
-## 9. Контрольные инварианты
-
-Минимальные проверки:
-
-1. `magic == "NL"`, `reserved == 0`, `version == 1`.
-2. `entry_count >= 0`.
-3. `table_end <= file_size`.
-4. Если `presorted_flag == 0xABBA`, `sort_to_original` — валидная перестановка.
-5. `effective_offset + packed_size` не выходит за EOF (кроме разрешенного deflate EOF+1 quirk).
-6. Итоговый распакованный размер равен `unpacked_size`.
-
-## 10. Эмпирическая проверка на retail-корпусе
-
-Проверка на полном наборе `testdata/Parkan - Iron Strategy`:
-
-- обнаружено `2` архива `RsLi`;
-- roundtrip `unpack -> repack -> byte-compare`: `2/2` совпали побайтно;
-- подтвержден ровно один `deflate EOF+1` случай (`sprites.lib`, entry `23`).
-
-Проверено legacy-валидатором архивов и тестами `crates/rsli`.
-
-## 11. Статус покрытия и что осталось до 100%
-
-Закрыто:
-
-- формат заголовка/таблицы;
-- XOR-алгоритм;
-- все используемые методы декодирования;
-- AO overlay;
-- lookup-поиск и fallback;
-- retail-валидация и побайтовый roundtrip.
-
-Осталось до полного 100% архитектурного покрытия движка:
-
-1. Полная функциональная семантика битов `flags` вне маски метода (`0x1E0`) для геймплейных подсистем.
-2. Канонический writer для авторинга новых архивов со стабильной стратегией выбора методов (`0x080/0x0A0/0x100`) и параметров компрессии.
-3. Формализация поведения для не-ASCII имен (на практике архивы используют ASCII-диапазон).