Skip to content

Commit d2c6697

Browse files
authored
Fix: some type hints (tools/extractor, legacy patch stubs) (#285)
* fix tools.extractor type hints * classes.generated - add legacy patch type hints * Creating type stubs for legacy patches This is better than putting all patched classes into __init__.pyi, and may be worth it given the backwards-compatible immutability of the legacy patch. * better legacy patch stub style * Remove meaningless stub definitions for legacy patches.
1 parent 8fa1464 commit d2c6697

File tree

9 files changed

+260
-17
lines changed

9 files changed

+260
-17
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Dict, List, Optional
2+
3+
from UnityPy.classes.generated import SampleClip, StreamedResource
4+
5+
class AudioClip(SampleClip):
6+
m_Name: str
7+
m_3D: Optional[bool] = None
8+
m_Ambisonic: Optional[bool] = None
9+
m_AudioData: Optional[List[int]] = None
10+
m_BitsPerSample: Optional[int] = None
11+
m_Channels: Optional[int] = None
12+
m_CompressionFormat: Optional[int] = None
13+
m_Format: Optional[int] = None
14+
m_Frequency: Optional[int] = None
15+
m_IsTrackerFormat: Optional[bool] = None
16+
m_Legacy3D: Optional[bool] = None
17+
m_Length: Optional[float] = None
18+
m_LoadInBackground: Optional[bool] = None
19+
m_LoadType: Optional[int] = None
20+
m_PreloadAudioData: Optional[bool] = None
21+
m_Resource: Optional[StreamedResource] = None
22+
m_Stream: Optional[int] = None
23+
m_SubsoundIndex: Optional[int] = None
24+
m_Type: Optional[int] = None
25+
m_UseHardware: Optional[bool] = None
26+
27+
@property
28+
def extension(self) -> str: ...
29+
30+
@property
31+
def samples(self) -> Dict[str, bytes]: ...
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import List, Tuple, Union
2+
3+
from UnityPy.classes import Component, PPtr
4+
from UnityPy.classes.generated import ComponentPair, EditorExtension
5+
6+
class GameObject(EditorExtension):
7+
m_Component: Union[List[ComponentPair], List[Tuple[int, PPtr[Component]]]]
8+
m_IsActive: Union[bool, int]
9+
m_Layer: int
10+
m_Name: str
11+
m_Tag: int
12+
13+
@property
14+
def m_Components(self) -> List[PPtr[Component]]: ...
15+
@property
16+
def m_Animator(self) -> Union[PPtr[Component], None]: ...
17+
@property
18+
def m_Animation(self) -> Union[PPtr[Component], None]: ...
19+
@property
20+
def m_Transform(self) -> Union[PPtr[Component], None]: ...
21+
@property
22+
def m_SkinnedMeshRenderer(self) -> Union[PPtr[Component], None]: ...
23+
@property
24+
def m_MeshRenderer(self) -> Union[PPtr[Component], None]: ...
25+
@property
26+
def m_MeshFilter(self) -> Union[PPtr[Component], None]: ...
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import List, Optional, Union
2+
3+
from UnityPy.classes.generated import (AABB, BlendShapeData, BoneInfluence,
4+
BoneWeights4, CompressedMesh,
5+
MeshBlendShape, MeshBlendShapeVertex,
6+
MinMaxAABB, NamedObject, StreamingInfo,
7+
SubMesh, VariableBoneCountWeights,
8+
VertexData)
9+
from UnityPy.classes.math import (ColorRGBA, Matrix4x4f, Vector2f, Vector3f,
10+
Vector4f)
11+
12+
class Mesh(NamedObject):
13+
m_BindPose: List[Matrix4x4f]
14+
m_CompressedMesh: CompressedMesh
15+
m_IndexBuffer: List[int]
16+
m_LocalAABB: AABB
17+
m_MeshCompression: int
18+
m_MeshUsageFlags: int
19+
m_Name: str
20+
m_SubMeshes: List[SubMesh]
21+
m_BakedConvexCollisionMesh: Optional[List[int]] = None
22+
m_BakedTriangleCollisionMesh: Optional[List[int]] = None
23+
m_BoneNameHashes: Optional[List[int]] = None
24+
m_BonesAABB: Optional[List[MinMaxAABB]] = None
25+
m_CollisionTriangles: Optional[List[int]] = None
26+
m_CollisionVertexCount: Optional[int] = None
27+
m_Colors: Optional[List[ColorRGBA]] = None
28+
m_CookingOptions: Optional[int] = None
29+
m_IndexFormat: Optional[int] = None
30+
m_IsReadable: Optional[bool] = None
31+
m_KeepIndices: Optional[bool] = None
32+
m_KeepVertices: Optional[bool] = None
33+
m_MeshMetrics_0_: Optional[float] = None
34+
m_MeshMetrics_1_: Optional[float] = None
35+
m_Normals: Optional[List[Vector3f]] = None
36+
m_RootBoneNameHash: Optional[int] = None
37+
m_ShapeVertices: Optional[List[MeshBlendShapeVertex]] = None
38+
m_Shapes: Optional[Union[BlendShapeData, List[MeshBlendShape]]] = None
39+
m_Skin: Optional[Union[List[BoneInfluence], List[BoneWeights4]]] = None
40+
m_StreamCompression: Optional[int] = None
41+
m_StreamData: Optional[StreamingInfo] = None
42+
m_Tangents: Optional[List[Vector4f]] = None
43+
m_UV: Optional[List[Vector2f]] = None
44+
m_UV1: Optional[List[Vector2f]] = None
45+
m_Use16BitIndices: Optional[int] = None
46+
m_VariableBoneCountWeights: Optional[VariableBoneCountWeights] = None
47+
m_VertexData: Optional[VertexData] = None
48+
m_Vertices: Optional[List[Vector3f]] = None
49+
50+
def export(self, format: str = "obj") -> str: ...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from UnityPy.classes import PPtr
2+
from UnityPy.classes.generated import Component
3+
from UnityPy.classes.legacy_patch import GameObject
4+
5+
class Renderer(Component):
6+
m_GameObject: PPtr[GameObject]
7+
8+
def export(self, export_dir: str) -> None: ...
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from typing import List, Optional, Tuple, Union
2+
3+
from UnityPy.classes import PPtr
4+
from UnityPy.classes.generated import (GUID, NamedObject, SerializedShader,
5+
Texture)
6+
7+
class Shader(NamedObject):
8+
m_Name: str
9+
compressedBlob: Optional[List[int]] = None
10+
compressedLengths: Optional[Union[List[int], List[List[int]]]] = None
11+
decompressedLengths: Optional[Union[List[int], List[List[int]]]] = None
12+
decompressedSize: Optional[int] = None
13+
m_AssetGUID: Optional[GUID] = None
14+
m_Dependencies: Optional[List[PPtr[Shader]]] = None
15+
m_NonModifiableTextures: Optional[List[Tuple[str, PPtr[Texture]]]] = None
16+
m_ParsedForm: Optional[SerializedShader] = None
17+
m_PathName: Optional[str] = None
18+
m_Script: Optional[str] = None
19+
m_ShaderIsBaked: Optional[bool] = None
20+
m_SubProgramBlob: Optional[List[int]] = None
21+
offsets: Optional[Union[List[int], List[List[int]]]] = None
22+
platforms: Optional[List[int]] = None
23+
stageCounts: Optional[List[int]] = None
24+
25+
def export(self) -> str: ...
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import List, Optional, Tuple
2+
3+
from PIL.Image import Image
4+
5+
from UnityPy.classes import PPtr
6+
from UnityPy.classes.generated import (GUID, MonoBehaviour, NamedObject, Rectf,
7+
SpriteAtlas, SpriteBone,
8+
SpriteRenderData)
9+
from UnityPy.classes.math import Vector2f, Vector4f
10+
11+
class Sprite(NamedObject):
12+
m_Extrude: int
13+
m_Name: str
14+
m_Offset: Vector2f
15+
m_PixelsToUnits: float
16+
m_RD: SpriteRenderData
17+
m_Rect: Rectf
18+
m_AtlasTags: Optional[List[str]] = None
19+
m_Bones: Optional[List[SpriteBone]] = None
20+
m_Border: Optional[Vector4f] = None
21+
m_IsPolygon: Optional[bool] = None
22+
m_PhysicsShape: Optional[List[List[Vector2f]]] = None
23+
m_Pivot: Optional[Vector2f] = None
24+
m_RenderDataKey: Optional[Tuple[GUID, int]] = None
25+
m_ScriptableObjects: Optional[List[PPtr[MonoBehaviour]]] = None
26+
m_SpriteAtlas: Optional[PPtr[SpriteAtlas]] = None
27+
28+
@property
29+
def image(self) -> Image: ...
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from typing import BinaryIO, List, Optional, Union
2+
3+
from PIL.Image import Image
4+
5+
from UnityPy.classes.generated import GLTextureSettings, StreamingInfo, Texture
6+
7+
class Texture2D(Texture):
8+
image_data: bytes
9+
m_CompleteImageSize: int
10+
m_Height: int
11+
m_ImageCount: int
12+
m_IsReadable: bool
13+
m_LightmapFormat: int
14+
m_Name: str
15+
m_TextureDimension: int
16+
m_TextureFormat: int
17+
m_TextureSettings: GLTextureSettings
18+
m_Width: int
19+
m_ColorSpace: Optional[int] = None
20+
m_DownscaleFallback: Optional[bool] = None
21+
m_ForcedFallbackFormat: Optional[int] = None
22+
m_IgnoreMasterTextureLimit: Optional[bool] = None
23+
m_IgnoreMipmapLimit: Optional[bool] = None
24+
m_IsAlphaChannelOptional: Optional[bool] = None
25+
m_IsPreProcessed: Optional[bool] = None
26+
m_MipCount: Optional[int] = None
27+
m_MipMap: Optional[bool] = None
28+
m_MipmapLimitGroupName: Optional[str] = None
29+
m_MipsStripped: Optional[int] = None
30+
m_PlatformBlob: Optional[List[int]] = None
31+
m_ReadAllowed: Optional[bool] = None
32+
m_StreamData: Optional[StreamingInfo] = None
33+
m_StreamingMipmaps: Optional[bool] = None
34+
m_StreamingMipmapsPriority: Optional[int] = None
35+
36+
@property
37+
def image(self) -> Image: ...
38+
def set_image(
39+
self,
40+
img: Union[Image, str, BinaryIO],
41+
target_format: Optional[int] = None,
42+
mipmap_count: int = 1,
43+
) -> None: ...
44+
def get_image_data(self) -> bytes: ...
45+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import List, Optional
2+
3+
from PIL.Image import Image
4+
5+
from UnityPy.classes.generated import GLTextureSettings, StreamingInfo, Texture
6+
7+
class Texture2DArray(Texture):
8+
image_data: bytes
9+
m_ColorSpace: int
10+
m_DataSize: int
11+
m_Depth: int
12+
m_Format: int
13+
m_Height: int
14+
m_IsReadable: bool
15+
m_MipCount: int
16+
m_Name: str
17+
m_TextureSettings: GLTextureSettings
18+
m_Width: int
19+
m_DownscaleFallback: Optional[bool] = None
20+
m_ForcedFallbackFormat: Optional[int] = None
21+
m_IgnoreMipmapLimit: Optional[bool] = None
22+
m_IsAlphaChannelOptional: Optional[bool] = None
23+
m_MipmapLimitGroupName: Optional[str] = None
24+
m_MipsStripped: Optional[int] = None
25+
m_StreamData: Optional[StreamingInfo] = None
26+
m_UsageMode: Optional[int] = None
27+
28+
@property
29+
def images(self) -> List[Image]: ...

UnityPy/tools/extractor.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
from io import BytesIO
44
from pathlib import Path
5-
from typing import Callable, Dict, List, Union
5+
from typing import Callable, Dict, List, Optional, Tuple, Union
66

77
import UnityPy
88
from UnityPy.classes import (
@@ -19,6 +19,7 @@
1919
Texture2D,
2020
)
2121
from UnityPy.enums.ClassIDType import ClassIDType
22+
from UnityPy.files import SerializedFile
2223

2324

2425
def export_obj(
@@ -27,8 +28,8 @@ def export_obj(
2728
append_name: bool = False,
2829
append_path_id: bool = False,
2930
export_unknown_as_typetree: bool = False,
30-
asset_filter: Callable[[Object], bool] = None,
31-
) -> List[int]:
31+
asset_filter: Optional[Callable[[Object], bool]] = None,
32+
) -> List[Tuple[SerializedFile, int]]:
3233
"""Exports the given object to the given filepath.
3334
3435
Args:
@@ -76,8 +77,8 @@ def extract_assets(
7677
ignore_first_container_dirs: int = 0,
7778
append_path_id: bool = False,
7879
export_unknown_as_typetree: bool = False,
79-
asset_filter: Callable[[Object], bool] = None,
80-
) -> List[int]:
80+
asset_filter: Optional[Callable[[Object], bool]] = None,
81+
) -> List[Tuple[SerializedFile, int]]:
8182
"""Extracts some or all assets from the given source.
8283
8384
Args:
@@ -90,7 +91,7 @@ def extract_assets(
9091
asset_filter (func(object)->bool, optional): Determines whether to export an object. Defaults to all objects.
9192
9293
Returns:
93-
List[int]: [description]
94+
List[Tuple[SerializedFile, int]]: [description]
9495
"""
9596
# load source
9697
env = UnityPy.load(src)
@@ -153,15 +154,15 @@ def defaulted_export_index(type: ClassIDType):
153154
###############################################################################
154155

155156

156-
def exportTextAsset(obj: TextAsset, fp: str, extension: str = ".txt") -> List[int]:
157+
def exportTextAsset(obj: TextAsset, fp: str, extension: str = ".txt") -> List[Tuple[SerializedFile, int]]:
157158
if not extension:
158159
extension = ".txt"
159160
with open(f"{fp}{extension}", "wb") as f:
160161
f.write(obj.m_Script.encode("utf-8", "surrogateescape"))
161162
return [(obj.assets_file, obj.object_reader.path_id)]
162163

163164

164-
def exportFont(obj: Font, fp: str, extension: str = "") -> List[int]:
165+
def exportFont(obj: Font, fp: str, extension: str = "") -> List[Tuple[SerializedFile, int]]:
165166
# TODO - export glyphs
166167
if obj.m_FontData:
167168
extension = ".ttf"
@@ -172,15 +173,15 @@ def exportFont(obj: Font, fp: str, extension: str = "") -> List[int]:
172173
return [(obj.assets_file, obj.object_reader.path_id)]
173174

174175

175-
def exportMesh(obj: Mesh, fp: str, extension=".obj") -> List[int]:
176+
def exportMesh(obj: Mesh, fp: str, extension=".obj") -> List[Tuple[SerializedFile, int]]:
176177
if not extension:
177178
extension = ".obj"
178179
with open(f"{fp}{extension}", "wt", encoding="utf8", newline="") as f:
179180
f.write(obj.export())
180181
return [(obj.assets_file, obj.object_reader.path_id)]
181182

182183

183-
def exportShader(obj: Shader, fp: str, extension=".txt") -> List[int]:
184+
def exportShader(obj: Shader, fp: str, extension=".txt") -> List[Tuple[SerializedFile, int]]:
184185
if not extension:
185186
extension = ".txt"
186187
with open(f"{fp}{extension}", "wt", encoding="utf8", newline="") as f:
@@ -190,7 +191,7 @@ def exportShader(obj: Shader, fp: str, extension=".txt") -> List[int]:
190191

191192
def exportMonoBehaviour(
192193
obj: Union[MonoBehaviour, Object], fp: str, extension: str = ""
193-
) -> List[int]:
194+
) -> List[Tuple[SerializedFile, int]]:
194195
export = None
195196

196197
if obj.object_reader.serialized_type.node:
@@ -224,7 +225,7 @@ def exportMonoBehaviour(
224225
return [(obj.assets_file, obj.object_reader.path_id)]
225226

226227

227-
def exportAudioClip(obj: AudioClip, fp: str, extension: str = "") -> List[int]:
228+
def exportAudioClip(obj: AudioClip, fp: str, extension: str = "") -> List[Tuple[SerializedFile, int]]:
228229
samples = obj.samples
229230
if len(samples) == 0:
230231
pass
@@ -239,7 +240,7 @@ def exportAudioClip(obj: AudioClip, fp: str, extension: str = "") -> List[int]:
239240
return [(obj.assets_file, obj.object_reader.path_id)]
240241

241242

242-
def exportSprite(obj: Sprite, fp: str, extension: str = ".png") -> List[int]:
243+
def exportSprite(obj: Sprite, fp: str, extension: str = ".png") -> List[Tuple[SerializedFile, int]]:
243244
if not extension:
244245
extension = ".png"
245246
obj.image.save(f"{fp}{extension}")
@@ -254,16 +255,15 @@ def exportSprite(obj: Sprite, fp: str, extension: str = ".png") -> List[int]:
254255
return exported
255256

256257

257-
def exportTexture2D(obj: Texture2D, fp: str, extension: str = ".png") -> List[int]:
258+
def exportTexture2D(obj: Texture2D, fp: str, extension: str = ".png") -> List[Tuple[SerializedFile, int]]:
258259
if not extension:
259260
extension = ".png"
260261
if obj.m_Width:
261262
# textures can be empty
262263
obj.image.save(f"{fp}{extension}")
263264
return [(obj.assets_file, obj.path_id)]
264265

265-
266-
def exportGameObject(obj: GameObject, fp: str, extension: str = "") -> List[int]:
266+
def exportGameObject(obj: GameObject, fp: str, extension: str = "") -> List[Tuple[SerializedFile, int]]:
267267
exported = [(obj.assets_file, obj.path_id)]
268268
refs = crawl_obj(obj)
269269
if refs:
@@ -299,7 +299,7 @@ def exportGameObject(obj: GameObject, fp: str, extension: str = "") -> List[int]
299299
MONOBEHAVIOUR_TYPETREES: Dict["Assembly-Name.dll", Dict["Class-Name", List[Dict]]] = {}
300300

301301

302-
def crawl_obj(obj: Object, ret: dict = None) -> Dict[int, Union[Object, PPtr]]:
302+
def crawl_obj(obj: Object, ret: Optional[dict] = None) -> Dict[int, Union[Object, PPtr]]:
303303
"""Crawls through the data struture of the object and returns a list of all the components."""
304304
if not ret:
305305
ret = {}

0 commit comments

Comments
 (0)