Skip to content

About

Read Unity AssetBundles in the browser and Node.js. Browse assets, extract text and script data, and decode textures and sprites with TypeScript and WebAssembly.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

unity-asset-reader

Read Unity AssetBundles in JavaScript, in your browser or in Node.js. Browse the assets in a bundle, read text and script data, and turn textures and sprites into RGBA pixels.

Try the live demo — open a bundle, browse its contents, and preview its textures and sprites.

  • The parser is written in TypeScript. Compressed texture decoding uses WebAssembly.
  • You do not need Unity, .NET, or a native application installed.
  • Packages for meshes, audio, Spine, shaders and animation build on the same core.
  • A command line tool lists, inspects and extracts bundles: npx unity-asset-reader-cli.

Quick start

Node.js

Install the core, the texture package and the Node.js file loader:

npm install unity-asset-reader unity-asset-reader-texture unity-asset-reader-node

Load a bundle (or a whole folder) and get every texture and sprite as pixels:

import { images } from "unity-asset-reader-texture";
import { loadPath } from "unity-asset-reader-node";

const env = loadPath("Build/StreamingAssets/bundles"); // a file, or a folder read recursively
env.files; // every unpacked file: [{ path: "CAB-…", data }, { path: "CAB-….resS", data }]
// Every Texture2D and Sprite, decoded. The WASM decoder loads on first use.
for await (const { rgba, width, height, path, name, formatName } of images(env)) {
  console.log(path ?? name, width, height, formatName); // "assets/ui/icon.png" 256 256 "DXT5"
}
  • Bytes that are not on disk (an upload, a download): skip unity-asset-reader-node and call load(bytes). bytes is a Uint8Array or an ArrayBuffer (a Buffer is a Uint8Array too). Or call await open(url) to fetch the file.
  • Saving a PNG: encodePng({ data: rgba, width, height }) from unity-asset-reader-texture returns the file's bytes, uncompressed. For smaller files, hand the RGBA to an image library, for example sharp(rgba, { raw: { width, height, channels: 4 } }).png().toFile("out.png").

Browser

npm install unity-asset-reader unity-asset-reader-texture
import { open } from "unity-asset-reader";
import { initTexture, isImage, imageInfo, decodeImage } from "unity-asset-reader-texture";

// A browser has no package folder to read from, so tell it where the two WASM files are:
// a CDN, as here, or your own static folder (`npx texture2ddecoder-copy-wasm public/wasm`).
await initTexture({ wasmPath: "https://cdn.jsdelivr.net/npm/texture2ddecoder-wasm@1/wasm" });

const env = await open("a.bundle"); // fetches it; a File from an <input> works too
const icon = env.get("Assets/UI/Icon.png"); // an asset by its path in the Unity project
if (icon && isImage(icon)) { // a Texture2D or a Sprite
  const { formatName, mipCount } = imageInfo(icon); // metadata, no decoding
  const { rgba, width, height } = await decodeImage(icon); // RGBA, top row first
}

Parsing is synchronous, so a big bundle blocks the page while it loads. Run it in a Web Worker: examples/cdn.html does that, and the Bundler Guide has the Vite and Next.js setups.

Choose your packages

What you need Packages
Read bundles and their assets unity-asset-reader
Decode textures and sprites too unity-asset-reader + unity-asset-reader-texture
Load files or folders from disk Add unity-asset-reader-node
Export meshes as OBJ or glTF Add unity-asset-reader-mesh
Convert audio clips to WAV (or Vorbis clips to Ogg) Add unity-asset-reader-audio
Export Spine skeletons (data, atlas, page textures) Add unity-asset-reader-spine
Write shaders as AssetStudio's inspection text (GLSL, Metal, SPIR-V) Add unity-asset-reader-shader
Decode animation clips into curves, or export an animated scene to glTF (.glb) Add unity-asset-reader-animation
Decode raw texture blocks without reading Unity files texture2ddecoder-wasm
List, inspect or extract bundles from the command line, without writing code npx unity-asset-reader-cli

How they fit together:

  • Every package except texture2ddecoder-wasm builds on the core. The feature packages list unity-asset-reader as a peer dependency, so install it next to them.
  • The texture package installs texture2ddecoder-wasm automatically.
  • The command line tool is an application, not a library: it installs the core and the packages it uses itself.
  • Install only what you need: reading text or script data does not load the texture decoder.
  • Each package has its own version.

What each package does, and what it leaves out:

  • Core (unity-asset-reader): unpacks bundles, lists assets and reads their data. Use load(bytes) for bytes you already have, or await open(url) to fetch a file. Its GltfWriter writes binary glTF 2.0 files from data you give it (meshes, nodes, skins, morph targets, animations). It does not decode textures to pixels or audio to WAV, does not convert Unity meshes, and does not read the disk. It throws UnsupportedError on a compression, container or Unity version it does not read.

  • Textures (unity-asset-reader-texture): converts Texture2D and Sprite assets to RGBA pixels with await decodeImage(asset). It does not decode mip levels past the first, Cubemap, Texture2DArray or Texture3D assets, or a few formats such as DXT3 and ARGBFloat (see Texture formats).

  • Node.js (unity-asset-reader-node): loads a file or folder with loadPath(), including split bundles and resource sidecars. It does not run in browsers, does not stream (each file is read whole), and does not look up dependency bundles itself: pass them, or their folder.

  • Meshes (unity-asset-reader-mesh): writes a Mesh as Wavefront OBJ text with meshToObj(), as AssetStudio exports it, or as a binary glTF 2.0 file with meshToGltf() (built on core's GltfWriter). It does not export materials, bones, blend shapes or UV channels past the first.

  • Audio (unity-asset-reader-audio): converts an AudioClip to a WAV file with fsb5ToWav(clip.data.audioData), for PCM and ADPCM sound. Converts Vorbis sound to Ogg with fsb5ToOgg(), given its setup header (none ships with the package). It does not convert MPEG, ATRAC9, XMA, Opus or other console codecs, or clips from before Unity 5.0, which hold their file as it is.

  • Spine (unity-asset-reader-spine): finds each spine-unity skeleton's data, atlas text and page textures with spineSkeletons(env). It does not parse the skeleton data or atlas text (both come back as stored), and does not read bundles built without type trees.

  • Shaders (unity-asset-reader-shader): writes a Shader as the inspection text AssetStudio exports, with shaderToText(): OpenGL and Metal source, and SPIR-V disassembly. It does not write Direct3D programs, and its text is not a shader Unity can compile.

  • Animation (unity-asset-reader-animation):

    • clipCurves(env, clip) decodes an AnimationClip into the curves AssetStudio's .anim export holds: each curve's path, attribute and keyframes, and the clip's events.
    • sceneToGltf(env, root, { clips }) exports a scene (nodes, skinned meshes, blend shapes) with its clips as a glTF 2.0 .glb. A humanoid clip's muscles (fingers aside) and RootQ are converted to bone rotations.

    It does not write .anim YAML. Of a humanoid clip, it does not convert the body position (the Hips keep their own position), the finger muscles, the root motion or the IK goals.

  • Command line (unity-asset-reader-cli): ls, info, extract and dump for one bundle, file or folder, built on the packages above. extract writes images as PNG, audio as WAV or Ogg, meshes as OBJ or glTF, shaders and text as text. It does not take several paths or globs (pass their folder), and does not export what the packages above do not (Direct3D shaders, MPEG audio, Vorbis audio without its setup headers).

  • Standalone decoder (texture2ddecoder-wasm): decodes compressed texture blocks to BGRA. It does not read Unity files.

Documentation

Supported formats and Unity versions

This is the short answer. The full tables, with what each claim is tested on, are in the package READMEs: containers, compression, classes and Unity versions, texture formats, platforms and sprites, and texture2ddecoder-wasm's block formats.

A few terms used below:

  • A SerializedFile is the file inside a bundle that holds the objects (assets). A game build also ships loose ones, outside any bundle (.assets, level0, ...).
  • A type tree is the field layout Unity can store next to the objects. With it, any class can be read; without it, only classes that have a hand-written reader.
  • A version-stripped file is built with AssetBundleStripUnityVersion, so it does not say which Unity version wrote it.

What it reads

  • Containers:
    • UnityFS.
    • UnityWeb and UnityRaw (legacy web player bundles).
    • UnityWebData1.0 (a WebGL .data file).
    • A gzip-wrapped file.
    • Loose SerializedFiles (.assets, level0, globalgamemanagers).
    • .resS and .resource resource files.
    • Split files (.split0, .split1, ...), through unity-asset-reader-node.
  • Bundle compression: none, LZ4, LZ4HC, LZMA.
  • SerializedFile formats: 2 to 23, little- and big-endian.
  • Classes: any class, through its type tree. These classes have a hand-written reader, which also reads bundles built without type trees: AssetBundle, TextAsset, MonoBehaviour, MonoScript, Material, Shader, Texture2D, Sprite, SpriteAtlas, AudioClip, VideoClip, Font, MovieTexture, Mesh (its vertices and triangles decoded), Avatar, AnimatorController, Animator, AnimatorOverrideController, AnimationClip, Animation, GameObject, Transform, RectTransform, MeshFilter, MeshRenderer and SkinnedMeshRenderer.
  • Texture formats: output is RGBA8, top row first, first mip level.
    • 22 plain formats (RGBA32, RGB565, RHalf, RGB9e5Float, YUY2, RGBA64, ...), in TypeScript.
    • Through WASM: BC1 (DXT1), BC3 (DXT5), BC4 to BC7; ETC1, ETC2 and their 3DS variants; EAC R and RG, signed too; PVRTC 2 and 4 bpp; ATC; ASTC LDR and HDR at 4x4 to 12x12.
    • Crunch (DXT1Crunched, DXT5Crunched, ETC_RGB4Crunched, ETC2_RGBA8Crunched), through WASM.
  • Sprites:
    • Cut out of their texture, or of their SpriteAtlas when it is loaded.
    • Packer flips and rotations undone.
    • Optional transparency outside a tight mesh.
    • The sprites a Unity 6000.6 SpriteAtlas holds itself, which have no Sprite objects, through packedSprites() and images().
  • Audio (through unity-asset-reader-audio):
    • AudioClip to WAV for FSB5 banks of PCM (8, 16, 32 bit), IMA ADPCM, FADPCM and GCADPCM sound. FSB5 is the FMOD sound bank format Unity stores clips in.
    • To Ogg for Vorbis sound, given a caller-supplied setup header table.
    • Tested to match AssetStudio's WAV on the PCM clips and the mono and stereo IMA ADPCM clips of the 2019.4, 2020.3 and 6000.3 bundles.
  • Shaders (through unity-asset-reader-shader): Shader to AssetStudio's inspection text.
    • OpenGL, OpenGL ES and Metal programs as their source; Vulkan programs as SPIR-V disassembly.
    • Tested to match AssetStudio's .shader file on the OpenGL ES, Vulkan and Metal shaders of the 2019.4 and 2020.3 bundles.
    • Tested to match its converter's text (on UnityPy's type tree) on those of the 6000.3 and 6000.6 bundles without an Int property, several keyword variants per stage included.
  • Platforms: Xbox 360 textures are byte-swapped back, and Switch textures deswizzled. Textures of every other build target are decoded as stored, except PS4 and PS5 ones, which are refused (#130).
  • Runtimes: browsers and Web Workers (ES2020, WebAssembly for textures); Node.js ^20.19.0 || >=22.12.0, with import and require.

Unity versions

Tested on bundles built by the Unity editor:

  • Unity 2019.4.41f2, 2020.3.30f1 and 6000.3.25f1. That covers SerializedFile formats 21 and 22 and UnityFS formats 7 and 8; LZ4, LZMA and uncompressed blocks; with and without type trees; and version-stripped files.
    • PVRTC comes from 2019.4 only (Unity 6 no longer writes it).
    • Sprites and atlases come from 2019.4 and 6000.3.
  • Unity 6000.6.4f1: SerializedFile format 23 metadata, type-tree dumps, Sprite and SpriteAtlas.
  • Shader comes from 2019.4, 2020.3, 6000.3.26f1 and 6000.6 (DX11, GLES3 and Vulkan; Metal from 2020.3 and 6000.3).
  • Avatar, AnimatorController, Animator, AnimatorOverrideController, AnimationClip, Animation, GameObject, Transform, RectTransform, MeshFilter, MeshRenderer and SkinnedMeshRenderer come from 2019.4, 2020.3, 6000.3.26f1 and 6000.6.

Tested on generated or hand-written bytes only. No real Unity build of these versions or containers is in the tests:

  • the class readers' version gates from Unity 3.4 to 6000.6;
  • SerializedFile formats 6, 8 and 15;
  • UnityFS format 6;
  • UnityWeb and UnityRaw formats 2, 3, 4 and 6;
  • UnityWebData and gzip.

Handled in code:

  • SerializedFile formats 2 to 23 (Unity 2020.1 to 6000.3 write 22, 6000.6 writes 23). Type tree reads work on every one of them, whatever the Unity version.
  • The class readers know the layouts from Unity 3.4 up to 6000.6, and read a newer version with the newest layout they know. Some start later: Sprite from 4.3, VideoClip from 5.6, SpriteAtlas from 2017.1, and Mesh, Shader, the animation classes and the scene classes from 2019.1.
  • Unity 2019.1 is the supported floor. Readers added since that decision (Mesh, Shader, Avatar, AnimatorController, Animator, AnimatorOverrideController, AnimationClip, Animation, GameObject, Transform, RectTransform, MeshFilter, MeshRenderer, SkinnedMeshRenderer) start there. The older readers keep their older layouts.
  • SpriteAtlas's 6000.6 layout is tested on Unity 6000.6.4f1 bundles, and so are the texture package's cuts of the sprites such an atlas holds.
  • A class reader refuses a version older than its first layout. readTypeTree() still reads such an object.
  • Texture2D is the exception: it has no floor. It has gates at 2.6 and 3.0 that no test covers, and reads any older version with its oldest layout.

Version-stripped files (AssetBundleStripUnityVersion):

  • Type tree reads work.

  • A class reader reads the object when its bytes or the SerializedFile format decide the layout, and refuses it otherwise:

    • Texture2D, MovieTexture and MonoScript: always refused.
    • Material, Sprite, SpriteAtlas, Mesh and the animation classes: refused outside formats 18 to 21.
    • Shader: refused outside format 21.

    So these classes are refused in every version-stripped file from Unity 2020.1 on.

  • If you know the editor that built the file, pass it to load() or open() as the unityVersion fallback. The class readers then read such objects too.

  • The table per class is in the core README.

Not supported

Each of these throws UnsupportedError, naming what it found:

  • Compression: LZHAM, brotli (a WebGL build's .br files), zstd.
  • Containers: UnityArchive, zip archives, encrypted bundles (UnityCN and other game-specific encryption).
  • Texture formats:
    • DXT3, ARGBFloat, RGBFloat, BGR24, and the signed plain formats.
    • Textures built for PS4 or PS5.
    • Sprites whose alpha texture (ETC1 split alpha) is not the size of their texture.
    • A Sprite object that points at a Unity 6000.6 SpriteAtlas (the atlas' own packed sprites are supported).
  • Meshes: a submesh of lines or points, a vertex format Unity does not define.
  • Audio:
    • Vorbis without a setup header table from the caller (none ships).
    • The other FSB5 codecs that are not PCM, ADPCM or Vorbis (MPEG, ATRAC9, XMA, ...).
    • GCADPCM of more than one channel.
    • Sound that is not an FSB5 bank (AudioClips from before Unity 5.0).
  • Shader text: Direct3D programs (they need Windows' D3DCompiler), and Int properties, on which AssetStudio fails too.

Not provided at all:

  • decoding video;
  • exporting models as FBX;
  • mip levels other than the first;
  • the Cubemap, Texture2DArray and Texture3D classes;
  • JPEG encoding;
  • writing or repacking bundles.

Development

git clone --recurse-submodules https://github.com/fatal10110/unity-asset-reader.git
cd unity-asset-reader
npm ci
npm run verify   # build, test, check:browser, no-C# guard

Compare libraries

See the feature and format comparison for a ✓ / ✗ table of Unity readers and texture decoders.

Acknowledgements

This project stands on the work below. The reader packages are a derivative port, not a clean-room rewrite:

  • Every ported file starts with a line naming the file it came from.
  • The copyright notices are kept in NOTICE and in each package's NOTICE or LICENSE.
Project License What it is used for
AssetStudio, Razviar's fork of Perfare's AssetStudio (© Perfare, RazTools, Razviar) MIT The source of truth for behaviour. The bundle, SerializedFile and type tree readers, the class readers, the plain texture conversion, the Xbox 360 byte swap, sprite cropping and the tight-mesh mask, loadPath()'s file handling, the WAV header and the shader text are hand-ported from it. Also the oracle for the export goldens: scripts/make-export-goldens.py runs its exporters, outside the repo.
UnityPy (© K0lb3) MIT The oracle that generates the parsing goldens (scripts/make-goldens.py). Also ported where AssetStudio lacks something or disagrees: the UnityFS header version gates, the [SerializeReference] registry, the common-string table and the Switch texture deswizzle.
AssetRipper TypeTreeDumps and Tpk (ds5678) TypeTreeDumps: no license stated; Tpk: MIT Indirectly: the type tree data in UnityPy's TPK table, which the common-string table and several class readers' version gates were taken from.
AssetsTools.NET (© nesrak1) MIT UnityPy's Switch deswizzle, ported into unity-asset-reader-texture, is based on its SwitchSwizzle.cs.
ImageSharp.Drawing (© Six Labors) Apache-2.0 The sprite tight-mesh triangle fill in unity-asset-reader-texture is a modified TypeScript translation of parts of v1.0.0-beta15, the version AssetStudio uses, so the masks match to the pixel.
ImageSharp (© Six Labors) Apache-2.0 The sprite variant-atlas resize in unity-asset-reader-texture is a modified TypeScript translation of parts of v2.1.3, the version AssetStudio uses, so the resized sprites match to the byte.
Fmod5Sharp (© 2021 Sam Byass) MIT The FSB5 parser, the ADPCM decoders and the Vorbis to Ogg rebuild of unity-asset-reader-audio are hand-ported from it.
python-fsb5 (© 2016 Simon Pinfold) MIT Second reference for the FSB5 parser of unity-asset-reader-audio.
CSspv (© 2017 Matthäus G. Chajdas) BSD-2-Clause The SPIR-V disassembler of unity-asset-reader-shader is hand-ported from it, as AssetStudio vendors it.
smol-v (© 2016-2018 Aras Pranckevicius) MIT or public domain The SMOL-V format Unity stores Vulkan programs in; the decoder of unity-asset-reader-shader is ported from AssetStudio's C# port of its decoder, and the decoder of its newer encoding (Unity 2020.3 and later) from smol-v's source/smolv.cpp.
texture2ddecoder (© K0lb3) MIT The C++ block and Crunch decoders that texture2ddecoder-wasm compiles to WASM (a git submodule).
Codecs inside texture2ddecoder: Perfare's AssetStudio (ATC, BCn), mikunyan (ASTC, ETC, PVRTC), FP16, BinomialLLC/crunch, Unity-Technologies/crunch MIT; MIT; MIT; public domain; zlib The block decoders, half floats, and Crunch and Unity Crunch unpacking.
fflate MIT Runtime dependency of unity-asset-reader: gzip and zlib, synchronously.
lzma1 MIT Runtime dependency of unity-asset-reader: LZMA, synchronously and without WASM.
Emscripten MIT or University of Illinois/NCSA Compiles texture2ddecoder to the WASM module and generates its JavaScript loader (the pinned emscripten/emsdk:4.0.7 Docker image).

Development only, not shipped:

  • TypeScript (Apache-2.0), Rollup, esbuild and tsx (MIT) build and test the packages.
  • Playwright (Apache-2.0) runs the browser smoke test.
  • The test bundles are built with the Unity Editor from our own projects (fixtures/BUILDING.md).

License

MIT, with two packages that also include code under another license:

  • unity-asset-reader-texture (packages/texture) is MIT AND Apache-2.0. Its tight-mesh fill and variant-atlas resize are derived from ImageSharp.Drawing and ImageSharp, and are under Apache-2.0 (see its NOTICE and LICENSE-APACHE).
  • unity-asset-reader-shader (packages/shader) is MIT AND BSD-2-Clause. Its SPIR-V disassembler is ported from CSspv, and is under BSD-2-Clause (see its NOTICE and LICENSE-BSD).

The upstreams the repo derives from (AssetStudio, UnityPy and others) are listed in NOTICE. Each published package ships its own NOTICE, which is the authoritative one for its tarball.

About

Read Unity AssetBundles in the browser and Node.js. Browse assets, extract text and script data, and decode textures and sprites with TypeScript and WebAssembly.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages