Давным-давно, когда создавался формат ZIP и всем хватало 640KB памяти, никто не задумывался, что файл может быть >4GB и содержать в себе больше 65535 элементов, поэтому поддержка таких объемов прикручена костылями и называется ZIP64
. До сих пор этот формат плохо понимают Far Manager, 7-Zip и проводник седьмой винды (а говорят, что в Microsoft берут только гениальных программистов, возьмите меня, я умнее, чем выгляжу).
Расширение ZIP64
добавляет две основные фичи:
- структура
ZIP64 End Of Central Directory Record (EOCD64)
с 64-битными полями и ZIP64 End Of Central Directory Locator
для поиска структуры EOCD64
- если значения некоторых полей
Central Directory Header
и Local File Header
превышают максимальный, то эти значения записываются в Extra Field этих структур в специальном формате ZIP64 Extended Extra Field
А теперь подробнее про эти нововведения.
ZIP64 End Of Central Directory Locator
Используется для поиска структуры EOCD64
и находится сразу перед стандартным End Of Central Directory Record
.
EOCD64Locator
struct EOCD64Locator
{
// Обязательная сигнатура, равна 0x07064b50
uint32_t signature;
// Номер диска для поиска EOCD64
uint32_t diskNumber;
// Смещение от начала файла до EOCD64
uint64_t eocd64Offset;
// Количество дисков
uint32_t totalDiskCount;
}; |
struct EOCD64Locator
{
// Обязательная сигнатура, равна 0x07064b50
uint32_t signature;
// Номер диска для поиска EOCD64
uint32_t diskNumber;
// Смещение от начала файла до EOCD64
uint64_t eocd64Offset;
// Количество дисков
uint32_t totalDiskCount;
};
Определить, что наш ZIP файл содержит такую запись просто: проверьте поля стандартного
End of central directory record
. Если 16-битные поля равны
0xFFFF
или 32-битные —
0xFFFFFFFF
, значит такая запись должна быть.
ZIP64 End Of Central Directory Record
Эта структура представляет собой 64-битную версию стандартного End Of Central Directory Record
. Находится по адресу EOCD64Locator.eocd64Offset
.
EOCD64
struct EOCD64
{
// Обязательная сигнатура, равна 0x06064b50
uint32_t signature;
// Размер записи EOCD64
uint64_t eocd64Size;
// Версия для создания
uint16_t versionMadeBy;
// Версия для распаковки
uint16_t versionToExtract;
// Номер текущего диска
uint32_t diskNumber;
// Номер диска для поиска Central Directory
uint32_t startDiskNumber;
// Количество записей в Central Directory
uint64_t numberCentralDirectoryRecord;
// Всего записей в Central Directory
uint64_t totalCentralDirectoryRecord;
// Размер Central Directory
uint64_t sizeOfCentralDirectory;
// Смещение Central Directory
uint64_t centralDirectoryOffset;
// zip64 extensible data sector (переменной длины)
uint8_t *dataSector;
}; |
struct EOCD64
{
// Обязательная сигнатура, равна 0x06064b50
uint32_t signature;
// Размер записи EOCD64
uint64_t eocd64Size;
// Версия для создания
uint16_t versionMadeBy;
// Версия для распаковки
uint16_t versionToExtract;
// Номер текущего диска
uint32_t diskNumber;
// Номер диска для поиска Central Directory
uint32_t startDiskNumber;
// Количество записей в Central Directory
uint64_t numberCentralDirectoryRecord;
// Всего записей в Central Directory
uint64_t totalCentralDirectoryRecord;
// Размер Central Directory
uint64_t sizeOfCentralDirectory;
// Смещение Central Directory
uint64_t centralDirectoryOffset;
// zip64 extensible data sector (переменной длины)
uint8_t *dataSector;
};
Extra Field
Если вы внимательно читали мои предыдущие заметки на тему ZIP формата (описание формата ZIP, чтение ZIP файла, запись ZIP файла), то могли заметить, что у записей типа Local File Header
и Central directory file header
есть поля под названием extraField
.
В это поле записываются расширенные данные, о которых не было известно на момент создания первой версии формата ZIP. В этом поле могут содержаться разные типы данных и реализуется это последовательностью структур типа ExtraFieldRecord
:
ExtraFieldRecord
struct ExtraFieldRecord
{
// Заголовок (для ZIP64 extended information равен 0x0001)
uint16_t headerId;
// Размер данных
uint16_t dataSize;
// Какие-то данные размером dataSize
uint8_t *data;
}; |
struct ExtraFieldRecord
{
// Заголовок (для ZIP64 extended information равен 0x0001)
uint16_t headerId;
// Размер данных
uint16_t dataSize;
// Какие-то данные размером dataSize
uint8_t *data;
};
Данные
ExtraFieldRecord.data
для ZIP64 extended information выглядят так:
ZIP64ExtendInformation
struct ZIP64ExtendInformation
{
// Размер несжатых данных
uint64_t uncompressedSize;
// Размер сжатых данных
uint64_t compressedSize;
// Смещение Local File Header от начала файла
uint64_t localFileHeaderOffset;
// Номер диска для поиска
uint32_t diskNumber;
}; |
struct ZIP64ExtendInformation
{
// Размер несжатых данных
uint64_t uncompressedSize;
// Размер сжатых данных
uint64_t compressedSize;
// Смещение Local File Header от начала файла
uint64_t localFileHeaderOffset;
// Номер диска для поиска
uint32_t diskNumber;
};
Обратите внимание — в это записи важен лишь порядок полей. Все поля
необязательно будут присутствовать.
Например, структура Central directory file header
будет содержать 0xFFFFFFFF
только в поле localFileHeaderOffset
, значит в Extra Field будет лежать только значение localFileHeaderOffset
, а остальные поля будут отсутствовать.
Порядок действий
- определить наличие
EOCD64
проверкой полей EOCD
на переполнение и считать сначала EOCD64Locator
, затем EOCD64
- для каждой структуры типа
Central directory file header
и Local File Header
определить поля на переполнение и, если таковые присутствуют, то считать их значения из ExtraField
Вот, в принципе, и вся наука. Вы не подумайте, что я нашел эту информацию тщательным изучением ZIP файлов, просто читайте спецификацию ZIP формата (на английском).