Skip to content

Commit 14f2134

Browse files
committed
chore: use new objreader syntax
1 parent d64b6c6 commit 14f2134

5 files changed

Lines changed: 110 additions & 85 deletions

File tree

UnityPy/export/MeshRendererExporter.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ class Renderer(Renderer):
2525
def get_mesh(meshR: Renderer) -> Optional[Mesh]:
2626
if isinstance(meshR, SkinnedMeshRenderer):
2727
if meshR.m_Mesh:
28-
return meshR.m_Mesh.read()
28+
return meshR.m_Mesh.deref_parse_as_object()
2929
else:
30-
m_GameObject = meshR.m_GameObject.read()
30+
m_GameObject = meshR.m_GameObject.deref_parse_as_object()
3131
for comp in m_GameObject.m_Component:
3232
if isinstance(comp, tuple):
3333
pptr = comp[1]
@@ -39,9 +39,9 @@ def get_mesh(meshR: Renderer) -> Optional[Mesh]:
3939
if not obj:
4040
continue
4141
if obj.type.name == "MeshFilter":
42-
filter: MeshFilter = pptr.read()
42+
filter: MeshFilter = pptr.deref_parse_as_object()
4343
if filter.m_Mesh:
44-
return filter.m_Mesh.read()
44+
return filter.m_Mesh.deref_parse_as_object()
4545
return None
4646

4747

@@ -66,7 +66,7 @@ def export_mesh_renderer(renderer: Renderer, export_dir: str) -> None:
6666
continue
6767
matPtr: Optional[PPtr[Material]] = renderer.m_Materials[i - firstSubMesh]
6868
if matPtr:
69-
mat: Material = matPtr.read()
69+
mat: Material = matPtr.deref_parse_as_object()
7070
else:
7171
material_names.append(None)
7272
continue
@@ -79,10 +79,10 @@ def export_mesh_renderer(renderer: Renderer, export_dir: str) -> None:
7979
if not isinstance(key, str):
8080
# FastPropertyName
8181
key = key.name
82-
tex: Texture2D = texEnv.m_Texture.read()
82+
tex: Texture2D = texEnv.m_Texture.deref_parse_as_object()
8383
texName = f"{tex.m_Name if tex.m_Name else key}.png"
8484
with env.fs.open(env.fs.sep.join([export_dir, texName]), "wb") as f:
85-
tex.read().image.save(f)
85+
tex.image.save(f)
8686

8787
# save .obj
8888
with env.fs.open(
@@ -172,7 +172,7 @@ def properties_to_dict(properties):
172172
# FastPropertyName
173173
key = key.name
174174

175-
tex: Texture2D = texEnv.m_Texture.read()
175+
tex: Texture2D = texEnv.m_Texture.deref_parse_as_object()
176176
texName = f"{tex.m_Name if tex.m_Name else key}.png"
177177
if key == "_MainTex":
178178
sb.append(f"map_Kd {texName}")

UnityPy/export/SpriteHelper.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,29 +47,30 @@ def get_image(sprite: Sprite, texture: PPtr[Texture2D], alpha_texture: Optional[
4747
if alpha_texture:
4848
cache_id = (texture.path_id, alpha_texture.path_id)
4949
if cache_id not in cache:
50-
original_image = get_image_from_texture2d(texture.read(), False)
51-
alpha_image = get_image_from_texture2d(alpha_texture.read(), False)
50+
original_image = get_image_from_texture2d(texture.deref_parse_as_object(), False)
51+
alpha_image = get_image_from_texture2d(alpha_texture.deref_parse_as_object(), False)
5252
original_image = Image.merge("RGBA", (*original_image.split()[:3], alpha_image.split()[0]))
5353
cache[cache_id] = original_image
5454
else:
5555
cache_id = texture.path_id
5656
if cache_id not in cache:
57-
original_image = get_image_from_texture2d(texture.read(), False)
57+
original_image = get_image_from_texture2d(texture.deref_parse_as_object(), False)
5858
cache[cache_id] = original_image
5959
return cache[cache_id]
6060

6161

6262
def get_image_from_sprite(m_Sprite: Sprite) -> Image.Image:
6363
atlas = None
6464
if m_Sprite.m_SpriteAtlas:
65-
atlas = m_Sprite.m_SpriteAtlas.read()
65+
atlas = m_Sprite.m_SpriteAtlas.deref_parse_as_object()
6666
elif m_Sprite.m_AtlasTags:
6767
# looks like the direct pointer is empty, let's try to find the Atlas via its name
6868
assert m_Sprite.assets_file, "Sprite assets file is not set!"
6969
for obj in m_Sprite.assets_file.objects.values():
7070
if obj.type == ClassIDType.SpriteAtlas:
71-
atlas = obj.read()
72-
if atlas.m_Name == m_Sprite.m_AtlasTags[0]:
71+
name = obj.peek_name()
72+
if name == m_Sprite.m_AtlasTags[0]:
73+
atlas = obj.parse_as_object()
7374
break
7475
atlas = None
7576

UnityPy/files/SerializedFile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def __init__(self, reader: EndianBinaryReader, parent=None, name=None, **kwargs)
315315
# read the asset_bundles to get the containers
316316
for obj in self.objects.values():
317317
if obj.type == ClassIDType.AssetBundle:
318-
self.assetbundle = obj.read()
318+
self.assetbundle = obj.parse_as_object()
319319
self._container = ContainerHelper(self.assetbundle)
320320
break
321321
else:

UnityPy/tools/extractor.py

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
Texture2D,
2020
)
2121
from UnityPy.enums.ClassIDType import ClassIDType
22-
from UnityPy.files import SerializedFile
22+
from UnityPy.files import ObjectReader, SerializedFile
2323

2424

2525
def 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

7781
def 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

196210
def 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
303324
MONOBEHAVIOUR_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

Comments
 (0)