Metroid Prime 3: Corruption / PAK
Материал из ConsolWiki
PAK представляет собой контейнер (архив) для хранения файлов с возможностью использования сжатия. В качестве алгоритма сжатия используется LZO.
Каждый блок данных в файле выровнен по размеру «слова» 64 байта.
Содержание |
Структура файла
| Размер (без учёта выравнивания) | Данные |
|---|---|
| 24 байта | Заголовок |
| 4 байта + количество секций × размер записи таблицы секций | Таблица секций |
| Суммарный размер секций | Секции |
Секции
В архиве имеется несколько типов секций:
| Имя | Назначение | Описание |
|---|---|---|
| STRG | Секция имён | В ней описаны имена, которые задаются некоторым файлам. Может быть пустой, но всегда присутствует. |
| RSHD | Секция файлов | В ней хранится вся информация о файлах в архиве. |
| DATA | Секция данных | В ней хранятся непосредственно данные файлов. |
Перед началом таблицы секции указано количество записей в ней. В игре всегда используется три секции.
Запись таблицы секций выглядит так:
| Смещение | Размер | Описание |
|---|---|---|
| 0x00 | 4 байта | Имя секции (четыре символа) |
| 0x04 | 4 байта | Размер секции |
Секции следуют друг за другом, их размеры округляются до 64-х байт из-за выравнивания. Смещение секции можно вычислить по сумме размеров заголовка, таблицы секций и предыдущих секций с учётом выравнивания.
Секция STRG
В начале секции находится 4 байта, в которых хранится количество заданных имён. Далее следует соответствующее количество записей вида:
| Размер | Описание |
|---|---|
| До нуль-терминатора (включительно) | Имя файла |
| 4 байта | Тип файла, 4 символа [1] |
| 8 байтов | Хеш имени файла |
- ↑ Удобно использовать в качестве префикса или расширения имени файла.
Имена подавляющего большинства файлов в архивах не указаны, вместо этого для обращения к ним используется хеш имени. Хеш-функция не известна.
Секция RSHD
В начале секции находится 4 байта, в которых хранится количество файлов в контейнере. Далее следует соответствующее количество записей вида:
| Смещение | Размер | Описание |
|---|---|---|
| 0x00 | 4 байта | Флаг компрессии [1] |
| 0x04 | 4 байта | Тип файла, аналогичен типам файлов в секции STRG |
| 0x08 | 8 байов | Хеш имени файла [2] |
| 0x10 | 4 байта | Размер файла [3] |
| 0x14 | 4 байта | Смещение файла в секции данных |
- ↑ Если true (не равен нулю) - то сжатие используется, в противном случае - нет.
- ↑ Можно рассматривать ещё и как ключ для связи с записями в секции имён.
- ↑ Указан размер данных в том виде, в котором они хранятся в контейнере. Если файл сжат, то размер исходных данных указан в заголовке сжатых данных.
Секция DATA
В секции DATA хранятся непосредственно данные файлов в запакованном или исходном виде, в зависимости от того, указана ли компрессия в записи файла. Как и все данные, данные файлов выравниваются по размеру 64 байта.
Сжатые файлы
Заголовок сжатого файла имеет следующий вид:
| Смещение | Размер | Описание |
|---|---|---|
| 0x00 | 4 байта | Сигнатура "CMPD" (вероятно, от слова "compressed") |
| 0x04 | 4 байта | Тип архива |
Заголовок потока сжатых данных выглядит таким образом:
| Смещение | Размер | Описание |
|---|---|---|
| 0x00 | 1 байт | Флаги |
| 0x01 | 3 байта | Размер потока сжатых данных |
| 0x04 | 4 байта | Размер файла |
Сами сжатые файлы встречаются двух типов, при чём второй в игре используется только для текстур. Если в качестве типа архива указан 1, то используется первый, если 2 - то второй.
Тип 1
Структура сжатого файла первого типа выглядит так:
| Размер | Описание |
|---|---|
| 8 байтов | Заголовок |
| 8 байтов | Заголовок потока сжатых данных |
| Указан в заголовке | Поток сжатых данных |
Тип 2
Второй тип сжатых файлов позволяет хранить некоторое количество данных из начала файла в несжатом виде. Таким образом можно, например, считывать заголовки файлов не прибегая к распаковке.
Структура сжатого файла второго типа выглядит так:
| Размер | Описание |
|---|---|
| 8 байтов | Заголовок |
| 4 байта | Неизвестно (всегда 12) [1] |
| 4 байта | Неизвестно (всегда 12) [2] |
| Встречался только 12 байтов [3] | Несжатые данные из начала файла |
| 8 байтов | Заголовок потока сжатых данных |
| Указан в заголовке | Поток сжатых данных |
- ↑ Исходя из того, что первые 12 байтов не запакованы, то можно предположить, что это как раз и есть размер этих данных.
- ↑ Скорей всего, это смещение в файле, с которого начинается сжатие данных. Как ещё один вариант - это отступ до заголовка потока запакованных данных.
- ↑ Если предыдущие предположения верны - то он указан в одном из предыдущих неизвестных значений.
Флаги
| Бит | Маска | Назначение | Описание |
|---|---|---|---|
| 0 | 0x80 | Флаг сжатия | Если не установлен, то при распаковке поток сжатых данных напрямую копируется в выходной поток. |
| 1 | 0x40 | Неизвестно | - |
| 2 | 0x20 | Неизвестно | - |
| 3 | 0x10 | Неизвестно | - |
| 4 | 0x08 | Неизвестно | - |
| 5 | 0x04 | Неизвестно | - |
| 6 | 0x02 | Неизвестно | - |
| 7 | 0x01 | Неизвестно | - |
Поток сжатых данных
Поток сжатых данных состоит из множества блоков вида:
| Размер | Описание |
|---|---|
| 2 байта | Размер порции |
| Указан в начале | Порция сжатых LZO данных |
Структуру можно изобразить так:
| Блок данных 1 |
| Блок данных 2 |
| ... |
| Блок данных N |
Блоки данных кончаются тогда, когда суммарный размер содержащихся в них сжатых данных дойдёт до размера, указанного в заголовке потока сжатых данных. Как вариант - когда размер распакованных данных дойдёт до указанного в заголовке размера, но первый вариант предпочтительней и безопасней.
Пример кода распаковки потока сжатых данных на C++:
while(stream_size > 0) { in->read(&chunk_size, 2); stream_size -= 2; in->read(buf, chunk_size); stream_size -= chunk_size; lzo_decompress(in_buf, chunk_size, out_buf, &size); out->write(out_buf, size); }