forked from K0lb3/UnityPy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPPtr.py
More file actions
112 lines (85 loc) · 3.7 KB
/
PPtr.py
File metadata and controls
112 lines (85 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, cast
from attr import define
if TYPE_CHECKING:
from ..enums.ClassIDType import ClassIDType
from ..files.ObjectReader import ObjectReader
from ..files.SerializedFile import SerializedFile
T = TypeVar("T")
@define(slots=True, kw_only=True)
class PPtr(Generic[T]):
m_FileID: int
m_PathID: int
assetsfile: Optional[SerializedFile] = None
@property
def file_id(self) -> int:
# backwards compatibility
return self.m_FileID
@property
def path_id(self) -> int:
# backwards compatibility
return self.m_PathID
@property
def type(self) -> ClassIDType:
return self.deref().type
# backwards compatibility - to be removed in UnityPy 2
def read(self):
return self.deref_parse_as_object()
# backwards compatibility - to be removed in UnityPy 2
def read_typetree(self):
return self.deref_parse_as_dict()
def deref(self, assetsfile: Optional[SerializedFile] = None) -> ObjectReader[T]:
assetsfile = assetsfile or self.assetsfile
if assetsfile is None:
raise ValueError("PPtr can't deref without an assetsfile!")
if self.m_PathID == 0:
raise ValueError("PPtr can't deref with m_PathID == 0!")
assetsfile_dst = None
if self.m_FileID == 0:
assetsfile_dst = assetsfile or self.assetsfile
else:
# resolve file id to external name
external_id = self.m_FileID - 1
if external_id >= len(assetsfile.externals):
raise FileNotFoundError("Failed to resolve pointer - invalid m_FileID!")
external = assetsfile.externals[external_id]
# resolve external name to assetsfile
container = assetsfile.parent
if container is None:
# TODO - use default fs
raise FileNotFoundError(f"PPtr points to {external.path} but no container is set!")
external_clean_path = external.path
if external_clean_path.startswith("archive:/"):
external_clean_path = external_clean_path[9:]
if external_clean_path.startswith("assets/"):
external_clean_path = external_clean_path[7:]
external_clean_path = external_clean_path.rsplit("/")[-1].lower()
for key, file in container.files.items():
if key.lower() == external_clean_path:
assetsfile_dst = file
break
else:
env = assetsfile.environment
cab = env.find_file(external_clean_path)
if cab:
assetsfile_dst = cab
else:
raise FileNotFoundError(f"Failed to resolve pointer - {external.path} not found!")
if assetsfile_dst is None:
raise FileNotFoundError(f"Failed to resolve pointer - {self.m_FileID} not found!")
return cast("ObjectReader[T]", assetsfile_dst.objects[self.m_PathID])
def deref_parse_as_object(self, assetsfile: Optional[SerializedFile] = None) -> T:
return self.deref(assetsfile).parse_as_object()
def deref_parse_as_dict(self, assetsfile: Optional[SerializedFile] = None) -> dict[str, Any]:
return self.deref(assetsfile).parse_as_dict()
def __bool__(self):
return self.m_PathID != 0
def __hash__(self) -> int:
return hash((self.m_FileID, self.m_PathID))
def __eq__(self, other: object) -> bool:
if not isinstance(other, PPtr):
return False
return self.m_FileID == other.m_FileID and self.m_PathID == other.m_PathID
__all__ = [
"PPtr",
]