Skip to content

IoStore containers (.utoc and .ucas) and pak files

Unreal EngineArchives and filesApplies toMECCHAPBZ

Unreal Engine 5 packs cooked content into IoStore containers: a .ucas file holds the data in compressed blocks and a .utoc file is its table of contents (which chunks exist, where, how they are compressed). The older .pak format still ships beside them. MECCHA CHAMELEON (Steam app 4704690, Unreal Engine 5.6.1, executable PenguinHotel, project Chameleon) has two IoStore containers and one pak in Chameleon/Content/Paks/. Sourced · Unreal Engine source Phantom Blade Zero’s containers, which are encrypted, differ as described in its section.

File Size (bytes) Holds Confidence
Chameleon-Windows.utoc / .ucas 2,526,778 / 3,105,798,464 the game’s cooked content Proven · MECCHA
global.utoc / .ucas 770 / 3,135,872 the engine’s script objects (global data) Proven · MECCHA
Chameleon-Windows.pak 88,565,611 non-IoStore files Observed · MECCHA

The header is 144 bytes, little-endian. Proven · MECCHA

FIoStoreTocHeader144 bytesMECCHA100% known

Little-endian. Values are those of Chameleon-Windows.utoc; global.utoc differs where noted.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x016char[16] = -==--==--==--==-magicProven
0x101u8 = 8versionProvenEIoStoreTocVersion 8 (ReplaceIoChunkHashWithIoHash) in both containers
0x111u8reservedProvenzero
0x122u16reservedProvenzero
0x144u32 = 144header sizeProven
0x184u32entry countProvenchunks: 12,407 (global.utoc: 1)
0x1C4u32compressed block countProven124,297 (global: 48)
0x204u32 = 12compressed block entry sizeProven
0x244u32compression method countProven1 (global: 0)
0x284u32 = 32compression method name lengthProven
0x2C4u32 = 65536compression block sizeProven
0x304u32directory index sizeProven439,500 (global: 0, no directory index)
0x344u32 = 1partition countProven
0x388u64container idProven0x97ACDB3EDF1CD386 (global: 0xFFFFFFFFFFFFFFFF)
0x4016u8[16]encryption key guidProvenall zero in both containers
0x501u8container flagsProven0x09 (global: 0x00), see below
0x511u8reservedProvenzero
0x522u16reservedProvenzero
0x544u32perfect hash seed countProven6,204 (global: 1)
0x588u64 = 0xFFFFFFFFFFFFFFFFpartition sizeProvenno size limit per partition
0x604u32chunks without perfect hash countProven0
0x644u32reservedProvenzero
0x6840u64[5]reservedProvenall zero

The container flags are a bit set: 1 compressed, 2 encrypted, 4 signed, 8 indexed (has a directory index). Sourced · Unreal Engine source The game’s container has 0x09 (compressed and indexed): it is neither encrypted nor signed. Proven · MECCHA global.utoc has none. Proven · MECCHA

In this order, with no gaps; their sizes add up to the file’s length exactly, so the layout holds for both containers. Proven · MECCHA

Array Element size Count Content Confidence
chunk ids 12 entry count FIoChunkId: an 8-byte id, a 2-byte index, a padding byte, and a chunk type byte at offset 11 Sourced · Unreal Engine source
offsets and lengths 10 entry count each chunk’s offset and length in the uncompressed address space: two 5-byte big-endian integers Proven · MECCHA
perfect hash seeds 4 seed count seeds of the minimal perfect hash that maps chunk ids to entries Sourced · Unreal Engine source
chunks without perfect hash 4 count (0) overflow entries Sourced · Unreal Engine source
compressed blocks 12 block count a 5-byte offset into the .ucas, a 3-byte compressed size, a 3-byte uncompressed size, a 1-byte compression method index (little-endian) Proven · MECCHA
compression method names 32 method count NUL-padded names: Oodle Proven · MECCHA
directory index directory index size one the file tree: a mount point string, directory and file entries Proven · MECCHA
chunk meta 24 entry count per chunk a 20-byte hash (FIoHash), flags and padding Sourced · Unreal Engine source

The directory index’s mount point is ../../../. Proven · MECCHA The 297,768 bytes after the directory index are exactly 12,407 chunk meta entries of 24 bytes. Proven · MECCHA

The type byte at offset 11 of the chunk ids takes only the values below in the two containers. Proven · MECCHA Chunk types seen (names from the engine’s EIoChunkType5): Sourced · Unreal Engine source

Type Name Chameleon-Windows global Confidence
1 ExportBundleData 6,549 Proven · MECCHA
2 BulkData 3,099 Proven · MECCHA
3 OptionalBulkData 1 Proven · MECCHA
5 ScriptObjects 1 Proven · MECCHA
6 ContainerHeader 1 Proven · MECCHA
8 ShaderCodeLibrary 4 Proven · MECCHA
9 ShaderCode 2,753 Proven · MECCHA

The 6,549 export bundle chunks equal the 6,549 .uexp and the 3,099 bulk chunks the 3,099 .ubulk entries of the build’s file manifest. Observed · MECCHA The chunks’ lengths add up to 7,533,837,449 bytes uncompressed (the largest 201,327,113); global’s one chunk is 3,135,860 bytes. Proven · MECCHA

The uncompressed address space is cut into 65,536-byte blocks. The game’s container has 124,297 blocks, 117,169 compressed with Oodle (method index 1) and 7,128 stored (index 0); global’s 48 blocks are all stored. Every block starts at a multiple of 16 in the .ucas. Proven · MECCHA 12,396 blocks have an uncompressed size other than 65,536. Proven · MECCHA

Chameleon-Windows.pak is a version 11 pak: the footer’s magic 0x5A6F12E1 is 204 bytes from the end of the file, the index is at offset 88,226,758 and is 74,330 bytes. Observed · MECCHA The shipped game lists 20,835 files in a build manifest (Manifest_UFSFiles_Win64.txt: 6,549 uexp, 6,527 uasset, 3,438 res, 3,099 ubulk, 415 png, 313 svg, 175 uplugin, 105 ini, 90 locres, 25 ufont) and 24 more outside the file system (Manifest_NonUFSFiles_Win64.txt: the .uproject, config files, plugin descriptors and the engine’s native libraries). Observed · MECCHA

What the pak holds

The pak is 88 MB; the cooked assets are in the IoStore container. Which of the manifest’s files (the .res and .png files, the config) the pak carries is not established.

Phantom Blade Zero keeps its content in ProjectPBZ/Content/Paks/: the IoStore container ProjectPBZ-Windows, the global container global, the pak ProjectPBZ-Windows.pak and nine movie paks pakchunkMovies*-Windows.pak. Observed · PBZ

Its game container’s TOC has the same version and header as above, with these values. The header and the arrays after it add up to the 47,488,040 bytes of the file exactly. Proven · PBZ

Header field ProjectPBZ-Windows.utoc Confidence
version 8 Proven · PBZ
container flags 0x0b: compressed, encrypted and indexed, not signed Proven · PBZ
entry count (chunks) 328,220 Proven · PBZ
compressed block count 1,360,326 Proven · PBZ
compression block size 262,144 Proven · PBZ
compression method Oodle Proven · PBZ
directory index size 15,409,392 Proven · PBZ
partition count 66 Proven · PBZ
partition size 2 GiB Proven · PBZ
encryption key guid all zero Proven · PBZ

The 66 partitions are the data files ProjectPBZ-Windows.ucas and ProjectPBZ-Windows_s1.ucas to _s65.ucas. Observed · PBZ The zero key GUID selects the game’s one default key. Sourced · Unreal Engine source global.utoc is not encrypted. Observed · PBZ

The pak files are version 12, with version 11’s footer layout (221 bytes), an encrypted index, the zero key GUID and Oodle compression. Observed · PBZ Version 12 adds a UTF-8 path directory (Utf8PakDirectory); CUE4Parse reads it as an Unreal Engine 5.7 pak. Sourced · CUE4Parse Each movie pak comes with an empty IoStore pair: no files, mount point /. Observed · PBZ

What the encrypted containers hold

Without the key, nothing past the headers and footers (the directory index, the packages, the pak indexes) is described here.