Skip to content

Commit ba2fc8e

Browse files
committed
Merge branch 'master' of https://github.com/K0lb3/UnityPy
2 parents 6126d5a + 9fc3667 commit ba2fc8e

1 file changed

Lines changed: 101 additions & 60 deletions

File tree

README.md

Lines changed: 101 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
A Unity asset extractor for Python based on [AssetStudio](https://github.com/Perfare/AssetStudio).
1010

1111
Next to extraction, UnityPy also supports editing Unity assets.
12-
Via the typetree structure all objects types can be edited.
13-
```py
12+
Via the typetree structure all object types can be edited in their native forms.
13+
14+
```python
1415
# modification via dict:
1516
raw_dict = obj.read_typetree()
1617
# modify raw dict
@@ -24,11 +25,11 @@ Via the typetree structure all objects types can be edited.
2425
If you need advice or if you want to talk about (game) data-mining,
2526
feel free to join the [UnityPy Discord](https://discord.gg/C6txv7M).
2627

27-
If you're using UnityPy a commercial project,
28+
If you're using UnityPy for a commercial project,
2829
a donation to a charitable cause or a sponsorship of this project is expected.
2930

30-
**As UnityPy is still in active development breaking changes can happen.**
31-
Those changes are usually limited to minor versions (x.y) and not to patch versions (x.y.z).
31+
**As UnityPy is still in active development, breaking changes can happen.**
32+
These changes are usually limited to minor versions (x.y) and not to patch versions (x.y.z).
3233
So in case that you don't want to actively maintain your project,
3334
make sure to make a note of the used UnityPy version in your README or add a check in your code.
3435
e.g.
@@ -42,22 +43,22 @@ if UnityPy.__version__ != '1.9.6':
4243
2. [Example](#example)
4344
3. [Important Classes](#important-classes)
4445
4. [Important Object Types](#important-object-types)
45-
5. [Custom Fileystem](#custom-filesystem)
46+
5. [Configurations](#configurations)
4647
6. [Credits](#credits)
4748

4849
## Installation
4950

50-
**Python 3.7.0 or higher is required**
51+
**Python 3.7.0 or higher is required.**
5152

52-
via pypi
53+
Install via PyPI:
5354

54-
```cmd
55+
```bash
5556
pip install UnityPy
5657
```
5758

58-
from source
59+
Install from source code:
5960

60-
```cmd
61+
```bash
6162
git clone https://github.com/K0lb3/UnityPy.git
6263
cd UnityPy
6364
python -m pip install .
@@ -70,17 +71,12 @@ python -m pip install .
7071
Visual C++ Redistributable is required for the brotli dependency.
7172
In case a new(ish) Python version is used, it can happen that the C-dependencies of UnityPy might not be precompiled for this version.
7273
In such cases the user either has to report this as issue or follow the steps of [this issue](https://github.com/K0lb3/UnityPy/issues/223) to compile it oneself.
73-
Another option for the user is downgrading Python to the latest version supported by UnityPy. For this see the python version badge at the top of the README.
74-
75-
### Crash without warning/error
74+
Another option for the user is downgrading Python to the latest version supported by UnityPy. For this see the Python version badge at the top of the README.
7675

77-
The C-implementation of the typetree reader can directly crash python.
78-
In case this happens, the usage of the C-typetree reader can be disabled by adding these two lines to your main file.
76+
#### Crash without warning/error
7977

80-
```python
81-
from UnityPy.helpers import TypeTreeHelper
82-
TypeTreeHelper.read_typetree_boost = False
83-
```
78+
The C-implementation of the typetree reader can directly crash Python.
79+
In case this happens, the usage of the C-typetree reader can be disabled. Read [this section](#disable-typetree-c-implementation) for more details.
8480

8581
## Example
8682

@@ -90,7 +86,7 @@ The following is a simple example.
9086
import os
9187
import UnityPy
9288

93-
def unpack_all_assets(source_folder : str, destination_folder : str):
89+
def unpack_all_assets(source_folder: str, destination_folder: str):
9490
# iterate over all files in source folder
9591
for root, dirs, files in os.walk(source_folder):
9692
for file_name in files:
@@ -134,24 +130,19 @@ def unpack_all_assets(source_folder : str, destination_folder : str):
134130
You probably have to read [Important Classes](#important-classes)
135131
and [Important Object Types](#important-object-types) to understand how it works.
136132

137-
People with slightly advanced python skills should look at [UnityPy/tools/extractor.py](UnityPy/tools/extractor.py) for a more advanced example.
133+
Users with slightly advanced Python skills should look at [UnityPy/tools/extractor.py](UnityPy/tools/extractor.py) for a more advanced example.
138134
It can also be used as a general template or as an importable tool.
139135

140-
### Setting the decryption key for Unity CN's AssetBundle encryption
141-
142-
The chinese version of Unity has its own inbuild option to encrypt AssetBundles/BundleFiles. As it's a feature of Unity itself, and not a game specific protection, it is included in UnityPy as well.
143-
To enable encryption simply use `UnityPy.set_assetbundle_decrypt_key(key)`, with key being the value that the game that loads the budles passes to `AssetBundle.SetAssetBundleDecryptKey`.
144-
145136
## Important Classes
146137

147-
### [Environment](UnityPy/environment.py)
138+
### Environment
148139

149-
Environment loads and parses the given files.
140+
[Environment](UnityPy/environment.py) loads and parses the given files.
150141
It can be initialized via:
151142

152143
- a file path - apk files can be loaded as well
153144
- a folder path - loads all files in that folder (bad idea for folders with a lot of files)
154-
- a stream - e.g., io.BytesIO, file stream,...
145+
- a stream - e.g., `io.BytesIO`, file stream,...
155146
- a bytes object - will be loaded into a stream
156147

157148
UnityPy can detect if the file is a WebFile, BundleFile, Asset, or APK.
@@ -183,26 +174,26 @@ with open(dst, "wb") as f:
183174
f.write(env.file.save())
184175
```
185176

186-
### [Asset](UnityPy/files/SerializedFile.py)
177+
### Asset
187178

188-
Assets are a container that contains multiple objects.
179+
Assets \([SerializedFile class](UnityPy/files/SerializedFile.py)\) are a container that contains multiple objects.
189180
One of these objects can be an AssetBundle, which contains a file path for some of the objects in the same asset.
190181

191182
All objects can be found in the `.objects` dict - `{ID : object}`.
192183

193184
The objects with a file path can be found in the `.container` dict - `{path : object}`.
194185

195-
### [Object](UnityPy/files/ObjectReader.py)
186+
### Object
196187

197-
Objects contain the _actual_ files, e.g., textures, text files, meshes, settings, ...
188+
Objects \([ObjectReader class](UnityPy/files/ObjectReader.py)\) contain the _actual_ files, e.g., textures, text files, meshes, settings, ...
198189

199190
To acquire the actual data of an object it has to be read first. This happens via the `.read()` function. This isn't done automatically to save time because only a small part of the objects are of interest. Serialized objects can be set with raw data using `.set_raw_data(data)` or modified with `.save()` function, if supported.
200191

201192
## Important Object Types
202193

203-
All object types can be found in [UnityPy/classes](UnityPy/classes/).
194+
Now UnityPy uses [auto generated classes](UnityPy/classes/generated.py) with some useful extension methods and properties defined in [legacy_patch](UnityPy/classes/legacy_patch/). You can search for a specific classes in the module `UnityPy.classes` with your IDE's autocompletion.
204195

205-
### [Texture2D](UnityPy/classes/Texture2D.py)
196+
### Texture2D
206197

207198
- `.m_Name`
208199
- `.image` converts the texture into a `PIL.Image`
@@ -226,7 +217,7 @@ for obj in env.objects:
226217
data.save()
227218
```
228219

229-
### [Sprite](UnityPy/classes/Sprite.py)
220+
### Sprite
230221

231222
Sprites are part of a texture and can have a separate alpha-image as well.
232223
Unlike most other extractors (including AssetStudio), UnityPy merges those two images by itself.
@@ -246,14 +237,15 @@ for obj in env.objects:
246237
data.image.save(path)
247238
```
248239

249-
### [TextAsset](UnityPy/classes/TextAsset.py)
240+
### TextAsset
250241

251242
TextAssets are usually normal text files.
252243

253244
- `.m_Name`
254245
- `.m_Script` - str
255246

256-
Some games save binary data as TextFile, so to convert the ``str`` back to bytes correctly ``m_Script.encode("utf-8", "surrogateescape")`` has to be used.
247+
Some games save binary data as TextAssets. As ``m_Script`` gets handled as str by default,
248+
use ``m_Script.encode("utf-8", "surrogateescape")`` to retrieve the original binary data.
257249

258250
**Export**
259251

@@ -272,7 +264,7 @@ for obj in env.objects:
272264
data.save()
273265
```
274266

275-
### [MonoBehaviour](UnityPy/classes/MonoBehaviour.py)
267+
### MonoBehaviour
276268

277269
MonoBehaviour assets are usually used to save the class instances with their values.
278270
The structure/typetree for these classes might not be contained in the asset files.
@@ -312,7 +304,7 @@ for obj in env.objects:
312304
UnityPy can generate the typetrees of MonoBehaviours from the game assemblies using an optional package, ``TypeTreeGeneratorAPI``, which has to be installed via pip.
313305
UnityPy will automatically try to generate the typetree of MonoBehaviours if the typetree is missing in the assets and ``env.typetree_generator`` is set.
314306

315-
```py
307+
```python
316308
import UnityPy
317309
from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator
318310

@@ -340,25 +332,27 @@ for obj in objects:
340332
```
341333

342334

343-
### [AudioClip](UnityPy/classes/AudioClip.py)
335+
### AudioClip
344336

345337
- `.samples` - `{sample-name : sample-data}`
346338

347339
The samples are converted into the .wav format.
348340
The sample data is a .wav file in bytes.
349341

350342
```python
351-
clip : AudioClip
343+
clip: AudioClip
352344
for name, data in clip.samples.items():
353345
with open(name, "wb") as f:
354346
f.write(data)
355347
```
356348

357-
### [Font](UnityPy/classes/Font.py)
349+
### Font
350+
351+
**Export**
358352

359353
```python
360354
if obj.type.name == "Font":
361-
font : Font = obj.read()
355+
font: Font = obj.read()
362356
if font.m_FontData:
363357
extension = ".ttf"
364358
if font.m_FontData[0:4] == b"OTTO":
@@ -368,14 +362,14 @@ if obj.type.name == "Font":
368362
f.write(font.m_FontData)
369363
```
370364

371-
### [Mesh](UnityPy/classes/Mesh.py)
365+
### Mesh
372366

373367
- `.export()` - mesh exported as .obj (str)
374368

375369
The mesh will be converted to the Wavefront .obj file format.
376370

377371
```python
378-
mesh : Mesh
372+
mesh: Mesh
379373
with open(f"{mesh.m_Name}.obj", "wt", newline = "") as f:
380374
# newline = "" is important
381375
f.write(mesh.export())
@@ -390,7 +384,7 @@ ALPHA-VERSION
390384
The mesh and materials will be in the Wavefront formats.
391385

392386
```python
393-
mesh_renderer : Renderer
387+
mesh_renderer: Renderer
394388
export_dir: str
395389

396390
if mesh_renderer.m_GameObject:
@@ -400,7 +394,7 @@ if mesh_renderer.m_GameObject:
400394
mesh_renderer.export(export_dir)
401395
```
402396

403-
### [Texture2DArray](UnityPy/classes/Texture2DArray.py)
397+
### Texture2DArray
404398

405399
WARNING - not well tested
406400

@@ -423,32 +417,79 @@ for obj in env.objects:
423417
# editing isn't supported yet!
424418
```
425419

426-
## Custom-Filesystem
420+
## Configurations
421+
422+
There're several configurations and interfaces that provide the customizability to UnityPy.
423+
424+
### Unity CN Decryption
425+
426+
The Chinese version of Unity has its own builtin option to encrypt AssetBundles/BundleFiles. As it's a feature of Unity itself, and not a game specific protection, it is included in UnityPy as well.
427+
To enable encryption simply use the code as follow, with `key` being the value that the game that loads the bundles passes to `AssetBundle.SetAssetBundleDecryptKey`.
428+
429+
```python
430+
import UnityPy
431+
UnityPy.set_assetbundle_decrypt_key(key)
432+
```
433+
434+
### Unity Fallback Version
435+
436+
In case UnityPy failed to detect the Unity version of the game assets, you can set a fallback version. e.g.
437+
438+
```python
439+
import UnityPy.config
440+
UnityPy.config.FALLBACK_UNITY_VERSION = "2.5.0f5"
441+
```
442+
443+
### Disable Typetree C-Implementation
444+
445+
The [C-implementation](UnityPyBoost/) of typetree reader can boost the parsing of typetree by a lot. If you want to disable it and use pure Python reader, you can put the following 2 lines in your main file.
446+
447+
```python
448+
from UnityPy.helpers import TypeTreeHelper
449+
TypeTreeHelper.read_typetree_boost = False
450+
```
451+
452+
### Custom Block (De)compression
453+
454+
Some game assets have non-standard compression/decompression algorithm applied on the block data. If you wants to customize the compression/decompression function, you can modify the corresponding function mapping. e.g.
455+
456+
```python
457+
from UnityPy.enums.BundleFile import CompressionFlags
458+
flag = CompressionFlags.LZHAM
459+
460+
from UnityPy.helpers import CompressionHelper
461+
CompressionHelper.COMPRESSION_MAP[flag] = custom_compress
462+
CompressionHelper.DECOMPRESSION_MAP[flag] = custom_decompress
463+
```
464+
465+
- `custom_compress(data: bytes) -> bytes` (where bytes can also be bytearray or memoryview)
466+
- `custom_decompress(data: bytes, uncompressed_size: int) -> bytes`
467+
468+
### Custom Filesystem
427469

428470
UnityPy uses [fsspec](https://github.com/fsspec/filesystem_spec) under the hood to manage all filesystem interactions.
429471
This allows using various different types of filesystems without having to change UnityPy's code.
430472
It also means that you can use your own custom filesystem to e.g. handle indirection via catalog files, load assets on demand from a server, or decrypt files.
431473

432474
Following methods of the filesystem have to be implemented for using it in UnityPy.
433475

434-
- sep (not a function, just the seperator as character)
435-
- isfile(self, path: str) -> bool
436-
- isdir(self, path: str) -> bool
437-
- exists(self, path: str, \*\*kwargs) -> bool
438-
- walk(self, path: str, \*\*kwargs) -> Iterable[List[str], List[str], List[str]]
439-
- open(self, path: str, mode: str = "rb", \*\*kwargs) -> file ("rb" mode required, "wt" required for ModelExporter)
440-
- makedirs(self, path: str, exist_ok: bool = False) -> bool
476+
- `sep` (not a function, just the separator as character)
477+
- `isfile(self, path: str) -> bool`
478+
- `isdir(self, path: str) -> bool`
479+
- `exists(self, path: str, **kwargs) -> bool`
480+
- `walk(self, path: str, **kwargs) -> Iterable[List[str], List[str], List[str]]`
481+
- `open(self, path: str, mode: str = "rb", **kwargs) -> file` ("rb" mode required, "wt" required for ModelExporter)
482+
- `makedirs(self, path: str, exist_ok: bool = False) -> bool`
441483

442484
## Credits
443485

444486
First of all,
445487
thanks a lot to all contributors of UnityPy and all of its users.
446488

447-
Also,
448-
many thanks to:
489+
Also, many thanks to:
449490

450491
- [Perfare](https://github.com/Perfare) for creating and maintaining and every contributor of [AssetStudio](https://github.com/Perfare/AssetStudio)
451492
- [ds5678](https://github.com/ds5678) for the [TypeTreeDumps](https://github.com/AssetRipper/TypeTreeDumps) and the [custom minimal Tpk format](https://github.com/AssetRipper/Tpk)
452493
- [Razmoth](https://github.com/Razmoth) for figuring out and sharing Unity CN's AssetBundle decryption ([src](https://github.com/Razmoth/PGRStudio)).
453494
- [nesrak1](https://github.com/nesrak1) for figuring out the [Switch texture swizzling](https://github.com/nesrak1/UABEA/blob/master/TexturePlugin/Texture2DSwitchDeswizzler.cs)
454-
- xiop_13690 (discord) for figuring out unsolved issues of the ManagedReferencesRegistry
495+
- xiop_13690 (discord) for figuring out unsolved issues of the ManagedReferencesRegistry

0 commit comments

Comments
 (0)