6868 "from UnityPy.classes.math import (ColorRGBA, Matrix3x4f, Matrix4x4f, Quaternionf, Vector2f, Vector3f, Vector4f, float3, float4,)" ,
6969 '''T = TypeVar("T")
7070UTTCG_Classes = dict()
71- def UTTCGen(fullname: str):
71+ def UTTCGen(fullname: str, typetree: dict ):
7272 """dataclass-like decorator for typetree classess with nested type support
7373
7474 limitations:
@@ -77,85 +77,56 @@ def UTTCGen(fullname: str):
7777 - generally supports nested types, however untested and could be slow
7878 - and ofc, zero type checking and safeguards :/
7979 """
80+ REFERENCED_ARGS = {'object_reader'}
8081 def __inner(clazz: T) -> T:
81- RESERVED_KWS = {'object_reader'}
8282 # Allow these to be propogated to the props
83- def __init__(self, **d):
84- def enusre_reserved(obj):
85- for k in RESERVED_KWS:
86- if hasattr(obj, k) and k in d:
87- setattr(obj, k, d[k])
88- return obj
83+ def __init__(self, **d):
8984 def reduce_init(clazz, **d):
9085 types : dict = clazz.__annotations__
9186 for k, sub in types.items():
9287 if type(sub) == str:
9388 sub = eval(sub) # attrs turns these into strings...why?
89+ while sub.__name__ == "Optional":
90+ sub = sub.__args__[0] # Reduce Optional[T] -> T
9491 reduce_arg = getattr(sub, "__args__", [None])[0]
92+ if k in REFERENCED_ARGS: # Directly refcounted
93+ reduce_arg = sub = lambda x: x
9594 if isinstance(d[k], list):
9695 if hasattr(reduce_arg, "__annotations__"):
97- setattr(self, k, [enusre_reserved( reduce_arg(**x) ) for x in d[k]])
96+ setattr(self, k, [reduce_arg(**x) for x in d[k]])
9897 else:
99- setattr(self, k, [enusre_reserved( reduce_arg(x) ) for x in d[k]])
98+ setattr(self, k, [reduce_arg(x) for x in d[k]])
10099 elif isinstance(d[k], dict) and hasattr(sub, "__annotations__"):
101- setattr(self, k, enusre_reserved( sub(**d[k]) ))
100+ setattr(self, k, sub(**d[k]))
102101 else:
103102 if isinstance(d[k], dict):
104- setattr(self, k, enusre_reserved( sub(**d[k]) ))
103+ setattr(self, k, sub(**d[k]))
105104 else:
106- setattr(self, k, enusre_reserved(sub(d[k])))
107- for __base__ in clazz.__bases__:
108- types : dict = __base__.__annotations__
109- args = {k:d[k] for k in types if k in d}
110- if len(args) == len(types):
111- super(clazz, self).__init__(**args)
112- reduce_init(__base__, **d)
113- for k in args:
114- if not k in RESERVED_KWS: del d[k]
115- reduce_init(clazz, **d)
116- enusre_reserved(self)
105+ setattr(self, k, sub(d[k]))
106+ def reduce_base(clazz, **d):
107+ for __base__ in clazz.__bases__:
108+ if hasattr(__base__, "__annotations__"):
109+ types : dict = __base__.__annotations__
110+ args = {k:d[k] for k in types if k in d}
111+ if len(args) == len(types):
112+ super(clazz, self).__init__(**args)
113+ reduce_init(__base__, **d)
114+ reduce_base(__base__, **d)
115+ reduce_base(clazz, **d)
116+ reduce_init(clazz, **d)
117117 def __repr__(self) -> str:
118118 return f"{clazz.__name__}({', '.join([f'{k}={getattr(self, k)!r}' for k in self.__annotations__])})"
119+ def __save(self):
120+ self.object_reader.save_typetree(self, self.__typetree__)
119121 clazz.__init__ = __init__
120122 clazz.__repr__ = __repr__
123+ clazz.__typetree__ = typetree
124+ clazz.save = __save
121125 UTTCG_Classes[fullname] = clazz
122126 return clazz
123127 return __inner
124128
125129# Helper functions
126- def UTTCGen_Reread(src: MonoBehaviour | ObjectReader, fullname: str = None) -> Tuple[dict, str]:
127- """Rereads the typetree data from the MonoBehaviour instance with our typetree definition.
128-
129- Args:
130- src: The MonoBehaviour instance or ObjectReader to read from.
131- fullname: The full name of the class to read. If None, it will be read from the MonoBehaviour instance's `m_Script` property.
132-
133- Returns:
134- Tuple[dict, str]: [raw_def, fullname] where `raw_def` is the raw data read from the MonoBehaviour instance and `fullname` is the full name of the class.
135- """
136- if not fullname and isinstance(src, MonoBehaviour):
137- script = src.m_Script.read()
138- fullname = script.m_ClassName
139- if script.m_Namespace:
140- fullname = f"{script.m_Namespace}.{fullname}"
141- definition = UTTCG_Typetrees.get(fullname, None)
142- assert definition is not None, f"Typetree definition for {fullname} not found"
143- if isinstance(src, MonoBehaviour):
144- src = src.object_reader
145- raw_def = src.read_typetree(definition, check_read=False)
146- return raw_def, fullname
147-
148- def UTTCGen_Instantiate(raw_def : dict, fullname: str):
149- """Instantiate a class from the typetree definition and the raw data.
150-
151- Args:
152- raw_def (dict): The raw data read from the MonoBehaviour instance.
153- fullname (str): The full name of the class to instantiate.
154- """
155- clazz = UTTCG_Classes.get(fullname, None)
156- assert clazz is not None, f"Class definition for {fullname} not found"
157- instance = clazz(**raw_def)
158- return instance
159130
160131def UTTCGen_AsInstance(src: MonoBehaviour | ObjectReader, fullname: str = None) -> T:
161132 """Instantiate a class from the typetree definition and the raw data.
@@ -170,8 +141,17 @@ def UTTCGen_AsInstance(src: MonoBehaviour | ObjectReader, fullname: str = None)
170141 Returns:
171142 T: An instance of the class defined by the typetree.
172143 """
173- raw_def, fullname = UTTCGen_Reread(src, fullname)
174- instance = UTTCGen_Instantiate(raw_def, fullname)
144+ if not fullname and isinstance(src, MonoBehaviour):
145+ script = src.m_Script.read()
146+ fullname = script.m_ClassName
147+ if script.m_Namespace:
148+ fullname = f"{script.m_Namespace}.{fullname}"
149+ clazz = UTTCG_Classes.get(fullname, None)
150+ assert clazz is not None, f"Class definition for {fullname} not found"
151+ if isinstance(src, MonoBehaviour):
152+ src = src.object_reader
153+ raw_def = src.read_typetree(clazz.__typetree__, check_read=False)
154+ instance = clazz(object_reader=src, **raw_def)
175155 return instance
176156''' ,
177157 ]
@@ -348,7 +328,14 @@ def emit_line(*lines: str):
348328 clazz = translate_name (clazz )
349329 clazzes .append (clazz )
350330 clazz_fields = list ()
351- emit_line (f"@UTTCGen('{ fullname } ')" )
331+
332+ def __encoder (obj ):
333+ if isinstance (obj , TypeTreeNode ):
334+ return obj .__dict__
335+ return obj
336+
337+ clazz_typetree = json .dumps (fields , default = __encoder )
338+ emit_line (f"@UTTCGen('{ fullname } ', { clazz_typetree } )" )
352339 if lvl1 :
353340 parent = translate_type (fields [0 ].m_Type , strip = True , fallback = False )
354341 emit_line (f"class { clazz } ({ parent } ):" )
@@ -454,15 +441,6 @@ def __open(fname: str):
454441 f = __open ("__init__.py" )
455442 process_namespace (f , classname_nodes , namespace )
456443
457- def __encoder (obj ):
458- if isinstance (obj , TypeTreeNode ):
459- return obj .__dict__
460- return obj
461-
462- __open ("__init__.py" ).write (
463- "\n UTTCG_Typetrees = " + json .dumps (fullname_nodes , indent = 4 , default = __encoder )
464- )
465-
466444
467445import re , logging
468446from UnityPy .helpers .TypeTreeGenerator import TypeTreeGenerator
0 commit comments