The PAK format is the primary storage method for all Divinity assets. The below article contains a technical breakdown of the format. Those more inclined to just extracting it can look here.
Overview[]
The PAK format appears to be encoded in little-endian format. A PAK archive consists of one or more archive files: a primary file, e.g. Main.pak, and perhaps a number of secondary files, e.g. Main_1.pak.
Manifest[]
The manifest only appears in the primary archive file. It consists of a header and a file table.
Header[]
The header is 0x15 (21) bytes long.
Offset | Type | Size (bytes) | Field |
---|---|---|---|
0x00 | uint32 | 4 | Format version number |
0x04 | uint32 | 4 | Offset of the start of the data section in bytes. This should be a multiple of 0x8000 (32 KiB). |
0x08 | uint32 | 4 | Number of archive files, including the primary. |
0x0C | uint32 | 4 | Length of the file table in bytes. This does not include the length of the header. |
0x10 | bool | 1 | Archive endianness; 0 is big-endian, 1 is little-endian. |
0x11 | uint32 | 4 | Number of files in the archive. |
File table[]
Following the header is the file table as a sequence of file records. Each file record is 0x110 (272) bytes long.
Offset | Type | Size (bytes) | Field |
---|---|---|---|
0x000 | char* | 256 | Relative file path. |
0x100 | uint32 | 4 | Offset of the file within the archive file in bytes, not including the archive header if the file is in the primary archive file. This should be a multiple of 0x8000 (32 KiB). |
0x104 | uint32 | 4 | File size in bytes. |
0x108 | uint32 | 4 | Compressed file size. If the value is zero, the file is not compressed. If the value is nonzero, the file is zlib-compressed. |
0x10C | uint32 | 4 | Index of the archive file the file is located in. 0 is the primary archive file. |
The remaining length between the end of the manifest and the start of the data section is padded with 0xAD bytes. (This is how the official packer does it, however the padding bytes don't actually matter, they can be anything.)
Data section[]
In the primary archive file, the data section follows the header. In the secondary archive files, the data section is the entire archive file. Files are laid out in the data section according to their record in the file table. Any space between the end of a file and the start of the next file is padded with 0xAD bytes. The end of the data section is also padded with 0xAD bytes to fill a block of 0x8000 bytes (32 KiB).