4242 "string" : "str" ,
4343 "TypelessData" : "bytes" ,
4444 # -- Extra
45+ "Array" : "List[object]" ,
4546 "Byte[]" : "bytes" ,
4647 "Byte" : "int" ,
4748 "String" : "str" ,
@@ -264,7 +265,7 @@ def declare_field(name: str, type: str, org_type: str = None):
264265def topsort (graph : dict ):
265266 # Sort the keys in topological order
266267 # We don't assume the guarantee otherwise
267- graph = {k : list (sorted (v )) for k , v in graph .items ()}
268+ graph = {k : list (sorted (( i for i in v if i != k ) )) for k , v in graph .items ()}
268269 vis = defaultdict (lambda : 0 )
269270 topo = list ()
270271
@@ -325,7 +326,6 @@ def emit_line(*lines: str):
325326 clazzes = list ()
326327
327328 logger .info (f"Subpass 2: Generating code for { namespace } " )
328- dp = defaultdict (lambda : - 1 )
329329 for clazz in topo :
330330 fullname = f"{ namespace } .{ clazz } " if namespace else clazz
331331 fields = classname_nodes .get (clazz , None )
@@ -334,8 +334,6 @@ def emit_line(*lines: str):
334334 f"Class { clazz } has no fields defined in TypeTree dump, skipped"
335335 )
336336 continue
337- # Heuristic: If there is a lvl1 field, it's a subclass
338- lvl1 = list (filter (lambda field : field .m_Level == 1 , fields ))
339337 clazz = translate_name (clazz )
340338 clazzes .append (clazz )
341339 clazz_fields = list ()
@@ -347,42 +345,20 @@ def __encoder(obj):
347345
348346 clazz_typetree = json .dumps (fields , default = __encoder )
349347 emit_line (f"@UTTCGen('{ fullname } ', { clazz_typetree } )" )
350- if lvl1 :
351- parent = translate_type (fields [0 ].m_Type , strip = True , fallback = False )
352- emit_line (f"class { translate_name (clazz )} ({ translate_name (parent )} ):" )
353- if dp [parent ] == - 1 :
354- # Reuse parent's fields with best possible effort
355- if pa_dep1 := getattr (UnityBuiltin , parent , None ):
356- dp [parent ] = len (pa_dep1 .__annotations__ )
357- else :
358- raise ValueError # XXX: Should NEVER happen
359- pa_dep1 = dp [parent ]
360- cur_dep1 = pa_dep1
361- for dep , (i , field ) in enumerate (
362- filter (lambda field : field [1 ].m_Level == 1 , enumerate (fields ))
363- ):
364- if dep < pa_dep1 :
365- # Skip parent fields at lvl1
366- continue
367- if i + 1 < len (fields ) and fields [i + 1 ].m_Type == "Array" :
368- field .m_Type = fields [i + 3 ].m_Type + "[]"
369- name , type = field .m_Name , translate_type (
370- field .m_Type , typenames = classname_nodes | import_defs
371- )
372- emit_line (f"\t { declare_field (name , type , field .m_Type )} " )
373- clazz_fields .append ((name , type , field .m_Type ))
374- cur_dep1 += 1
375- dp [clazz ] = cur_dep1
376- else :
377- # No inheritance
378- emit_line (f"class { clazz } :" )
379- for field in fields :
380- name , type = field .m_Name , translate_type (
381- field .m_Type , typenames = classname_nodes | import_defs
382- )
383- emit_line (f"\t { declare_field (name , type , field .m_Type )} " )
384- clazz_fields .append ((name , type ))
385- dp [clazz ] = len (fields )
348+ emit_line (f"class { clazz } :" )
349+ for field in fields :
350+ if field .m_Type == clazz :
351+ continue
352+ if field .m_Level > 1 :
353+ # Nested type. We should have already defined it
354+ # with type hints
355+ continue
356+ name , type = field .m_Name , translate_type (
357+ field .m_Type , typenames = classname_nodes | import_defs
358+ )
359+ emit_line (f"\t { declare_field (name , type , field .m_Type )} " )
360+ clazz_fields .append ((name , type ))
361+
386362 if not clazz_fields :
387363 # Empty class. Consider MRO
388364 emit_line ("\t pass" )
@@ -464,7 +440,7 @@ def __main__():
464440 parser .add_argument (
465441 "--backend" ,
466442 help = "Backend to use for code generation" ,
467- default = "AssetRipper " ,
443+ default = "AssetStudio " ,
468444 )
469445 parser .add_argument (
470446 "--filter" ,
@@ -476,8 +452,8 @@ def __main__():
476452 help = "[JSON] Load tree dump in json format {str[fullname]: List[TypeTreeNode]},..." ,
477453 )
478454 parser .add_argument (
479- "--asm" ,
480- help = "[Asm] Load typetree dump from game assembly DLL" ,
455+ "--asm-dir " ,
456+ help = "[Asm] Load typetree dump from game assembly DLL folder " ,
481457 type = str
482458 )
483459 parser .add_argument (
@@ -508,12 +484,23 @@ def __main__():
508484 gen = TypeTreeGenerator (args .unity_version , args .backend )
509485 def populate_gen ():
510486 # https://github.com/UnityPy-Org/TypeTreeGeneratorAPI/pull/1
511- for asm ,clz in gen .get_monobehavior_definitions ():
512- node = gen .get_nodes_as_json (asm , clz )
513- pass
514- if args .asm :
515- print ("Loading .NET Assembly" , args .asm )
516- gen .load_dll (args .asm )
487+ for asm ,clz in gen .get_class_definitions ():
488+ try :
489+ node = gen .get_nodes_as_json (asm , clz )
490+ node = json .loads (node )
491+ typetree [clz ] = node
492+ except Exception as e :
493+ logger .warning (f"Skipping nodes for { asm } .{ clz } : { e } " )
494+ if args .asm_dir :
495+ print ("Loading .NET Assemblies" , args .asm_dir )
496+ for dll in os .listdir (args .asm_dir ):
497+ if not dll .lower ().endswith (".dll" ):
498+ continue
499+ try :
500+ print (f"Loading { dll } " )
501+ gen .load_dll (open (os .path .join (args .asm_dir , dll ), "rb" ).read ())
502+ except Exception as e :
503+ logger .warning (f"Skipping { dll } : { e } " )
517504 populate_gen ()
518505 elif args .il2cpp and args .metadata :
519506 print ("Loading IL2CPP" , args .il2cpp , args .metadata )
@@ -526,6 +513,8 @@ def populate_gen():
526513 else :
527514 raise ValueError ("No valid input source specified." )
528515 if typetree :
516+ with open (".typetree.json" , "w" ) as f :
517+ json .dump (typetree , f , indent = 4 )
529518 regex = re .compile (args .filter )
530519 typetree = {k : v for k , v in typetree .items () if regex .match (k )}
531520 process_typetree (typetree , args .outdir )
0 commit comments