5151 "Vector2" : "Vector2f" ,
5252 "Vector3" : "Vector3f" ,
5353 "Vector4" : "Vector4f" ,
54+ "Quaternion" : "Quaternionf" ,
5455}
5556# XXX: Can't use attrs here since subclassing MonoBehavior and such - though defined by the typetree dump
5657# seem to be only valid if the class isn't a property of another class
6263 "# fmt: off" ,
6364 "# Auto-generated by https://github.com/mos9527/UnityPyTypetreeCodegen" ,
6465 "" "from typing import List, Union, Optional, TypeVar" ,
66+ "from UnityPy.files.ObjectReader import ObjectReader" ,
6567 "from UnityPy.classes import *" ,
6668 "from UnityPy.classes.math import (ColorRGBA, Matrix3x4f, Matrix4x4f, Quaternionf, Vector2f, Vector3f, Vector4f, float3, float4,)" ,
6769 '''T = TypeVar("T")
68- def UTTCGen(clazz : T) -> T:
69- """dataclass-like decorator for typetree classess with nested type support
70-
71- limitations:
72- - the behavior is similar to slotted dataclasses where shared attributes are inherited
73- but allows ommiting init of the parent if kwargs are not sufficient
74- - generally supports nested types, however untested and could be slow
75- - and ofc, zero type checking and safeguards :/
76- """
77- RESERVED_KWS = {'object_reader'}
78- # Allow these to be propogated to the props
79- def __init__(self, **d):
80- def enusre_reserved(obj):
81- for k in RESERVED_KWS:
82- if hasattr(obj, k) and k in d:
83- setattr(obj, k, d[k])
84- return obj
85- def reduce_init(clazz, **d):
86- types : dict = clazz.__annotations__
87- for k, sub in types.items():
88- if type(sub) == str:
89- sub = eval(sub) # attrs turns these into strings...why?
90- reduce_arg = getattr(sub, "__args__", [None])[0]
91- if isinstance(d[k], list):
92- if hasattr(reduce_arg, "__annotations__"):
93- setattr(self, k, [enusre_reserved(reduce_arg(**x)) for x in d[k]])
94- else:
95- setattr(self, k, [enusre_reserved(reduce_arg(x)) for x in d[k]])
96- elif isinstance(d[k], dict) and hasattr(sub, "__annotations__"):
97- setattr(self, k, enusre_reserved(sub(**d[k])))
98- else:
99- if isinstance(d[k], dict):
100- setattr(self, k, enusre_reserved(sub(**d[k])))
101- else:
102- setattr(self, k, enusre_reserved(sub(d[k])))
103- for __base__ in clazz.__bases__:
104- types : dict = __base__.__annotations__
105- args = {k:d[k] for k in types if k in d}
106- if len(args) == len(types):
107- super(clazz, self).__init__(**args)
108- reduce_init(__base__, **d)
109- for k in args:
110- if not k in RESERVED_KWS: del d[k]
111- reduce_init(clazz, **d)
112- enusre_reserved(self)
113- def __repr__(self) -> str:
114- return f"{clazz.__name__}({', '.join([f'{k}={getattr(self, k)!r}' for k in self.__annotations__])})"
115- clazz.__init__ = __init__
116- clazz.__repr__ = __repr__
117- return clazz
70+ UTTCG_Classes = dict()
71+ def UTTCGen(fullname: str):
72+ """dataclass-like decorator for typetree classess with nested type support
11873
74+ limitations:
75+ - the behavior is similar to slotted dataclasses where shared attributes are inherited
76+ but allows ommiting init of the parent if kwargs are not sufficient
77+ - generally supports nested types, however untested and could be slow
78+ - and ofc, zero type checking and safeguards :/
79+ """
80+ def __inner(clazz: T) -> T:
81+ RESERVED_KWS = {'object_reader'}
82+ # 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
89+ def reduce_init(clazz, **d):
90+ types : dict = clazz.__annotations__
91+ for k, sub in types.items():
92+ if type(sub) == str:
93+ sub = eval(sub) # attrs turns these into strings...why?
94+ reduce_arg = getattr(sub, "__args__", [None])[0]
95+ if isinstance(d[k], list):
96+ if hasattr(reduce_arg, "__annotations__"):
97+ setattr(self, k, [enusre_reserved(reduce_arg(**x)) for x in d[k]])
98+ else:
99+ setattr(self, k, [enusre_reserved(reduce_arg(x)) for x in d[k]])
100+ elif isinstance(d[k], dict) and hasattr(sub, "__annotations__"):
101+ setattr(self, k, enusre_reserved(sub(**d[k])))
102+ else:
103+ if isinstance(d[k], dict):
104+ setattr(self, k, enusre_reserved(sub(**d[k])))
105+ 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)
117+ def __repr__(self) -> str:
118+ return f"{clazz.__name__}({', '.join([f'{k}={getattr(self, k)!r}' for k in self.__annotations__])})"
119+ clazz.__init__ = __init__
120+ clazz.__repr__ = __repr__
121+ UTTCG_Classes[fullname] = clazz
122+ return clazz
123+ return __inner
124+
119125# Helper functions
120- def UTTCGen_Reread(Mono: MonoBehaviour):
121- script = Mono.m_Script.read()
122- fullname = script.m_ClassName
123- if script.m_Namespace:
124- fullname = f"{script.m_Namespace}.{fullname}"
125- definition = TYPETREE_DEFS.get(fullname, None)
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)
126142 assert definition is not None, f"Typetree definition for {fullname} not found"
127- raw_def = Mono.object_reader.read_typetree(definition)
143+ if isinstance(src, MonoBehaviour):
144+ src = src.object_reader
145+ raw_def = src.read_typetree(definition, check_read=False)
128146 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
159+
160+ def UTTCGen_AsInstance(src: MonoBehaviour | ObjectReader, fullname: str = None) -> T:
161+ """Instantiate a class from the typetree definition and the raw data.
162+
163+ In most cases, this is the function you want to use.
164+ It will read the typetree data from the MonoBehaviour instance and instantiate the class with the data.
165+
166+ Args:
167+ src (MonoBehaviour | ObjectReader): The MonoBehaviour instance or ObjectReader to read from.
168+ fullname (str): The full name of the class to read. If None, it will be read from the MonoBehaviour instance's `m_Script` property.
169+
170+ Returns:
171+ T: An instance of the class defined by the typetree.
172+ """
173+ raw_def, fullname = UTTCGen_Reread(src, fullname)
174+ instance = UTTCGen_Instantiate(raw_def, fullname)
175+ return instance
129176''' ,
130177 ]
131178)
@@ -286,6 +333,7 @@ def emit_line(*lines: str):
286333 logger .info (f"Subpass 2: Generating code for { namespace } " )
287334 dp = defaultdict (lambda : - 1 )
288335 for clazz in topo :
336+ fullname = f"{ namespace } .{ clazz } " if namespace else clazz
289337 fields = classname_nodes .get (clazz , None )
290338 if not fields :
291339 logger .debug (
@@ -297,7 +345,7 @@ def emit_line(*lines: str):
297345 clazz = translate_name (clazz )
298346 clazzes .append (clazz )
299347 clazz_fields = list ()
300- emit_line (f"@UTTCGen" )
348+ emit_line (f"@UTTCGen(' { fullname } ') " )
301349 if lvl1 :
302350 parent = translate_type (fields [0 ].m_Type , strip = True , fallback = False )
303351 emit_line (f"class { clazz } ({ parent } ):" )
@@ -409,7 +457,7 @@ def __encoder(obj):
409457 return obj
410458
411459 __open ("__init__.py" ).write (
412- "\n TYPETREE_DEFS = " + json .dumps (fullname_nodes , indent = 4 , default = __encoder )
460+ "\n UTTCG_Typetrees = " + json .dumps (fullname_nodes , indent = 4 , default = __encoder )
413461 )
414462
415463
0 commit comments