Skip to content

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:

  1. 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.
  2. 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.
  3. Optional external tools. These help where a license or the scope forbids building the capability in.

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.

UnityGamesIL2CPPShip AddressablesSerializedFile version
5.310015
5.6.720017
2018.410017
2019.431021
2020.372222
2021.x122822
2022.x163422
2023.x21122
6000.x145522

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, level files and data.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.

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.

MeasuredValue
Block-info compressionLZ4HC in every file
Data-block compressionLZ4HC in most games. Uncompressed in Rain World, Chippy and Rust. LZMA in Beat Saber and one VRChat world.
LZ4 chunk size131,072 bytes in every file
Largest single block4,209,067,949 bytes (Rust), so block sizes are unsigned 32-bit
Flags0x243 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.

VersionUnityWhat changes for a reader
93.5–4.7The endianness byte moves into the header
10–145.0 alphasScript index, type hashes, the type-tree flag, 64-bit path ids
155.0.1–5.4A per-object “stripped” byte
16–175.5–2018.4Type ids index a type list, and the script index moves into the type
18–202019.1–2019.2Reference type hashes, SerializeReference types
212019.3–2019.4Type dependencies
222020.1 onward64-bit file offsets. 51 of the 58 local games use this version.
236000.5Type trees move out to a .typetreedata file
24–266000.7A 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.

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;
    • .resS offsets, 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 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 AssetReference uses.
  • 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 to settings.json and the <platform>/*.bundle files. Paths in it begin with the {UnityEngine.AddressableAssets.Addressables.RuntimePath} placeholder.
AddressablesUnityCatalogLocal gamesStatus
< 1.182018.3–2020.2JSONnoneuntested
1.18–1.202020.3–2021.3JSONRisk of Rain 2, Vertigo 2, Rain World, KoboldKare, Paint the Town Redread
1.21–1.272021.3–2023.2JSON by default, binary optionalBONELAB, ULTRAKILL, Megabonk, REPO, Placid Plastic Duck Simulatorread (JSON only; no binary sample)
2.3–2.86000.0–6000.3binary catalog.bin version 2Guilty as Sock!, Content Warning, BongoCat, Gamble With Your Friendsread
2.96000.3eitherBeat Saber (JSON)read
3.x / 4.x2026+binary version 3noneuntested

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.

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’s mainAsset is 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.
ToolLicenseHow DigitalHeaven can use itUnity rangeNotes
AssetRipperGPL-3.0Only as a separate program the user installs3.5–6000.4The only tool that exports scenes, prefabs, terrain and lightmaps. Does not read catalogs.
AssetRipper PremiumProprietary, $10 a monthSame, on the user’s machinesameAdds shader decompilation (experimental), IL2CPP method recovery and static mesh separation
AssetsTools.NETMITLink or portUnityFS (5.3+); SerializedFile up to 22, partly 23netstandard2.0, little reflection. Texture decoders including Crunch, ASTC and ETC. No mesh, material or animation decoding.
AddressablesToolsNuGet says MIT; the repo has no license fileProbably linkJSON and binary catalogs, versions 1–3The only C# catalog reader
AssetStudioMITLink or portOriginal stops at 2022.1 (archived); aelurum’s fork reaches 6000.2No scenes, prefabs or materials
UnityPyMIT (Python)Port the algorithmsUnityFS, UnityWeb/Raw, SerializedFile 23Ships a class database of about 200 KB covering every version
Cpp2ILMITLinkVersion-awareRecovers MonoBehaviour layouts for IL2CPP games
Il2CppDumperMITLink5.3–2022.2Stalled 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 classAssetRipperAssetStudioUnityPyAssetsTools.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.

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.

UnityDigitalHeavenToday
Mesh, submeshes, UV0–3GLBLossless (UV4–7 are dropped)
Vertex colorsGLB COLOR_0, COLOR_1Lossless for two sets: COLOR_0 multiplies the base color and COLOR_1 weights a detail layer (see vertex colors)
Skin, blendshapesGLB skin and morphsThe GLB writer can’t write them yet; the loader can read them
Texture2D (BC1–5)PNGDecoders exist
BC7, ETC2, ASTC, CrunchPNGDecoders needed
Standard / URP / HDRP Litlit materialLossless, with texture repacking done by a dh.texture pack recipe
Poiyomi, lilToonlit + matcap + detail + emissiveApproximate; rim, toon ramp and outline are lost
Unknown shaderproperty-name heuristics → litEach one gets a line in the import report
Prefabdh.objectRenderers, colliders and rigidbodies only
Box and mesh collidersbox, hull, meshSphere and capsule become hulls
Scenedh.mapRepeated 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-probegroupGood fit
Reflection probesreflectionProbe + .dh-reflcookGood fit, with prefilter differences
Humanoid Avatardh.rigLossless bone map
VRChat descriptor, PhysBonesvisemes, eyelids, spring bonesApproximate
AnimationClip, Animator—Needs a clip format
AudioCliploose audiodh.sound isn’t built yet
Terrain, fog, particles—Need formats
MonoBehaviourproperty bags for recipesA game recipe maps them to entities

These DigitalHeaven changes would close the most gaps, in order of value:

  1. Skin and morph output in the GLB writer.
  2. A shared texture-codec library: move the DXT decoder out of the Source project and add BC7, then ETC2 and ASTC.
  3. Sphere and capsule colliders.
  4. 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.
  5. A vertex color lane. Shipped: two sets, see vertex colors.
  6. dh.sound.
  7. A toon shader model.
  8. A clip format.
  9. dh.terrain.

These are recommendations, not decisions.

  1. 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.
  2. 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.
  3. Decoders. DigitalHeaven’s own, branching on tree fields and the engine version: mesh, skin, material, animation, terrain and lighting.
  4. Catalogs. DigitalHeaven’s own reader, written from the measured layout. AddressablesTools has no license file, so it serves as a reference only.
  5. 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.
RangeContainerSerializedFileLocal gamesTier
2020.3–6000.4UnityFS 7, 82251Must
2017.4–2019.4 (and 5.5–5.6)UnityFS 6, 717, 216Should
6000.5–6000.6UnityFS 823none yetShould, claimed only once a sample exists
6000.7?24–26noneLater
5.0–5.4UnityWeb/Raw, UnityFS 610–151Later
3.5–4.7 and UnityArchiveUnityWeb/Raw≤ 9noneNever
China-encrypted bundlesanyanynoneNever in the base; a recipe hook only

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.

Each wave is sized by scope:

  1. Decide the open questions below.
  2. Container, SerializedFile, type trees, the object table, the version resolver, and dh import unity --list, tested on synthetic files.
  3. Textures and meshes, including skin and morph output.
  4. Materials.
  5. Objects, prefabs, avatars and colliders.
  6. Scenes into maps.
  7. Lighting, with calibration.
  8. Addressables catalogs and .unitypackage. Move this earlier if the first target game ships Addressables.
  9. Game recipes and the recipe registry.
  10. Audio, animation and terrain, each after its DigitalHeaven format exists.
  1. Project name. Platforms.Unity follows the one-project-per-platform rule, but collides with the existing Unity/ group and the com.unity.unity platform id.
  2. 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.
  3. AssetRipper. Is an optional, user-installed GPL program acceptable, or should DigitalHeaven avoid depending on it even as a separate process?
  4. First target game. It decides whether Addressables comes first.
  5. 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.
  6. Unity 6.5 and newer (SerializedFile 23). Should it be in the first pass?