Skip to content

Commit 4467d52

Browse files
committed
Version 0.0.3
Patch instance saving
1 parent 15bab7a commit 4467d52

File tree

3 files changed

+49
-70
lines changed

3 files changed

+49
-70
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Used in
1212
## Usage
1313
Example usage in `rla_transport_defines`
1414
```python
15+
env = UnityPy.load(...)
1516
from generated.Sekai.Streaming import TransportDefine
1617
from generated import UTTCGen_AsInstance
1718

@@ -25,5 +26,5 @@ for reader in filter(lambda x: x.type == ClassIDType.MonoBehaviour, env.objects)
2526
# Possibly modify on the instance itself and saving it is also possible
2627
instance.validBones[0] = "BadBone"
2728
instance.save()
28-
29+
env.save()
2930
```

UnityPyTypetreeCodegen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = (0, 0, 2)
1+
__version__ = (0, 0, 3)

UnityPyTypetreeCodegen/__main__.py

Lines changed: 46 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"from UnityPy.classes.math import (ColorRGBA, Matrix3x4f, Matrix4x4f, Quaternionf, Vector2f, Vector3f, Vector4f, float3, float4,)",
6969
'''T = TypeVar("T")
7070
UTTCG_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
160131
def 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-
"\nUTTCG_Typetrees = " + json.dumps(fullname_nodes, indent=4, default=__encoder)
464-
)
465-
466444

467445
import re, logging
468446
from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator

0 commit comments

Comments
 (0)