SerializedFile containers
A Unity player keeps its scenes and assets in SerializedFiles: one container format for globalgamemanagers,
globalgamemanagers.assets, resources.assets, every levelN (a scene) and every sharedassetsN.assets (the assets
scenes share). A file is a header, a block of metadata (which types occur, where each object’s bytes are, which other
files it points into) and the objects’ bytes. Bulk data (texture pixels, vertex streams, audio) is not inside: objects
name a range of a .resS or .resource file beside it. Proven · SkateBIRD
Three container versions occur in the games here. Enter the Gungeon (Unity 2017.4) writes version 17; SkateBIRD (Unity 2019.4) writes version 21; Risk of Rain 2 (2021.3), Schedule I (2022.3), Sledding Game, PEAK and Big Walk (Unity 6000.3) write version 22. Proven · every serialized file of each install The same containers sit inside a UnityFS bundle when a build packs its data into one; Big Walk does.
Builds measured
Section titled “Builds measured”| Game | Container | Unity version in the file | Files | Embedded type trees | Confidence |
|---|---|---|---|---|---|
| Enter the Gungeon | 17 | 2017.4.27f1 |
5 | none | Proven |
| Enter the Gungeon’s bundles | 17 | 2017.4.27f1 |
34 (inside 30 bundles) | every type | Proven |
| SkateBIRD | 21 | 2019.4.20f1 |
31 | none | Proven |
| Risk of Rain 2 | 22 | 2021.3.45f2 |
5 | none | Proven |
| Schedule I | 22 | 2022.3.62f2 |
9 | none | Proven |
| Sledding Game | 22 | 6000.3.12f1 |
7 | none | Proven |
| PEAK 3.0.0 | 22 | 6000.3.15f1 |
13 | none | Proven |
| Big Walk | 22 | 6000.3.17f1 |
12 (inside data.unity3d) |
none | Proven |
| Sledding Game’s Addressables | 22 | 6000.3.12f1 |
50 (inside 50 bundles) | every type | Proven |
| PEAK 3.0.0’s level bundles | 22 | 6000.3.15f1 |
417 (inside 8 bundles) | every type of 416 files, none in the manifest bundle maps |
Proven |
“Proven” here is a census of every serialized file of the install: each header parses, the stated file size equals the file’s size, the data offset is a multiple of 16 and lies after the metadata, the target platform is 19 (Windows x64), and the has-type-tree byte is 0. Player builds strip type trees from their files; the layout of each class is supplied from outside (type trees). The files in bundles are the other case: the same census of the 479 serialized files of the three games’ bundles (a second reader agrees on every header, type, object table and external) finds the has-type-tree byte 1 in 466 of them, where each type is followed by its tree.
Header
Section titled “Header”The header’s first fields are big-endian whatever the file’s endianness byte says; the metadata after it is read in the file’s own endianness (little in every file measured). Proven
All fields big-endian. The metadata starts at byte 20 and ends metadata size bytes later; object bytes start at the data offset.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | u32 | metadata size | Proven | bytes of metadata after the header; 1,340 in SkateBIRD's globalgamemanagers |
| 0x4 | 4 | u32 | file size | Proven | equals the file's length in every file |
| 0x8 | 4 | u32 = 21 | version | Proven | |
| 0xC | 4 | u32 | data offset | Proven | where the objects' bytes begin; a multiple of 16 in every file (4,096 in globalgamemanagers) |
| 0x10 | 1 | u8 | endianness | Proven | 0 little-endian in every file |
| 0x11 | 3 | u8[3] | reserved | Proven | zero |
Version 22 keeps the version-21 field positions for the version but zeroes the old size fields and repeats them wider after the endianness bytes: sizes and the data offset become 32-, 64- and 64-bit numbers. Proven · Sledding, PEAK, Big Walk
All fields big-endian. The metadata starts at byte 48.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | u32 = 0 | legacy metadata size | Proven | unused in version 22 |
| 0x4 | 4 | u32 = 0 | legacy file size | Proven | unused in version 22 |
| 0x8 | 4 | u32 = 22 | version | Proven | |
| 0xC | 4 | u32 = 0 | legacy data offset | Proven | unused in version 22 |
| 0x10 | 1 | u8 | endianness | Proven | 0 little-endian in every file |
| 0x11 | 3 | u8[3] | reserved | Proven | zero |
| 0x14 | 4 | u32 | metadata size | Proven | |
| 0x18 | 8 | u64 | file size | Proven | equals the file's length in every file |
| 0x20 | 8 | u64 | data offset | Proven | a multiple of 16 in every file |
| 0x28 | 8 | u64 = 0 | reserved | Proven | zero in every file |
Metadata
Section titled “Metadata”The metadata follows the header in this order. Counts are i32. Proven · SkateBIRD The reader that parses this
order consumes the metadata of every SkateBIRD file to its stated end, and its object tables match an independent reader’s
on all 503,045 objects of the 31 files (path ids, class ids, byte offsets and sizes). Proven · SkateBIRD
| Part | Content | Confidence |
|---|---|---|
| Unity version | NUL-terminated string (2019.4.20f1) |
Proven |
| Target platform | i32; 19 in every file |
Proven |
| Has type trees | u8; 0 in every file (stripped) |
Proven |
| Types | count, then one type entry each | Proven |
| Objects | count, then one object entry each | Proven |
| Script types | count, then per entry a file index (i32), 4-byte alignment, a path id (i64) |
Proven · SkateBIRD |
| External files | count, then per entry an empty string, a 16-byte GUID, a kind (i32) and a path string |
Proven · SkateBIRD |
| Reference types | count, then type entries with a script id when the script index is not negative; with stored trees each is followed by its tree and three strings | Proven · PEAK, Sledding, Big Walk |
| User information | NUL-terminated string | Proven · SkateBIRD |
Type entry
Section titled “Type entry”23 bytes for a class other than MonoBehaviour (114), 39 for MonoBehaviour. In the metadata endianness (little).
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | i32 | class id | Proven | Unity's class id (1 GameObject, 4 Transform, 21 Material, 28 Texture2D, 43 Mesh, 114 MonoBehaviour, ...) |
| 0x4 | 1 | u8 | stripped | Proven | |
| 0x5 | 2 | i16 | script type index | Proven | -1 for a class other than MonoBehaviour |
| 0x7 | 16 | u8[16] | script id | Proven | only present when the class id is 114 (MonoBehaviour): the hash of the script's identity |
| 7, or 23 with a script id | 16 | u8[16] | old type hash | Sourced | UnityPy's name; the hash of the class's layout |
Type entry with a stored tree
Section titled “Type entry with a stored tree”When the has-type-tree byte is 1, each type entry (and each reference type entry) continues after its old type hash with the type’s tree, then, for an ordinary type, a list of type indices, and for a reference type three strings. Proven · PEAK, Sledding The metadata of all 466 files that store trees is consumed exactly to its stated end by this layout, and an independent reader gives the same tree for every one of the 83,867 types. Proven · PEAK, Sledding
In the metadata endianness (little). Nodes are in depth-first order; a node's parent is the nearest earlier node whose level is one less.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | i32 | node count | Proven | at least 1 |
| 0x4 | 4 | i32 | string buffer size | Proven | |
| 0x8 | 32 per node | node[] | nodes | Proven | see the node layout |
| after the nodes | string buffer size | char[] | string buffer | Proven | NUL-terminated strings |
| after the buffer | variable | i32 count + i32[] | type dependencies | Proven | ordinary types: indices into the type table (empty for most) |
| after the buffer | variable | char[] x3 | class, namespace and assembly | Proven | reference types instead of the dependency list: three NUL-terminated strings |
In the metadata endianness (little).
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 2 | i16 | version | Proven | the node's version, as in a package's tree |
| 0x2 | 1 | u8 | level | Proven | 0 for the root, a child's level is its parent's plus one |
| 0x3 | 1 | u8 | type flags | Observed | 0, or 1 on Array and TypelessData nodes, 2 on managedReference nodes, 4 on ManagedReferencesRegistry, 8 on the element type of an array of managed references: values 0, 1, 2, 4 and 8 in 180,511 nodes of 13 files |
| 0x4 | 4 | u32 | type string offset | Proven | top bit clear: offset into the string buffer; top bit set: the low 31 bits are an offset into the engine's common string table |
| 0x8 | 4 | u32 | name string offset | Proven | as the type string offset |
| 0xC | 4 | i32 | byte size | Proven | -1 where the size varies |
| 0x10 | 4 | i32 | index | Observed | the node's position in the list, in all 180,511 nodes of 13 files |
| 0x14 | 4 | u32 | meta flags | Proven | 0x4000 aligns the position to 4 bytes after the node |
| 0x18 | 8 | u64 | reference type hash | Observed | 0 in all 180,511 nodes of 13 files |
Object entry
Section titled “Object entry”Each entry starts on a 4-byte boundary of the file. The object’s bytes are size bytes at data offset + start; its class
is that of its type entry. Path ids are unique within a file. Proven
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | i64 | path id | Proven | the object's identity within this file |
| 0x8 | 4 | u32 | start | Proven | offset from the data offset |
| 0xC | 4 | u32 | size | Proven | |
| 0x10 | 4 | u32 | type index | Proven | index into the type table |
As version 21 but the start is 64 bits wide.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | i64 | path id | Proven | |
| 0x8 | 8 | i64 | start | Proven | |
| 0x10 | 4 | u32 | size | Proven | |
| 0x14 | 4 | u32 | type index | Proven |
References between objects
Section titled “References between objects”An object points at another with a PPtr: a file id and a path id. File id 0 is the same file; file id n is external
file n − 1 of the metadata. Sourced · UnityPy A script field that stores an object reference writes it as 12 bytes: an i32 file id and
an i64 path id, a path id of 0 meaning null. Observed · SkateBIRD, Sledding
Scenes link the shared libraries through their externals. SkateBIRD’s level12 lists globalgamemanagers.assets,
sharedassets0 to sharedassets5, sharedassets7, sharedassets12, resources.assets and Library/unity default resources; level13 adds sharedassets13. Observed · SkateBIRD A scene file holds no Texture2D, Material,
Shader or AudioClip object in any of SkateBIRD’s 14 scenes; its renderers reference the sharedassets files.
Observed · SkateBIRD
Managed references
Section titled “Managed references”A field marked [SerializeReference] stores a reference to a plain C# object, not a Unity object. An object that has such
fields ends with a ManagedReferencesRegistry named references: an int version and a vector RefIds of
ReferencedObjects, each an SInt64 rid, a ReferencedManagedType type (three strings: class, ns, asm) and a
ReferencedObjectData data. The fields that hold a reference store its rid. The data is laid out by the tree of the class
type names, which the file keeps in its reference types (matched by class, namespace and assembly), not in
the object’s own tree; a null reference has an empty class and no data. Sourced · UnityPy All 1,427 objects of Sledding
Game’s 50 bundles decode by that rule, each to exactly its last byte, the 94 MonoBehaviours with [SerializeReference]
fields among them. Proven · Sledding A referenced object’s own tree names a registry of its own, which is not stored:
those objects decode to their last byte with it left out. Proven · Sledding
Container versions and Unity versions
Section titled “Container versions and Unity versions”A reader accepts a container version it was checked against and refuses others: versions 17, 21 and 22 are the ones verified against real data here. Observed A version 21 file is not a version 22 file read with other widths; field offsets and the object table differ, so one reader needs both layouts. Proven
Version 17 has the version 21 header and object entry. Its metadata differs in three ways: a stored tree node is 24 bytes (no reference type hash, at 24 in later versions), there is no reference types table, and a type has no dependency list. Proven · Gungeon Enter the Gungeon’s 5 loose files read so with the object table an independent reader gives (4,285 objects), and every one of the 93,034 objects of its 30 bundles’ 34 files decodes by its stored tree to exactly its last byte. Proven · Gungeon The reference type hash arrives in version 19, the reference types table in version 20 and the dependency lists (and a reference type’s class names) in version 21. Sourced · UnityPy, AssetStudio
No game documented here writes 18, 19 or 20, so where their fields begin rests on two readers’ sources. Unity’s serialized container has older versions too (older players), and newer editors may raise it. What would settle 18 to 20: one player of Unity 2018.x or 2019.1 to 2019.2 read with an independent reader.