Package Format
The on-disk layout as implemented. Little-endian unless the package is a byte-swapped console
build. Every optional field is gated on the package version p_ver against a named constant in
versions.rs.
Header
| Field | Type | Condition |
|---|---|---|
| signature | u32 |
must be 0x9E2A83C1 |
| package version | i16 |
|
| licensee version | i16 |
|
| header size | i32 |
|
| folder name | i32 len + bytes |
negative length ⇒ UTF-16, byte count is len × -2 |
| package flags | u32 |
|
| name count / offset | i32 ×2 |
|
| export count / offset | i32 ×2 |
|
| import count / offset | i32 ×2 |
|
| depends offset | i32 |
p_ver ≥ 415 |
| import/export GUID offset + counts | i32, u32, u32 |
p_ver ≥ 623 |
| thumbnail table offset | u32 |
p_ver ≥ 584 |
| GUID | i32 ×4 |
|
| generations | i32 count + {export, name, netobj} |
netobj only if p_ver ≥ 322 |
| engine version | i32 |
p_ver ≥ 245 |
| cooker version | i32 |
p_ver ≥ 277 |
| compression method + chunk count + chunks | u32, u32, chunk[] |
p_ver ≥ 334 |
| package source | i32 |
p_ver ≥ 334 |
| additional packages to cook | i32 + FString[] |
p_ver ≥ 516 |
| texture allocations | FTextureAllocations |
p_ver ≥ 767 |
A compressed chunk is four u32s — decompressed offset, decompressed size, compressed offset,
compressed size. FTextureAllocations is a count-prefixed array of {sizeX, sizeY, mips, format,
createFlags, exportIndices[]}.
The header can also be written, mirroring the same gates — used when flattening a compressed package into an uncompressed image.
Package flags
| Bit | Name | Bit | Name |
|---|---|---|---|
0x1 |
AllowDownload | 0x100000 |
DisallowLazyLoading |
0x2 |
ClientOptional | 0x200000 |
ContainsScript |
0x4 |
ServerSideOnly | 0x400000 |
ContainsDebugInfo |
0x8 |
Cooked | 0x800000 |
RequireImportsAlreadyLoaded |
0x10 |
Unsecure | 0x2000000 |
StoreCompressed |
0x20 |
SavedWithNewerVersion | 0x4000000 |
StoreFullyCompressed |
0x8000 |
Need | 0x10000000 |
ContainsFaceFxData |
0x20000 |
ContainsMap | 0x20000000 |
NoExportAllowed |
0x40000 |
Trash | 0x40000000 |
StrippedSource |
0x80000000 |
FilterEditorOnly |
Compression
Two levels of chunking.
Outer — the header's chunk table maps compressed regions into the decompressed image.
Inner — at each chunk's compressed offset:
u32 tag PACKAGE_FILE_TAG, or byte-swapped on console builds
u32 block size if it reads back as the tag itself, use the default 131072
u32 summary (unused)
u32 total size total uncompressed bytes in this chunk
then ceil(total / block) pairs of (compressed_size, decompressed_size), then the blocks back to
back. If the tag matched only after a swap, every subsequent size word is swapped too.
LZO only
Zlib and LZX are recognised as compression-method values but decompressing them is not
implemented. Run decompress on a package first if you want to check.
Name table
i32 length + characters + u64 flags. Positive length ⇒ ANSI, decoded as Latin-1 (one byte per
char, not UTF-8). Negative ⇒ UTF-16 with -length code units. Trailing NUL stripped.
An FName reference is two i32s: table index and instance number. Instance 0 prints bare;
instance n > 0 prints as Name_<n-1>.
Export table
| Field | Type | Condition |
|---|---|---|
| class index | i32 |
package index |
| super index | i32 |
|
| outer index | i32 |
|
| object name | FName |
|
| archetype | i32 |
|
| object flags | u64 |
|
| serial size | i32 |
|
| serial offset | i32 |
if size ≠ 0, or p_ver ≥ 249 |
| component map | i32 + (FName, i32)[] |
p_ver < 543 |
| export flags | u32 |
p_ver ≥ 247 |
| generation net object counts | i32 + i32[] |
p_ver ≥ 322 |
| package GUID | i32 ×4 |
p_ver ≥ 322 |
| package flags | u32 |
p_ver ≥ 543 |
Import table
Four fields, no version gates: class package FName, class name FName, outer index i32, object
name FName.
Package indices
One i32 addresses both tables, the engine's own convention:
| Value | Means |
|---|---|
n > 0 |
export n - 1 |
n < 0 |
import -n - 1 |
0 |
null / the package itself |
Path names
The outer chain is walked and joined with ., except that : is used when the object being
appended is not a Package and its outer is one — UE3's subobject notation.
Texture2D UI_Menu.Backgrounds.MainMenu
DistributionFloatUniform ParticleSystem.Emitter:Distribution_0
That maps to a filesystem path by replacing separators with / and appending the class as an
extension on the leaf: UI_Menu/Backgrounds/MainMenu.Texture2D.
Export payload
Each export's serial range holds, in order:
- a net index (
i32) ifp_ver ≥ 684 - the tagged property stream, terminated by a property named
None - the class's native serialization — whatever the C++
Serializeoverride wrote
No boundary marker
There is no length field between (2) and (3). The split is found by parsing (2) until it terminates; everything left is (3). This is why property decoding is written to fail safely — a mis-parse silently moves bytes from one side to the other rather than erroring.
Not parsed
The depends table, the import/export GUID table, and the thumbnail table are located by the header but never read. Their offsets survive a header rewrite unchanged.