UnityPy Documentation
repository·master·Indexed 23 days ago
https://github.com/k0lb3/unitypyA Python-based asset extractor and editor for Unity files. UnityPy allows developers to extract textures, meshes, and other assets, and modify existing Unity assets via dictionary or class-based patching. It includes the Environment class for loading files (APKs, AssetBundles, and Assets) and the Object class for parsing data. The library supports exporting Texture2D, Sprite, TextAsset, MonoBehaviour, AudioClip, Font, and Mesh assets, and offers integration with TypeTreeGeneratorAPI for MonoBehaviour assets lacking a typetree.
What's inside UnityPy
- UnityPyBoost is a C-extension designed to accelerate various parts of the UnityPy library. It provides significant performance improvements over the original Python implementations. The extension is structured such that the filename of the compiled module corresponds to the location of the original Python functionality it replaces or augments.
How to modify Unity assets via dict or parsed class
masterUnityPy allows you to edit Unity assets using two primary methods: modifying a raw dictionary or modifying a parsed class instance. After making changes, use the
.patch()method to apply them back to the object.Method 1: Modification via dict
Use
.parse_as_dict()to get a dictionary representation, modify the keys, and then use.patch().Method 2: Modification via parsed class
Use
.parse_as_object()to get a class instance, modify its attributes, and then use.patch().Note: For object types containing
m_Name, you can use.peek_name()to check the name without the overhead of full parsing.# modification via dict: raw_dict = obj.parse_as_dict() # modify raw dict obj.patch(raw_dict) # modification via parsed class instance = obj.parse_as_object() # modify instance obj.patch(instance)Implement a Custom Filesystem
masterUnityPy uses
fsspecfor filesystem interactions. You can implement a custom filesystem by providing a class that implements the following methods:sep: The character used as a separator.isfile(self, path: str) -> boolisdir(self, path: str) -> boolexists(self, path: str, **kwargs) -> boolwalk(self, path: str, **kwargs) -> Iterable[List[str], List[str], List[str]]open(self, path: str, mode: str = "rb", **kwargs) -> file(Note:"rb"is required;"wt"is required for ModelExporter)makedirs(self, path: str, exist_ok: bool = False) -> bool
Install UnityPy via pip or source
masterUnityPy requires Python 3.8 or higher.
To install the package via PyPI:
pip install UnityPyTo install from the source code:
git clone https://github.com/K0lb3/UnityPy.git cd UnityPy python -m pip install .Windows Users Note: Visual C++ Redistributable is required for the
brotlidependency. If C-dependencies are not precompiled for your Python version, you may need to compile them manually or downgrade to a supported Python version.pip install UnityPyHow to use TypeTreeGenerator for MonoBehaviours
masterIf MonoBehaviour assets lack a typetree, you can generate one from the game's assemblies using the
TypeTreeGeneratorAPIpackage (install viapip install TypeTreeGeneratorAPI).To use it, create a
TypeTreeGeneratorinstance with the game's Unity version, load the local game files, and assign the generator to yourenv.typetree_generator. UnityPy will then automatically use it when calling.parse_as_object()on MonoBehaviours.import UnityPy from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator # create generator GAME_ROOT_DIR: str # e.g. r"D:\Program Files (x86)\Steam\steamapps\common\Aethermancer Demo" GAME_UNITY_VERSION: str # you can get the version via an object # e.g. objects[0].assets_file.unity_version generator = TypeTreeGenerator(GAME_UNITY_VERSION) generator.load_local_game(GAME_ROOT_DIR) # generator.load_local_game(root_dir: str) - for a Windows game # generator.load_dll_folder(dll_dir: str) - for mono / non-il2cpp or generated dummies # generator.load_dll(dll: bytes) # generator.load_il2cpp(il2cpp: bytes, metadata: bytes) env = UnityPy.load(fp) # assign generator to env env.typetree_generator = generator for obj in objects: if obj.type.name == "MonoBehaviour": # automatically tries to use the generator in the background if necessary x = obj.parse_as_object()Set Unity Fallback Version
masterIf UnityPy fails to detect the Unity version of the game assets, you can manually set a fallback version using
UnityPy.config.FALLBACK_UNITY_VERSION.import UnityPy.config UnityPy.config.FALLBACK_UNITY_VERSION = "2.5.0f5"Configure Unity CN Decryption
masterTo decrypt AssetBundles/BundleFiles encrypted by the Chinese version of Unity, use
UnityPy.set_assetbundle_decrypt_key(key). Thekeyshould be the value passed toAssetBundle.SetAssetBundleDecryptKeyby the game.import UnityPy UnityPy.set_assetbundle_decrypt_key(key)Customize Block (De)compression
masterYou can override the compression/decompression algorithms for specific flags by modifying the
CompressionHelpermaps. This is useful for games using non-standard algorithms.Required function signatures:
custom_compress(data: bytes) -> bytescustom_decompress(data: bytes, uncompressed_size: int) -> bytes
from UnityPy.enums.BundleFile import CompressionFlags flag = CompressionFlags.LZHAM from UnityPy.helpers import CompressionHelper CompressionHelper.COMPRESSION_MAP[flag] = custom_compress CompressionHelper.DECOMPRESSION_MAP[flag] = custom_decompressHow the Environment manages files and split files
masterThe
Environmentis designed to handle complex Unity data structures, specifically:- Split Files: Unity often splits large assets into multiple parts (e.g.,
data.assets.split0,data.assets.split1). TheEnvironmentautomatically detects these patterns and reconstructs the full file in memory whenload_fileorload_filesis called. - Case Insensitivity: Unity paths are often case-insensitive. The
Environmentusesfind_sensitive_pathto resolve file requests that might have different casing than the actual filesystem. - Dependency Management: When loading files, the environment tracks whether a file is a primary asset or a dependency, which affects how they are indexed in the
containerandobjectsproperties.
- Split Files: Unity often splits large assets into multiple parts (e.g.,
Disable Typetree C-Implementation
masterTo disable the high-performance C-implementation of the typetree reader and use the pure Python reader instead, set
TypeTreeHelper.read_typetree_boosttoFalse.from UnityPy.helpers import TypeTreeHelper TypeTreeHelper.read_typetree_boost = FalseUnpack all assets from a folder
masterThis example demonstrates how to iterate through a source folder, load every file using
UnityPy.load(), and extractTexture2DorSpriteobjects as.pngfiles. It shows two approaches: one using the object's internal name and another using the original container path to preserve directory structures.import os import UnityPy def unpack_all_assets(source_folder: str, destination_folder: str): # iterate over all files in source folder for root, dirs, files in os.walk(source_folder): for file_name in files: # generate file_path file_path = os.path.join(root, file_name) # load that file via UnityPy.load env = UnityPy.load(file_path) # iterate over internal objects for obj in env.objects: # process specific object types if obj.type.name in ["Texture2D", "Sprite"]: # parse the object data data = obj.parse_as_object() # create destination path dest = os.path.join(destination_folder, data.m_Name) # make sure that the extension is correct # you probably only want to do so with images/textures dest, ext = os.path.splitext(dest) dest = dest + ".png" img = data.image img.save(dest) # alternative way which keeps the original path for path,obj in env.container.items(): if obj.type.name in ["Texture2D", "Sprite"]: data = obj.parse_as_object() # create dest based on original path dest = os.path.join(destination_folder, *path.split("/")) # make sure that the dir of that path exists os.makedirs(os.path.dirname(dest), exist_ok = True) # correct extension dest, ext = os.path.splitext(dest) dest = dest + ".png" data.image.save(dest)Export Mesh assets
masterMeshes can be exported to the Wavefront
.objformat using the.export()method, which returns the mesh data as a string.mesh: Mesh with open(f"{mesh.m_Name}.obj", "wt", newline = "") as f: # newline = "" is important f.write(mesh.export())