Unity import
Research Nothing here is built or approved yet. This page collects what four research passes measured on 2026-10-04, so the design can be decided from facts.
DigitalHeaven should be able to consume Unity content: the AssetBundles and Addressables that Unity games ship, and the .unitypackage files that avatar bases ship. The importer follows the same rule as the Source importers. DigitalHeaven ships the recipe. The user supplies the ingredients: the game or package they own. The result is a local pallet carrying the same shared platform id everyone else’s copy has.
Three layers are planned:
- A generic Unity importer. Unity has changed its formats many times, so this is several readers, one for each version range. It is not one reader for everything.
- Game-specific importers. Each is a game recipe on top of the generic importer. It adds metadata, rewrites the game’s internal ids, decides which assets are content roots, and later wires behaviors.
- Optional external tools. These help where a license or the scope forbids building the capability in.
The corpus
Section titled “The corpus”Every claim marked measured was read from real files on the dev machine:
- 58 Unity games, from Unity 5.3 to 6000.4.3.
- 22,029 AssetBundles totaling 114 GB.
- 20 Addressables installs across 19 games, plus 21 BONELAB mod catalogs.
Game files never left the machine. Only header excerpts and counts were recorded.
| Unity | Games | IL2CPP | Ship Addressables | SerializedFile version |
|---|---|---|---|---|
| 5.3 | 1 | 0 | 0 | 15 |
| 5.6.7 | 2 | 0 | 0 | 17 |
| 2018.4 | 1 | 0 | 0 | 17 |
| 2019.4 | 3 | 1 | 0 | 21 |
| 2020.3 | 7 | 2 | 2 | 22 |
| 2021.x | 12 | 2 | 8 | 22 |
| 2022.x | 16 | 3 | 4 | 22 |
| 2023.x | 2 | 1 | 1 | 22 |
| 6000.x | 14 | 5 | 5 | 22 |
What the corpus showed:
- Every bundle uses the UnityFS container. None use the older UnityWeb or UnityRaw containers, and none are encrypted.
- Every sampled bundle carries its type tree, the self-description of each object’s fields. That includes about 2,900 bundles from 22 games.
- Loose player data never carries type trees. That means
sharedassets,levelfiles anddata.unity3d.
So a bundle or Addressables importer can read objects from the trees the files carry. Only an importer for loose player data needs a database of class layouts.
Unity’s file formats
Section titled “Unity’s file formats”The UnityFS container
Section titled “The UnityFS container”A bundle has a big-endian header:
- the signature,
UnityFS; - a format version, 6, 7 or 8;
- the engine version strings;
- the block-info sizes;
- the flags.
The block info is a table of compressed blocks followed by a directory of nodes. Each node is a SerializedFile (CAB-<hash>) or a resource stream: .resS for texture and mesh data, .resource for audio and video.
| Measured | Value |
|---|---|
| Block-info compression | LZ4HC in every file |
| Data-block compression | LZ4HC in most games. Uncompressed in Rain World, Chippy and Rust. LZMA in Beat Saber and one VRChat world. |
| LZ4 chunk size | 131,072 bytes in every file |
| Largest single block | 4,209,067,949 bytes (Rust), so block sizes are unsigned 32-bit |
| Flags | 0x243 in 22,009 files, 0x43 in 20 |
Flag 0x200 means “padding at the start” from 2020.3.34, 2021.3.2 and 2022.1.1 onward. In older Chinese builds the same bit means encryption, so a reader interprets it by engine version. Every local file that sets it uses it as padding.
SerializedFile
Section titled “SerializedFile”| Version | Unity | What changes for a reader |
|---|---|---|
| 9 | 3.5–4.7 | The endianness byte moves into the header |
| 10–14 | 5.0 alphas | Script index, type hashes, the type-tree flag, 64-bit path ids |
| 15 | 5.0.1–5.4 | A per-object “stripped” byte |
| 16–17 | 5.5–2018.4 | Type ids index a type list, and the script index moves into the type |
| 18–20 | 2019.1–2019.2 | Reference type hashes, SerializeReference types |
| 21 | 2019.3–2019.4 | Type dependencies |
| 22 | 2020.1 onward | 64-bit file offsets. 51 of the 58 local games use this version. |
| 23 | 6000.5 | Type trees move out to a .typetreedata file |
| 24–26 | 6000.7 | A separate type-tree version, shared sub-trees |
Objects reference each other through PPtr: a file index plus a path id. File 0 is the file itself. File n is the file’s nth external, which inside a bundle looks like archive:/CAB-x/CAB-x.
Where readers need version logic
Section titled “Where readers need version logic”When a type tree is present, one generic tree-driven reader handles most classes with no version checks. That covers GameObject, Transform, Material, Light, Camera, renderers, PrefabInstance, ReflectionProbe and Terrain metadata. Version logic is needed only for these:
- The container and the SerializedFile header. That’s version 6/7/8, the alignment rules, the meaning of
0x200, and the layouts before and after version 22. - Payloads the tree doesn’t describe:
- Mesh vertex data: the vertex-format enum changed around 2017 and 2019, plus compressed meshes, the index format and streamed data;
- texture pixels: BC1–7, Crunch, ETC2 and ASTC;
- AnimationClip streamed, dense and constant curves;
.resSoffsets, which are 32-bit before 2020.1 and 64-bit after.
- MonoBehaviour layouts without a tree. The layout comes from the game’s Mono assemblies or, for IL2CPP games, from its metadata through Cpp2IL.
Notable breakpoints:
- 2018.3 reworked prefabs into
PrefabInstance. - 2020.1 moved to large-file offsets.
- 2021.x changed how materials store keywords.
- 6000.5 extracts type trees into a separate file.
Engine version strings also arrive in irregular forms (5.x.x, 5.5.0p3, 6000.0.67f1-DWR, 6000.3.15x1-13), so the version parser must tolerate suffixes.
Addressables
Section titled “Addressables”Addressables is a lookup layer on top of ordinary AssetBundles. It doesn’t replace them.
flowchart TD K["key: address, label, GUID or scene index"] --> A["asset location<br/>provider: BundledAssetProvider<br/>internal id: the asset's name inside its bundle"] A -->|dependencies| D["bundle locations<br/>the asset's own bundle first, then everything it needs"] D --> B["AssetBundleProvider<br/>path, hash, CRC, size"] B --> F["UnityFS bundle"] -->|"look up the internal id in m_Container"| O["the object"]- Addresses and labels are keys. A label maps to many assets. Every addressable asset is also keyed by its Unity GUID, which is what
AssetReferenceuses. - Groups exist only in the editor. In a built game, all that’s left of a group is its bundle names.
- The catalog lives in
StreamingAssets/aa/, next tosettings.jsonand the<platform>/*.bundlefiles. Paths in it begin with the{UnityEngine.AddressableAssets.Addressables.RuntimePath}placeholder.
Catalog formats
Section titled “Catalog formats”| Addressables | Unity | Catalog | Local games | Status |
|---|---|---|---|---|
| < 1.18 | 2018.3–2020.2 | JSON | none | untested |
| 1.18–1.20 | 2020.3–2021.3 | JSON | Risk of Rain 2, Vertigo 2, Rain World, KoboldKare, Paint the Town Red | read |
| 1.21–1.27 | 2021.3–2023.2 | JSON by default, binary optional | BONELAB, ULTRAKILL, Megabonk, REPO, Placid Plastic Duck Simulator | read (JSON only; no binary sample) |
| 2.3–2.8 | 6000.0–6000.3 | binary catalog.bin version 2 | Guilty as Sock!, Content Warning, BongoCat, Gamble With Your Friends | read |
| 2.9 | 6000.3 | either | Beat Saber (JSON) | read |
| 3.x / 4.x | 2026+ | binary version 3 | none | untested |
The JSON catalog is string tables plus four base64 blobs:
m_BucketDataString: each key’s list of entries.m_KeyDataString: kind-tagged keys (ASCII, UTF-16, integers, hashes, JSON objects).m_EntryDataString: 28 bytes per location.m_ExtraDataString: the bundle options.
The binary catalog starts with the magic bytes 42 89 E3 0D. Locations are shared through offsets. Long strings are linked lists of reused segments, joined in reverse order from version 2 on. Both formats decode into the same model.
A prototype decoder of about 250 lines reads both formats and resolves addresses with no Unity installed. In every one of the 20 installs, each bundle on disk matched the size its catalog records, to the byte. For example, ULTRAKILL’s Filth.prefab resolves to its own bundle plus 24 dependency bundles.
Game-specific rules
Section titled “Game-specific rules”Games bend Addressables, which is why game importers need a hook to rewrite internal ids:
- BONELAB rewrites every bundle id as
PALLET_BARCODE:<barcode>:\…. Its pallets and crates are Stress Level Zero’s own JSON layer on top of Addressables. A crate’smainAssetis the Unity GUID used as the catalog key. Mods ship their own catalog and monoscripts bundle, and reference game content by barcode. - Placid Plastic Duck Simulator puts DLC bundles under
DLCs/, with paths relative to the game root. - ULTRAKILL names assets inside bundles by GUID instead of by path.
Existing tools
Section titled “Existing tools”| Tool | License | How DigitalHeaven can use it | Unity range | Notes |
|---|---|---|---|---|
| AssetRipper | GPL-3.0 | Only as a separate program the user installs | 3.5–6000.4 | The only tool that exports scenes, prefabs, terrain and lightmaps. Does not read catalogs. |
| AssetRipper Premium | Proprietary, $10 a month | Same, on the user’s machine | same | Adds shader decompilation (experimental), IL2CPP method recovery and static mesh separation |
| AssetsTools.NET | MIT | Link or port | UnityFS (5.3+); SerializedFile up to 22, partly 23 | netstandard2.0, little reflection. Texture decoders including Crunch, ASTC and ETC. No mesh, material or animation decoding. |
| AddressablesTools | NuGet says MIT; the repo has no license file | Probably link | JSON and binary catalogs, versions 1–3 | The only C# catalog reader |
| AssetStudio | MIT | Link or port | Original stops at 2022.1 (archived); aelurum’s fork reaches 6000.2 | No scenes, prefabs or materials |
| UnityPy | MIT (Python) | Port the algorithms | UnityFS, UnityWeb/Raw, SerializedFile 23 | Ships a class database of about 200 KB covering every version |
| Cpp2IL | MIT | Link | Version-aware | Recovers MonoBehaviour layouts for IL2CPP games |
| Il2CppDumper | MIT | Link | 5.3–2022.2 | Stalled since 2024 |
The asset classes each tool can export:
- ✅: exports it.
- 🟡: partial. For AssetsTools.NET this means it reads the data and DigitalHeaven would write the decoder.
- ❌: doesn’t export it.
| Asset class | AssetRipper | AssetStudio | UnityPy | AssetsTools.NET |
|---|---|---|---|---|
| Mesh | ✅ | ✅ | ✅ | 🟡 |
| Skin and blendshapes | ✅ (Unity project) | ✅ (FBX) | ❌ | 🟡 |
| Texture2D | ✅ | ✅ | ✅ | ✅ |
| Material | ✅ | ❌ | 🟡 | 🟡 |
| Shader | 🟡 stub | 🟡 | 🟡 | ❌ |
| AnimationClip | ✅ | ✅ | 🟡 | 🟡 |
| Prefab, scene | ✅ | ❌ | ❌ | 🟡 |
| Terrain, lightmaps | ✅ | ❌ | ❌ | 🟡 |
| AudioClip | ✅ | ✅ | ✅ | 🟡 |
| MonoBehaviour, IL2CPP | 🟡 | 🟡 | 🟡 | ✅ (Cpp2IL) |
No MIT-licensed tool exports prefabs, scenes, terrain, lightmaps or materials. DigitalHeaven writes those decoders itself, or reaches them through an AssetRipper the user installed.
Mapping Unity onto DigitalHeaven
Section titled “Mapping Unity onto DigitalHeaven”Nothing in the repository reads Unity content today. Every Unity project in it goes from DigitalHeaven to Unity. Studio’s platform catalog already lists ULTRAKILL with a “Unity importer” recipe, and nothing implements that importer yet.
| Unity | DigitalHeaven | Today |
|---|---|---|
| Mesh, submeshes, UV0–3 | GLB | Lossless (UV4–7 are dropped) |
| Vertex colors | GLB COLOR_0, COLOR_1 | Lossless for two sets: COLOR_0 multiplies the base color and COLOR_1 weights a detail layer (see vertex colors) |
| Skin, blendshapes | GLB skin and morphs | The GLB writer can’t write them yet; the loader can read them |
| Texture2D (BC1–5) | PNG | Decoders exist |
| BC7, ETC2, ASTC, Crunch | PNG | Decoders needed |
| Standard / URP / HDRP Lit | lit material | Lossless, with texture repacking done by a dh.texture pack recipe |
| Poiyomi, lilToon | lit + matcap + detail + emissive | Approximate; rim, toon ramp and outline are lost |
| Unknown shader | property-name heuristics → lit | Each one gets a line in the import report |
| Prefab | dh.object | Renderers, colliders and rigidbodies only |
| Box and mesh colliders | box, hull, mesh | Sphere and capsule become hulls |
| Scene | dh.map | Repeated meshes become object instances. Spawns come from the game recipe. |
| Lightmaps (RGBM, dLDR, BC6H, directional) | imported .dh-lightmap (MonoSH) | Good fit; needs a calibration measurement |
| Light probes (SH L2) | .dh-probegroup | Good fit |
| Reflection probes | reflectionProbe + .dh-reflcook | Good fit, with prefilter differences |
| Humanoid Avatar | dh.rig | Lossless bone map |
| VRChat descriptor, PhysBones | visemes, eyelids, spring bones | Approximate |
| AnimationClip, Animator | — | Needs a clip format |
| AudioClip | loose audio | dh.sound isn’t built yet |
| Terrain, fog, particles | — | Need formats |
| MonoBehaviour | property bags for recipes | A game recipe maps them to entities |
These DigitalHeaven changes would close the most gaps, in order of value:
- Skin and morph output in the GLB writer.
- A shared texture-codec library: move the DXT decoder out of the Source project and add BC7, then ETC2 and ASTC.
- Sphere and capsule colliders.
- A shared table of material property names in Core. The Unity runtime’s material loader already holds one, and the importer would use it in reverse.
A vertex color lane.Shipped: two sets, see vertex colors.dh.sound.- A toon shader model.
- A clip format.
dh.terrain.
Proposed shape
Section titled “Proposed shape”These are recommendations, not decisions.
The reader stack
Section titled “The reader stack”- Container. Use AssetsTools.NET for UnityFS. Add a ported reader for UnityWeb/Raw only if a pre-5.3 range is ever wanted, and a decryption hook that only a game recipe fills in.
- Type data. Use the file’s own type tree first. Then a class database keyed by Unity version, which is needed only for player data and SerializedFile 23. Then Mono assemblies or Cpp2IL for MonoBehaviours.
- Decoders. DigitalHeaven’s own, branching on tree fields and the engine version: mesh, skin, material, animation, terrain and lighting.
- Catalogs. DigitalHeaven’s own reader, written from the measured layout. AddressablesTools has no license file, so it serves as a reference only.
- AssetRipper stays optional. It is a separate program the user installs, used for scene-level fidelity and to cross-check imports. DigitalHeaven never bundles it or links to it.
Version support
Section titled “Version support”| Range | Container | SerializedFile | Local games | Tier |
|---|---|---|---|---|
| 2020.3–6000.4 | UnityFS 7, 8 | 22 | 51 | Must |
| 2017.4–2019.4 (and 5.5–5.6) | UnityFS 6, 7 | 17, 21 | 6 | Should |
| 6000.5–6000.6 | UnityFS 8 | 23 | none yet | Should, claimed only once a sample exists |
| 6000.7 | ? | 24–26 | none | Later |
| 5.0–5.4 | UnityWeb/Raw, UnityFS 6 | 10–15 | 1 | Later |
| 3.5–4.7 and UnityArchive | UnityWeb/Raw | ≤ 9 | none | Never |
| China-encrypted bundles | any | any | none | Never in the base; a recipe hook only |
Layering game importers
Section titled “Layering game importers”The generic importer exposes extension points, and a game recipe fills them in:
- Where content comes from: a folder, a catalog, a single bundle, or a
.unitypackage. - The internal id transform, for example BONELAB’s
PALLET_BARCODE. - Which keys are content roots, for example BONELAB crates or every address.
- Shader-family mappers.
- MonoBehaviour mapping onto DigitalHeaven entities.
- Pallet naming and metadata.
- Later, logic wiring and scripts.
A game that needs only data is a row in the generic project. A game that needs code gets its own project that references the generic one, as Platforms.Postal2 does with Platforms.Source.
A .unitypackage is a different reader family. It is a tar of GUID folders holding source assets (YAML, FBX, PNG), not built data. It fits the offline library recipe for paid avatar bases: the file name and SHA-256 go in, and a local pallet comes out.
Work in waves
Section titled “Work in waves”Each wave is sized by scope:
- Decide the open questions below.
- Container, SerializedFile, type trees, the object table, the version resolver, and
dh import unity --list, tested on synthetic files. - Textures and meshes, including skin and morph output.
- Materials.
- Objects, prefabs, avatars and colliders.
- Scenes into maps.
- Lighting, with calibration.
- Addressables catalogs and
.unitypackage. Move this earlier if the first target game ships Addressables. - Game recipes and the recipe registry.
- Audio, animation and terrain, each after its DigitalHeaven format exists.
Open questions
Section titled “Open questions”- Project name.
Platforms.Unityfollows the one-project-per-platform rule, but collides with the existingUnity/group and thecom.unity.unityplatform id. - Loose player data. Is it in scope, or only bundles and Addressables? Including it means shipping a class-layout database. The public dumps have no license file, so the database would be generated from Unity editors.
- AssetRipper. Is an optional, user-installed GPL program acceptable, or should DigitalHeaven avoid depending on it even as a separate process?
- First target game. It decides whether Addressables comes first.
- DigitalHeaven format changes to approve: vertex colors, sphere and capsule colliders, terrain (bake to a mesh, or a new
dh.terrain), and a toon shader model. - Unity 6.5 and newer (SerializedFile 23). Should it be in the first pass?