UnityFS bundles
A UnityFS file is Unity’s archive: a header, a compressed table of blocks and files, and the files’ bytes in
compressed blocks. AssetBundles and Addressables content are UnityFS files, and so is a player’s data.unity3d when a
build packs its data into one archive. Four ways occur among the games here. Observed
| Game | What is in UnityFS | Where | Confidence |
|---|---|---|---|
| SkateBIRD | nothing: no bundle, no Addressables, loose serialized files only | SkateBIRD_Data |
Observed · SkateBIRD |
| Sledding Game | Addressables content (localization tables, one asset group, a scripts group); the scenes are loose files | Sledding Game_Data/StreamingAssets/aa/ |
Observed · Sledding |
| PEAK 3.0.0 | 208 level scenes in seven AssetBundles; the rest is loose files | PEAK_Data/StreamingAssets/maps/ |
Observed · PEAK |
| PEAK 2.6.b | the player itself in one data.unity3d |
PEAK_Data/ |
Observed · PEAK |
| Big Walk | the player itself in one data.unity3d; no Addressables |
Big Walk_Data/ |
Observed · Big Walk |
Header
Section titled “Header”Every number in the header and the block table is big-endian. Proven · Big Walk, PEAK, Sledding
Big-endian. After the header the reader aligns to 16 bytes when the format version is 7 or later.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | char[] = UnityFS\0 | signature | Proven | |
| 0x8 | 4 | u32 = 8 | format version | Proven | 6 and 7 are also documented by UnityPy |
| 0xC | string | char[] | player version | Proven | NUL-terminated; 5.x.x in every bundle measured |
| after the player version | string | char[] | engine version | Proven | NUL-terminated; the Unity version of the build (6000.3.17f1 in Big Walk's bundle) |
| after the engine version | 8 | u64 | bundle size | Proven | equals the file's length |
| +8 | 4 | u32 | compressed block-table size | Proven | |
| +12 | 4 | u32 | uncompressed block-table size | Proven | |
| +16 | 4 | u32 | flags | Proven | 0x243 in every bundle measured |
The flags, as UnityPy documents them for the Unity versions that matter here: the low six bits name the compression of
the block table (0 none, 1 LZMA, 2 LZ4, 3 LZ4HC, 4 LZHAM); 0x40 says the directory is stored with the block table;
0x80 puts the block table at the end of the file instead of after the header; 0x200 pads to 16 bytes before the
data. Sourced · UnityPy 0x243 is LZ4HC with the directory combined and the data padded.
All 59 bundles of the three games carry exactly 0x243: PEAK’s seven level bundles and its maps manifest bundle, Sledding
Game’s 50 Addressables bundles and Big Walk’s data.unity3d. None sets 0x80, none compresses its block table another way
than LZ4HC (the other compression ids, LZMA and LZHAM, do not occur), each states a size equal to its file’s length, and each
has format version 8 and player version 5.x.x. Proven · Big Walk, PEAK, Sledding
Block table and directory
Section titled “Block table and directory”The block table is decompressed first. It holds the data’s blocks and then the files in them. Sourced · UnityPy
Big-endian.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 16 | u8[16] | hash | Unknown | UnityPy reads and ignores it |
| 0x10 | 4 | i32 | block count | Proven | |
| 0x14 | 10 per block | block[] | blocks | Proven | u32 uncompressed size, u32 compressed size, u16 flags (low six bits: the block's compression) |
| after the blocks | 4 | i32 | file count | Proven | |
| after the count | variable | file[] | files | Proven | i64 offset, i64 size, u32 flags, NUL-terminated path; offsets are into the decompressed data |
The block table of every bundle measured is consumed exactly (no bytes left over), and its blocks decompress to as many bytes as the files’ sizes add up to. Proven · Big Walk, PEAK, Sledding · Big Walk’s bundle, PEAK’s eight bundles, Sledding’s 50 The block sizes add up to the end of the last file, and the file’s length is the start of the data plus the blocks’ compressed sizes, in all 59. Proven · Big Walk, PEAK, Sledding
| Property | Value | Confidence |
|---|---|---|
| Block uncompressed size | at most 131,072 bytes (128 KiB) | Proven · Big Walk, PEAK, Sledding |
| Block compression | LZ4HC (flag 0x3), or stored (flag 0x0) |
Proven · Big Walk, PEAK, Sledding |
| Blocks by compression | LZ4HC 328,093 and stored 13,934: PEAK 283,256 and 13,475, Sledding Game 1,698 and none, Big Walk 43,139 and 459 | Proven · Big Walk, PEAK, Sledding |
| Node flags 4 | a serialized file (PEAK 417, Sledding Game 50, Big Walk 12) | Proven · Big Walk, PEAK, Sledding |
| Node flags 0 | a stream file (.resS, .resource) (PEAK 24, Sledding Game 16, Big Walk 7) |
Proven · Big Walk, PEAK, Sledding |
A block is an LZ4 block of plain literal runs and matches. A reader decompresses blocks in order and concatenates them, so the file at an offset is found by walking the cumulative uncompressed sizes. Sourced · UnityPy Two block readers written from this description, one of them reading only the blocks a byte range needs, give the same SHA-256 for each of the 526 nodes of the 59 bundles (PEAK’s 441, Sledding Game’s 66, Big Walk’s 19: 38.9 GB, 0.2 GB and 5.7 GB of decompressed bytes). Proven · Big Walk, PEAK, Sledding
Nodes and the files they point at
Section titled “Nodes and the files they point at”A node’s path is the file’s name inside the bundle. PEAK’s level bundles name them BuildPlayer-Level_<N> (a scene),
BuildPlayer-Level_<N>.sharedAssets and, for 24 of them, BuildPlayer-Level_<N>.resource (a stream); its manifest bundle
maps and Sledding Game’s Addressables bundles name a serialized file CAB-<32 hex digits> and a stream
CAB-<32 hex digits>.resS; Big Walk’s bundle names the player’s own files (level2, sharedassets1.assets,
Resources/unity_builtin_extra). Proven · PEAK, Sledding, Big Walk Node paths are unique within a bundle, and every
node lies inside the blocks’ decompressed bytes. Proven · PEAK, Sledding, Big Walk
A serialized file in a bundle points at other files by their external paths (SerializedFile externals). Three forms occur:
| External path | Names | Where it occurs |
|---|---|---|
archive:/<root>/<node> |
the node <node>, in this bundle or another; <root> is the first node’s path of the bundle that holds it, without its extension (CAB-… names are whole) |
1,392 externals of PEAK’s and Sledding Game’s serialized files: 1,311 name a node of their own bundle (PEAK), 81 a node of another bundle (Sledding Game); every one names a node of the installed bundles |
Library/unity default resources, Resources/unity_builtin_extra |
the engine’s built-in resources, which are not nodes of PEAK’s bundles | 416 and 208 externals of PEAK’s files (every level file names the first, every shared-assets file the second; the files are the loose PEAK_Data/Resources/unity default resources and unity_builtin_extra); 11 and 5 of Big Walk’s, whose bundle holds Resources/unity_builtin_extra as a node |
globalgamemanagers.assets, resources.assets, sharedassets<N>.assets |
the player’s files by their plain names | 35 externals of Big Walk’s twelve files, each naming a node of the same bundle |
Proven · PEAK, Sledding, Big Walk
PEAK shares assets across levels inside a bundle: every level bundle has one sharedAssets file of about a gigabyte
(1.04 to 1.11 GB) that 63 of the bundle’s 64 serialized files point at (31 of 32 in the last bundle), and several smaller
shared-asset files that other levels point at. A level therefore needs, beside its own scene and shared assets, the shared
assets of other levels. Proven · PEAK Sledding Game’s 50 bundles point at one another the same way: its 81
archive:/ externals all name nodes of other bundles. Proven · Sledding
A streamed resource (StreamingInfo: path, offset, size) in a bundled file names a stream node of the same bundle by the
last segment of its path; all 317 of Sledding Game’s, 206,661,032 bytes in all, lie inside the stream node they name.
Proven · Sledding No decoded object of PEAK’s level files carries a StreamingInfo; the 24 .resource nodes are
named by fields of another shape. Proven · PEAK
Type trees in bundles
Section titled “Type trees in bundles”A serialized file inside an AssetBundle or Addressables bundle stores its type trees (the has-type-tree byte is 1),
unlike the player’s own loose files, which strip them: 466 of the 479 serialized files in the bundles of the three games
do (all 416 PEAK level files, which hold 83,643 types, and all 50 of Sledding Game’s, 224 types), and each stores a tree
for every one of its types. Proven · PEAK, Sledding The 13 that store none are PEAK’s maps manifest bundle and
the 12 files of Big Walk’s data.unity3d, the player’s own, which are stripped like loose player files.
Proven · PEAK, Big Walk A stored tree is read without an outside
type tree package except for the engine’s common strings, whose table the
package supplies per Unity version. Proven · PEAK, Sledding
Trees are stored per type, not per object: the 83,643 types of PEAK’s 416 level files hold 586 different trees, 60 of them for the 60 classes other than MonoBehaviour (a class has the same tree in every file) and 526 for MonoBehaviour (one per script, so a file has one MonoBehaviour type per script it uses). Proven · PEAK Sledding Game’s 224 types hold 17 different trees, 6 of them MonoBehaviour’s. Proven · Sledding A MonoBehaviour’s tree holds the script’s own fields after the base fields, which a stripped player file does not. Proven · PEAK, Sledding
The stored trees of the classes other than MonoBehaviour equal the type tree package’s trees for the file’s Unity version, node for node (type, name, byte size, version, meta flags): all 8,367 such types of PEAK’s level files and all 173 of Sledding Game’s. Proven · PEAK, Sledding The layout of a stored tree is in the SerializedFile type entry.
Sledding Game’s Addressables
Section titled “Sledding Game’s Addressables”Sledding Game’s StreamingAssets/aa/ is an Addressables 2.9.1 content folder: catalog.bin (146,902 bytes, a binary
content catalog), catalog.hash, settings.json and 50 bundles under StandaloneWindows64/. Observed · Sledding
The 50 bundles are one defaultlocalgroup_assets_all_*.bundle (1,457,997 bytes; a 124,588-byte serialized file and a
22,369,728-byte stream), one *_monoscripts_*.bundle (2,051 bytes) and the Localization package’s bundles: 16
localization-string-tables-*, 16 localization-asset-tables-*, 15 localization-assets-* (one of them shared) and
localization-locales. Observed · Sledding Their serialized files are version 22,
Unity 6000.3.12f1, with type trees. Observed · Sledding The mountain scene is not among them; it is the loose
level1. Observed · Sledding
PEAK 3.0.0 (Unity 6000.3.15f1) ships its game levels as seven AssetBundles named levels0-31, levels32-63,
levels64-95, levels96-127, levels128-159, levels160-191 and levels192-207 (each .assetbundle, with a
.manifest text file beside it), plus a bundle maps that holds the AssetBundleManifest. Observed · PEAK They
hold 208 generated scenes, Assets/8_SCENES/Generated/Level_0.unity to Level_207.unity (32 per bundle, 16 in the
last), by the manifests’ asset lists. Observed · PEAK The maps manifest lists no dependencies between the seven
bundles, and no file of one bundle points at a node of another. Proven · PEAK
A level bundle is 2.18 GB (1.41 GB for the last) and holds, per level, two serialized files, BuildPlayer-Level_N (the
scene) and BuildPlayer-Level_N.sharedAssets (the assets only that level uses), and some levels’ .resource streams
(the first bundle: 68 files, 64 serialized and 4 streams). Observed · PEAK All 416 serialized files (208 scenes
and 208 shared-asset files) are version 22, Unity 6000.3.15f1, with type trees. Proven · PEAK The scene files
are 111,465,236 to 180,596,228 bytes; the shared-asset files 303,653 bytes to 1.04 GB (Level_22).
Proven · PEAK
The player’s loose files (level0 to level4, sharedassets0 to sharedassets4) hold the menus and the island scene
(Airport, MainMenu, Pretitle, Title, WilIsland in the build settings). Observed · PEAK
Build 2.6.b instead shipped PEAK_Data/data.unity3d, a 1,504,357,874-byte bundle read offline. Observed · PEAK
The community extractor AssetStudio does not open it: its decompression cache is written beside the input bundle, and a
copy then stops in the shader parser with EndOfStreamException errors. Observed · PEAK
The 2.6.b layout is known from a reconnaissance of that build; the 3.0.0 install has loose files and the level bundles
and no data.unity3d. What 2.6.b’s data.unity3d held beside the same levels is not established.
The EndOfStreamException in AssetStudio’s shader parser on PEAK 2.6.b is observed; which shader object or layout it
fails on is not established.
Big Walk’s data.unity3d
Section titled “Big Walk’s data.unity3d”Big Walk’s single bundle is 2,451,391,952 bytes with 43,598 blocks (43,139 LZ4HC, 459 stored) that decompress to
5,714,422,612 bytes in 19 files. Proven · Big Walk The files are the player’s data folder:
globalgamemanagers, Resources/unity_builtin_extra, globalgamemanagers.assets, sharedassets0 to sharedassets3
(.assets with .resS streams), level0 to level3 (level2.resS too), resources.assets and
resources.assets.resS. Proven · Big Walk The twelve serialized files are all
version 22 containers of Unity 6000.3.17f1, each with a stated size equal
to its directory entry. Proven · Big Walk The largest member is sharedassets1.assets.resS
(4,162,131,536 bytes). Proven · Big Walk
The scene files are level0 (platform init), level1 (menus), level2 (the whole world, 91,750,964 bytes) and level3
(sky). Inferred Beside the bundle, Big Walk_Data holds il2cpp_data/Metadata/global-metadata.dat, boot.config,
app.info and three sharedassets*.resource audio streams. Observed · Big Walk