1
- from io import BytesIO
2
- import os
3
1
import json
2
+ import os
3
+ from io import BytesIO
4
+ from pathlib import Path
5
+ from typing import Callable , Dict , List , Union
6
+
4
7
import UnityPy
5
8
from UnityPy .classes import (
9
+ AudioClip ,
10
+ Font ,
11
+ GameObject ,
12
+ Mesh ,
13
+ MonoBehaviour ,
6
14
Object ,
7
15
PPtr ,
8
- MonoBehaviour ,
9
- TextAsset ,
10
- Font ,
11
16
Shader ,
12
- Mesh ,
13
17
Sprite ,
18
+ TextAsset ,
14
19
Texture2D ,
15
- AudioClip ,
16
- GameObject ,
17
20
)
18
21
from UnityPy .enums .ClassIDType import ClassIDType
19
- from typing import Union , List , Dict , Callable
20
- from pathlib import Path
21
22
22
23
23
24
def export_obj (
@@ -57,7 +58,7 @@ def export_obj(
57
58
return []
58
59
59
60
if append_name :
60
- fp = os .path .join (fp , obj .name if obj . name else obj .type .name )
61
+ fp = os .path .join (fp , obj .m_Name if getattr ( obj , "m_Name" ) else obj .type .name )
61
62
62
63
fp , extension = os .path .splitext (fp )
63
64
@@ -114,7 +115,7 @@ def defaulted_export_index(type: ClassIDType):
114
115
# the check of the various sub directories is required to avoid // in the path
115
116
obj_dest = os .path .join (
116
117
dst ,
117
- * (x for x in obj_path .split ("/" )[: ignore_first_container_dirs ] if x ),
118
+ * (x for x in obj_path .split ("/" )[ignore_first_container_dirs : ] if x ),
118
119
)
119
120
os .makedirs (os .path .dirname (obj_dest ), exist_ok = True )
120
121
exported .extend (
@@ -156,8 +157,8 @@ def exportTextAsset(obj: TextAsset, fp: str, extension: str = ".txt") -> List[in
156
157
if not extension :
157
158
extension = ".txt"
158
159
with open (f"{ fp } { extension } " , "wb" ) as f :
159
- f .write (obj .script )
160
- return [(obj .assets_file , obj .path_id )]
160
+ f .write (obj .m_Script . encode ( "utf-8" , "surrogateescape" ) )
161
+ return [(obj .assets_file , obj .object_reader . path_id )]
161
162
162
163
163
164
def exportFont (obj : Font , fp : str , extension : str = "" ) -> List [int ]:
@@ -167,65 +168,60 @@ def exportFont(obj: Font, fp: str, extension: str = "") -> List[int]:
167
168
if obj .m_FontData [0 :4 ] == b"OTTO" :
168
169
extension = ".otf"
169
170
with open (f"{ fp } { extension } " , "wb" ) as f :
170
- f .write (obj .m_FontData )
171
- return [(obj .assets_file , obj .path_id )]
171
+ f .write (bytes ( obj .m_FontData ) )
172
+ return [(obj .assets_file , obj .object_reader . path_id )]
172
173
173
174
174
175
def exportMesh (obj : Mesh , fp : str , extension = ".obj" ) -> List [int ]:
175
176
if not extension :
176
177
extension = ".obj"
177
178
with open (f"{ fp } { extension } " , "wt" , encoding = "utf8" , newline = "" ) as f :
178
179
f .write (obj .export ())
179
- return [(obj .assets_file , obj .path_id )]
180
+ return [(obj .assets_file , obj .object_reader . path_id )]
180
181
181
182
182
183
def exportShader (obj : Shader , fp : str , extension = ".txt" ) -> List [int ]:
183
184
if not extension :
184
185
extension = ".txt"
185
186
with open (f"{ fp } { extension } " , "wt" , encoding = "utf8" , newline = "" ) as f :
186
187
f .write (obj .export ())
187
- return [(obj .assets_file , obj .path_id )]
188
+ return [(obj .assets_file , obj .object_reader . path_id )]
188
189
189
190
190
191
def exportMonoBehaviour (
191
192
obj : Union [MonoBehaviour , Object ], fp : str , extension : str = ""
192
193
) -> List [int ]:
193
194
export = None
194
- # TODO - add generic way to add external typetrees
195
- if obj .serialized_type and obj .serialized_type .node :
196
- extension = ".json"
197
- export = json .dumps (obj .read_typetree (), indent = 4 , ensure_ascii = False ).encode (
198
- "utf8" , errors = "surrogateescape"
199
- )
195
+
196
+ if obj .object_reader .serialized_type .node :
197
+ # a typetree is available from the SerializedFile for this object
198
+ export = obj .object_reader .read_typetree ()
200
199
elif isinstance (obj , MonoBehaviour ):
201
- # no set typetree
202
- # check if we have a script
203
- script = obj .m_Script
204
- if script :
200
+ # try to get the typetree from the MonoBehavior script
201
+ script_ptr = obj .m_Script
202
+ if script_ptr :
205
203
# looks like we have a script
206
- script = script .read ()
204
+ script = script_ptr .read ()
207
205
# check if there is a locally stored typetree for it
208
206
nodes = MONOBEHAVIOUR_TYPETREES .get (script .m_AssemblyName , {}).get (
209
207
script .m_ClassName , None
210
208
)
211
209
if nodes :
212
- # we have a typetree
213
- # adjust the name
214
- # name = (
215
- # f"{script.m_ClassName}-{obj.name}"
216
- # if obj.name
217
- # else script.m_ClassName
218
- # )
219
- extension = ".json"
220
- export = json .dumps (
221
- obj .read_typetree (nodes ), indent = 4 , ensure_ascii = False
222
- ).encode ("utf8" , errors = "surrogateescape" )
210
+ export = obj .object_reader .read_typetree (nodes )
211
+ else :
212
+ export = obj .object_reader .read_typetree ()
213
+
223
214
if not export :
224
215
extension = ".bin"
225
- export = obj .raw_data
216
+ export = obj .object_reader .raw_data
217
+ else :
218
+ extension = ".json"
219
+ export = json .dumps (export , indent = 4 , ensure_ascii = False ).encode (
220
+ "utf8" , errors = "surrogateescape"
221
+ )
226
222
with open (f"{ fp } { extension } " , "wb" ) as f :
227
223
f .write (export )
228
- return [(obj .assets_file , obj .path_id )]
224
+ return [(obj .assets_file , obj .object_reader . path_id )]
229
225
230
226
231
227
def exportAudioClip (obj : AudioClip , fp : str , extension : str = "" ) -> List [int ]:
@@ -240,16 +236,16 @@ def exportAudioClip(obj: AudioClip, fp: str, extension: str = "") -> List[int]:
240
236
for name , clip_data in samples .items ():
241
237
with open (os .path .join (fp , f"{ name } .wav" ), "wb" ) as f :
242
238
f .write (clip_data )
243
- return [(obj .assets_file , obj .path_id )]
239
+ return [(obj .assets_file , obj .object_reader . path_id )]
244
240
245
241
246
242
def exportSprite (obj : Sprite , fp : str , extension : str = ".png" ) -> List [int ]:
247
243
if not extension :
248
244
extension = ".png"
249
245
obj .image .save (f"{ fp } { extension } " )
250
246
exported = [
251
- (obj .assets_file , obj .path_id ),
252
- (obj .m_RD .texture .assets_file , obj .m_RD .texture .path_id ),
247
+ (obj .assets_file , obj .object_reader . path_id ),
248
+ (obj .m_RD .texture .assetsfile , obj .m_RD .texture .path_id ),
253
249
]
254
250
alpha_assets_file = getattr (obj .m_RD .alphaTexture , "assets_file" , None )
255
251
alpha_path_id = getattr (obj .m_RD .alphaTexture , "path_id" , None )
@@ -322,8 +318,7 @@ def crawl_obj(obj: Object, ret: dict = None) -> Dict[int, Union[Object, PPtr]]:
322
318
# MonoBehaviour really on their typetree
323
319
# while Object denotes that the class of the object isn't implemented yet
324
320
if isinstance (obj , (MonoBehaviour , Object )):
325
- obj .read_typetree ()
326
- data = obj .type_tree .__dict__ .values ()
321
+ data = obj .read_typetree ().__dict__ .values ()
327
322
else :
328
323
data = obj .__dict__ .values ()
329
324
0 commit comments