Metroid Prime 3: Corruption/PAK

Материал из ConsolWiki
Перейти к: навигация, поиск

PAK представляет собой контейнер (архив) для хранения файлов с возможностью использования сжатия. В качестве алгоритма сжатия используется LZO.
Каждый блок данных в файле выровнен по размеру «слова» 64 байта.

Структура файла

Размер (без учёта выравнивания) Данные
24 байта Заголовок
4 байта + количество секций × размер записи таблицы секций Таблица секций
Суммарный размер секций Секции

Секции

В архиве имеется несколько типов секций:

Имя Назначение Описание
STRG Секция имён В ней описаны имена, которые задаются некоторым файлам. Может быть пустой, но всегда присутствует.
RSHD Секция файлов В ней хранится вся информация о файлах в архиве.
DATA Секция данных В ней хранятся непосредственно данные файлов.

Перед началом таблицы секции указано количество записей в ней. В игре всегда используется три секции.
Запись таблицы секций выглядит так:

Смещение Размер Описание
0x00 4 байта Имя секции (четыре символа)
0x04 4 байта Размер секции
Показать код на языке: C++, Delphi

Секции следуют друг за другом, их размеры округляются до 64-х байт из-за выравнивания. Смещение секции можно вычислить по сумме размеров заголовка, таблицы секций и предыдущих секций с учётом выравнивания.

Секция STRG

В начале секции находится 4 байта, в которых хранится количество заданных имён. Далее следует соответствующее количество записей вида:

Размер Описание
До нуль-терминатора (включительно) Имя файла
4 байта Тип файла, 4 символа [1]
8 байтов Хеш имени файла
Показать код на языке: C++ (псевдокод)
  1. Удобно использовать в качестве префикса или расширения имени файла.

Имена подавляющего большинства файлов в архивах не указаны, вместо этого для обращения к ним используется хеш имени. Хеш-функция не известна.

Секция RSHD

В начале секции находится 4 байта, в которых хранится количество файлов в контейнере. Далее следует соответствующее количество записей вида:

Смещение Размер Описание
0x00 4 байта Флаг компрессии [1]
0x04 4 байта Тип файла, аналогичен типам файлов в секции STRG
0x08 8 байов Хеш имени файла [2]
0x10 4 байта Размер файла [3]
0x14 4 байта Смещение файла в секции данных
Показать код на языке: C++, Delphi
  1. Если true (не равен нулю) - то сжатие используется, в противном случае - нет.
  2. Можно рассматривать ещё и как ключ для связи с записями в секции имён.
  3. Указан размер данных в том виде, в котором они хранятся в контейнере. Если файл сжат, то размер исходных данных указан в заголовке сжатых данных.

Секция DATA

В секции DATA хранятся непосредственно данные файлов в запакованном или исходном виде, в зависимости от того, указана ли компрессия в записи файла. Как и все данные, данные файлов выравниваются по размеру 64 байта.

Сжатые файлы

Заголовок сжатого файла имеет следующий вид:

Смещение Размер Описание
0x00 4 байта Сигнатура "CMPD" (вероятно, от слова "compressed")
0x04 4 байта Тип архива
Показать код на языке: C++

Заголовок потока сжатых данных выглядит таким образом:

Смещение Размер Описание
0x00 1 байт Флаги
0x01 3 байта Размер потока сжатых данных
0x04 4 байта Размер файла
Показать код на языке: C++

Сами сжатые файлы встречаются двух типов, при чём второй в игре используется только для текстур. Если в качестве типа архива указан 1, то используется первый, если 2 - то второй.

Тип 1

Структура сжатого файла первого типа выглядит так:

Размер Описание
8 байтов Заголовок
8 байтов Заголовок потока сжатых данных
Указан в заголовке Поток сжатых данных
Показать код на языке: C++ (псевдокод)

Тип 2

Второй тип сжатых файлов позволяет хранить некоторое количество данных из начала файла в несжатом виде. Таким образом можно, например, считывать заголовки файлов не прибегая к распаковке.
Структура сжатого файла второго типа выглядит так:

Размер Описание
8 байтов Заголовок
4 байта Неизвестно (всегда 12) [1]
4 байта Неизвестно (всегда 12) [2]
Встречался только 12 байтов [3] Несжатые данные из начала файла
8 байтов Заголовок потока сжатых данных
Указан в заголовке Поток сжатых данных
Показать код на языке: C++ (псевдокод)
  1. Исходя из того, что первые 12 байтов не запакованы, то можно предположить, что это как раз и есть размер этих данных.
  2. Скорей всего, это смещение в файле, с которого начинается сжатие данных. Как ещё один вариант - это отступ до заголовка потока запакованных данных.
  3. Если предыдущие предположения верны - то он указан в одном из предыдущих неизвестных значений.

Флаги

Бит Маска Назначение Описание
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);
}