1919 Texture2D ,
2020)
2121from UnityPy .enums .ClassIDType import ClassIDType
22- from UnityPy .files import SerializedFile
22+ from UnityPy .files import ObjectReader , SerializedFile
2323
2424
2525def export_obj (
26- obj : Union [Object , PPtr ],
27- fp : Path ,
26+ obj : Union [ObjectReader , PPtr ],
27+ fp : str ,
2828 append_name : bool = False ,
2929 append_path_id : bool = False ,
3030 export_unknown_as_typetree : bool = False ,
@@ -53,25 +53,29 @@ def export_obj(
5353 return []
5454
5555 # set filepath
56- obj = obj .read ()
56+ if isinstance (obj , PPtr ):
57+ obj = obj .deref ()
58+
59+ instance = obj .parse_as_object ()
5760
5861 # a filter that returned True during an earlier extract_assets check can return False now with more info from read()
59- if asset_filter and not asset_filter (obj ):
62+ if asset_filter and not asset_filter (instance ):
6063 return []
6164
6265 if append_name :
66+ name = getattr (instance , "m_Name" , obj .type .name )
6367 fp = os .path .join (
6468 fp ,
65- obj . m_Name if getattr ( obj , "m_Name" ) else obj . object_reader . type . name , # noqa: B009
69+ name ,
6670 )
6771
6872 fp , extension = os .path .splitext (fp )
6973
7074 if append_path_id :
71- fp = f"{ fp } _{ obj .object_reader . path_id } "
75+ fp = f"{ fp } _{ obj .m_PathID } "
7276
7377 # export
74- return export_func (obj , fp , extension )
78+ return export_func (instance , fp , extension )
7579
7680
7781def extract_assets (
@@ -158,15 +162,21 @@ def defaulted_export_index(type: ClassIDType):
158162###############################################################################
159163
160164
161- def exportTextAsset (obj : TextAsset , fp : str , extension : str = ".txt" ) -> List [Tuple [SerializedFile , int ]]:
165+ def exportTextAsset (
166+ obj : Union [TextAsset , ObjectReader ], fp : str , extension : str = ".txt"
167+ ) -> List [Tuple [SerializedFile , int ]]:
168+ if isinstance (obj , ObjectReader ):
169+ obj = obj .parse_as_object ()
162170 if not extension :
163171 extension = ".txt"
164172 with open (f"{ fp } { extension } " , "wb" ) as f :
165173 f .write (obj .m_Script .encode ("utf-8" , "surrogateescape" ))
166174 return [(obj .assets_file , obj .object_reader .path_id )]
167175
168176
169- def exportFont (obj : Font , fp : str , extension : str = "" ) -> List [Tuple [SerializedFile , int ]]:
177+ def exportFont (obj : Union [Font , ObjectReader ], fp : str , extension : str = "" ) -> List [Tuple [SerializedFile , int ]]:
178+ if isinstance (obj , ObjectReader ):
179+ obj = obj .parse_as_object ()
170180 # TODO - export glyphs
171181 if obj .m_FontData :
172182 extension = ".ttf"
@@ -177,15 +187,19 @@ def exportFont(obj: Font, fp: str, extension: str = "") -> List[Tuple[Serialized
177187 return [(obj .assets_file , obj .object_reader .path_id )]
178188
179189
180- def exportMesh (obj : Mesh , fp : str , extension = ".obj" ) -> List [Tuple [SerializedFile , int ]]:
190+ def exportMesh (obj : Union [Mesh , ObjectReader ], fp : str , extension = ".obj" ) -> List [Tuple [SerializedFile , int ]]:
191+ if isinstance (obj , ObjectReader ):
192+ obj = obj .parse_as_object ()
181193 if not extension :
182194 extension = ".obj"
183195 with open (f"{ fp } { extension } " , "wt" , encoding = "utf8" , newline = "" ) as f :
184196 f .write (obj .export ())
185197 return [(obj .assets_file , obj .object_reader .path_id )]
186198
187199
188- def exportShader (obj : Shader , fp : str , extension = ".txt" ) -> List [Tuple [SerializedFile , int ]]:
200+ def exportShader (obj : Union [Shader , ObjectReader ], fp : str , extension = ".txt" ) -> List [Tuple [SerializedFile , int ]]:
201+ if isinstance (obj , ObjectReader ):
202+ obj = obj .parse_as_object ()
189203 if not extension :
190204 extension = ".txt"
191205 with open (f"{ fp } { extension } " , "wt" , encoding = "utf8" , newline = "" ) as f :
@@ -194,39 +208,32 @@ def exportShader(obj: Shader, fp: str, extension=".txt") -> List[Tuple[Serialize
194208
195209
196210def exportMonoBehaviour (
197- obj : Union [MonoBehaviour , Object ], fp : str , extension : str = ""
211+ obj : Union [MonoBehaviour , ObjectReader ], fp : str , extension : str = ""
198212) -> List [Tuple [SerializedFile , int ]]:
199- export = None
200-
201- if obj .object_reader .serialized_type .node :
202- # a typetree is available from the SerializedFile for this object
203- export = obj .object_reader .read_typetree ()
204- elif isinstance (obj , MonoBehaviour ):
205- # try to get the typetree from the MonoBehavior script
206- script_ptr = obj .m_Script
207- if script_ptr :
208- # looks like we have a script
209- script = script_ptr .read ()
210- # check if there is a locally stored typetree for it
211- nodes = MONOBEHAVIOUR_TYPETREES .get (script .m_AssemblyName , {}).get (script .m_ClassName , None )
212- if nodes :
213- export = obj .object_reader .read_typetree (nodes )
214- else :
215- export = obj .object_reader .read_typetree ()
213+ reader = obj .object_reader if isinstance (obj , MonoBehaviour ) else obj
216214
217- if not export :
218- extension = ".bin"
219- export = obj .object_reader .raw_data
220- else :
215+ try :
216+ export = reader .parse_as_dict ()
221217 extension = ".json"
222218 export = json .dumps (export , indent = 4 , ensure_ascii = False ).encode ("utf8" , errors = "surrogateescape" )
219+ except Exception :
220+ extension = ".bin"
221+ export = reader .get_raw_data ()
223222 with open (f"{ fp } { extension } " , "wb" ) as f :
224223 f .write (export )
225- return [(obj .assets_file , obj .object_reader . path_id )]
224+ return [(obj .assets_file , obj .path_id )]
226225
227226
228- def exportAudioClip (obj : AudioClip , fp : str , extension : str = "" ) -> List [Tuple [SerializedFile , int ]]:
229- samples = obj .samples
227+ def exportAudioClip (
228+ obj : Union [AudioClip , ObjectReader ], fp : str , extension : str = ""
229+ ) -> List [Tuple [SerializedFile , int ]]:
230+ if isinstance (obj , ObjectReader ):
231+ clip = obj .parse_as_object ()
232+ else :
233+ clip = obj
234+ obj = clip .object_reader
235+
236+ samples = clip .samples
230237 if len (samples ) == 0 :
231238 pass
232239 elif len (samples ) == 1 :
@@ -237,25 +244,35 @@ def exportAudioClip(obj: AudioClip, fp: str, extension: str = "") -> List[Tuple[
237244 for name , clip_data in samples .items ():
238245 with open (os .path .join (fp , f"{ name } .wav" ), "wb" ) as f :
239246 f .write (clip_data )
240- return [(obj .assets_file , obj .object_reader . path_id )]
247+ return [(obj .assets_file , obj .path_id )]
241248
242249
243- def exportSprite (obj : Sprite , fp : str , extension : str = ".png" ) -> List [Tuple [SerializedFile , int ]]:
244- if not extension :
245- extension = ".png"
246- obj .image .save (f"{ fp } { extension } " )
250+ def exportSprite (
251+ obj : Union [Sprite , ObjectReader ], fp : str , extension : str = ".png"
252+ ) -> List [Tuple [SerializedFile , int ]]:
253+ if isinstance (obj , ObjectReader ):
254+ sprite = obj .parse_as_object ()
255+ else :
256+ sprite = obj
257+ obj = sprite .object_reader
258+
259+ sprite .image .save (f"{ fp } { extension } " )
247260 exported = [
248- (obj .assets_file , obj .object_reader . path_id ),
249- (obj .m_RD .texture .assetsfile , obj .m_RD .texture .path_id ),
261+ (obj .assets_file , obj .path_id ),
262+ (sprite .m_RD .texture .assetsfile , sprite .m_RD .texture .path_id ),
250263 ]
251- alpha_assets_file = getattr (obj .m_RD .alphaTexture , "assets_file" , None )
252- alpha_path_id = getattr (obj .m_RD .alphaTexture , "path_id" , None )
264+ alpha_assets_file = getattr (sprite .m_RD .alphaTexture , "assets_file" , None )
265+ alpha_path_id = getattr (sprite .m_RD .alphaTexture , "path_id" , None )
253266 if alpha_path_id and alpha_assets_file :
254267 exported .append ((alpha_assets_file , alpha_path_id ))
255268 return exported
256269
257270
258- def exportTexture2D (obj : Texture2D , fp : str , extension : str = ".png" ) -> List [Tuple [SerializedFile , int ]]:
271+ def exportTexture2D (
272+ obj : Union [Texture2D , ObjectReader ], fp : str , extension : str = ".png"
273+ ) -> List [Tuple [SerializedFile , int ]]:
274+ if isinstance (obj , ObjectReader ):
275+ obj = obj .parse_as_object ()
259276 if not extension :
260277 extension = ".png"
261278 if obj .m_Width :
@@ -264,7 +281,11 @@ def exportTexture2D(obj: Texture2D, fp: str, extension: str = ".png") -> List[Tu
264281 return [(obj .assets_file , obj .object_reader .path_id )]
265282
266283
267- def exportGameObject (obj : GameObject , fp : str , extension : str = "" ) -> List [Tuple [SerializedFile , int ]]:
284+ def exportGameObject (
285+ obj : Union [GameObject , ObjectReader ], fp : str , extension : str = ""
286+ ) -> List [Tuple [SerializedFile , int ]]:
287+ if isinstance (obj , ObjectReader ):
288+ obj = obj .parse_as_object ()
268289 exported = [(obj .assets_file , obj .object_reader .path_id )]
269290 refs = crawl_obj (obj )
270291 if refs :
@@ -303,34 +324,34 @@ def exportGameObject(obj: GameObject, fp: str, extension: str = "") -> List[Tupl
303324MONOBEHAVIOUR_TYPETREES : Dict [ASSEMBLY_NAME_DLL , Dict [CLASS_NAME , List [Dict ]]] = {}
304325
305326
306- def crawl_obj (obj : Object , ret : Optional [dict ] = None ) -> Dict [int , Union [Object , PPtr ]]:
327+ def crawl_obj (obj : Union [ Object , ObjectReader , PPtr ] , ret : Optional [dict ] = None ) -> Dict [int , Union [Object , PPtr ]]:
307328 """Crawls through the data struture of the object
308329 and returns a list of all the components.
309330 """
310331 if not ret :
311332 ret = {}
312333
334+ values : Sequence
313335 if isinstance (obj , PPtr ):
314- if obj .path_id == 0 and obj .file_id == 0 and obj .index == - 2 :
336+ if obj .m_PathID == 0 and obj .m_FileID == 0 and obj .m_Index == - 2 :
315337 return ret
316338 try :
317- obj = obj .read ()
339+ instance = obj .deref_parse_as_dict ()
340+ values = instance .values ()
318341 except AttributeError :
319342 return ret
343+ elif isinstance (obj , Object ):
344+ values = obj .__dict__ .values ()
345+ elif isinstance (obj , ObjectReader ):
346+ values = obj .parse_as_dict ().values ()
320347 else :
321348 return ret
322- ret [obj .path_id ] = obj
323349
324- # MonoBehaviour really on their typetree
325- # while Object denotes that the class of the object isn't implemented yet
326- if isinstance (obj , (MonoBehaviour , Object )):
327- data = obj .read_typetree ().__dict__ .values ()
328- else :
329- data = obj .__dict__ .values ()
350+ ret [obj .m_PathID ] = obj
330351
331- for value in flatten (data ):
352+ for value in flatten (values ):
332353 if isinstance (value , (Object , PPtr )):
333- if value .path_id in ret :
354+ if value .m_PathID in ret :
334355 continue
335356 crawl_obj (value , ret )
336357
0 commit comments