|
| 1 | +// credit: https://github.com/AhmedAhmedEG/Unity-Type-Tree-Generator/blob/main/UnityTypeTreeGeneratorCLI/CLI.cs |
| 2 | +// changes: |
| 3 | +// - assemblyMap -> monoTemplateFieldsGenerator.loadedAssemblies |
| 4 | +// - collpase GenerateTypeTreeNode into TypeTemplateToTypeTreeNodes |
| 5 | +using AssetsTools.NET; |
| 6 | +using AssetsTools.NET.Extra; |
| 7 | +using Mono.Cecil; |
| 8 | +using TypeTreeNode = AssetStudio.TypeTreeNode; |
| 9 | + |
| 10 | +namespace TypeTreeGeneratorAPI |
| 11 | +{ |
| 12 | + internal class TypeTreeGenerator_AssetsTools : TypeTreeGenerator |
| 13 | + { |
| 14 | + private readonly UnityVersion unityVersionExtra; |
| 15 | + private readonly MonoCecilTempGenerator monoTemplateFieldsGenerator = new(""); |
| 16 | + private ClassDatabaseFile classDatabase = new(); |
| 17 | + |
| 18 | + public TypeTreeGenerator_AssetsTools(string unityVersion) : base(unityVersion) |
| 19 | + { |
| 20 | + unityVersionExtra = new UnityVersion(unityVersionString); |
| 21 | + } |
| 22 | + |
| 23 | + ~TypeTreeGenerator_AssetsTools() |
| 24 | + { |
| 25 | + monoTemplateFieldsGenerator.Dispose(); |
| 26 | + } |
| 27 | + |
| 28 | + public void InitClassDatabase(Stream stream) |
| 29 | + { |
| 30 | + var classPackage = new ClassPackageFile(); |
| 31 | + classPackage.Read(new AssetsFileReader(stream)); |
| 32 | + |
| 33 | + classDatabase = classPackage.GetClassDatabase(this.unityVersionString); |
| 34 | + } |
| 35 | + |
| 36 | + public override List<TypeTreeNode>? GenerateTreeNodes(string assemblyName, string fullName) |
| 37 | + { |
| 38 | + var asm = monoTemplateFieldsGenerator.loadedAssemblies[assemblyName]; |
| 39 | + var type = asm.MainModule.Types.First(t => t.Name == fullName); |
| 40 | + |
| 41 | + var typeTemplates = GenerateTypeTemplates(asm, type); |
| 42 | + if (typeTemplates.Any()) |
| 43 | + { |
| 44 | + return GenerateTypeTreeNodes(typeTemplates); |
| 45 | + } |
| 46 | + return null; |
| 47 | + } |
| 48 | + |
| 49 | + public override List<Tuple<string, string>> GetMonoBehaviourDefinitions() |
| 50 | + { |
| 51 | + var monoBehaviourDefs = new List<Tuple<string, string>>(); |
| 52 | + foreach (var (asmName, asmDef) in monoTemplateFieldsGenerator.loadedAssemblies) |
| 53 | + { |
| 54 | + foreach (var type in asmDef.MainModule.Types) |
| 55 | + { |
| 56 | + if (IsMonoBehaviour(type)) |
| 57 | + { |
| 58 | + monoBehaviourDefs.Add(new Tuple<string, string>(asmName, type.FullName)); |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + return monoBehaviourDefs; |
| 63 | + } |
| 64 | + |
| 65 | + public override void LoadDLL(Stream dllStream) |
| 66 | + { |
| 67 | + var assembly = AssemblyDefinition.ReadAssembly(dllStream, readerParameters); |
| 68 | + resolver.Register(assembly); |
| 69 | + monoTemplateFieldsGenerator.loadedAssemblies.Add(assembly.MainModule.Name, assembly); |
| 70 | + } |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | + /* Returns a template field for a given type using the class database, It's essential for unity base types |
| 75 | + like MonoBehavior and GameObject, MonoCecilTempGenerator will never return template fields for unity base types. */ |
| 76 | + private AssetTypeTemplateField? QueryClassDatabase(TypeDefinition type) |
| 77 | + { |
| 78 | + var typeTemplate = new AssetTypeTemplateField(); |
| 79 | + var classDatabaseType = classDatabase.FindAssetClassByName(type.Name); |
| 80 | + |
| 81 | + if (classDatabaseType is null || (classDatabaseType.EditorRootNode == null && classDatabaseType.ReleaseRootNode == null)) return null; |
| 82 | + typeTemplate.FromClassDatabase(classDatabase, classDatabaseType); |
| 83 | + |
| 84 | + return typeTemplate; |
| 85 | + } |
| 86 | + |
| 87 | + |
| 88 | + /* Returns a list of type templates for a given type utilizing the class database for unity base types, and |
| 89 | + MonoCecilTempGenerator for the type it self, it returns an empty list if there was no type templates returned |
| 90 | + from any of the unity base types. */ |
| 91 | + private List<AssetTypeTemplateField> GenerateTypeTemplates(AssemblyDefinition asm, TypeDefinition type) |
| 92 | + { |
| 93 | + var typeTemplates = new List<AssetTypeTemplateField>(); |
| 94 | + var generatorTypeTemplates = monoTemplateFieldsGenerator.Read(asm, |
| 95 | + type.Namespace, |
| 96 | + type.Name, |
| 97 | + this.unityVersionExtra); |
| 98 | + |
| 99 | + // If the generator resulted in nothing, this means this type is not serializable in the first place, so ignore it. |
| 100 | + if (generatorTypeTemplates.Count == 0) return typeTemplates; |
| 101 | + |
| 102 | + var baseTypes = GetBaseTypes(type); |
| 103 | + foreach (var typeTemplate in baseTypes.Select(QueryClassDatabase)) |
| 104 | + { |
| 105 | + // template fields of type component are not needed for deserialization. |
| 106 | + if (typeTemplate is null || typeTemplate.Type == "Component") continue; |
| 107 | + typeTemplates.Add(typeTemplate); |
| 108 | + |
| 109 | + /* If the current template field type was one of those, then break as the generator recursively gets the |
| 110 | + rest of the base types internally, thus we prevent possible duplicates */ |
| 111 | + if (typeTemplate.Type is "MonoBehaviour" or "ScriptableObject") |
| 112 | + break; |
| 113 | + } |
| 114 | + |
| 115 | + /* If there's not a single type template for any of the base types, it means this type is not supported, thus, |
| 116 | + do not generate type templates for the type it self and return an empty list. */ |
| 117 | + if (!typeTemplates.Any()) return typeTemplates; |
| 118 | + |
| 119 | + typeTemplates.AddRange(generatorTypeTemplates); |
| 120 | + return typeTemplates; |
| 121 | + } |
| 122 | + |
| 123 | + // Returns a list of type tree nodes given a list of type templates. |
| 124 | + private static List<TypeTreeNode> GenerateTypeTreeNodes(List<AssetTypeTemplateField> typeTemplates) |
| 125 | + { |
| 126 | + var typeTreeNodes = new List<TypeTreeNode>(); |
| 127 | + if (!typeTemplates.Any()) return typeTreeNodes; |
| 128 | + |
| 129 | + typeTreeNodes.AddRange(TypeTemplateToTypeTreeNodes(typeTemplates[0], level: 0)); |
| 130 | + |
| 131 | + for (int i = 1; i < typeTemplates.Count; i++) |
| 132 | + typeTreeNodes.AddRange(TypeTemplateToTypeTreeNodes(typeTemplates[i], level: 1)); |
| 133 | + |
| 134 | + return typeTreeNodes; |
| 135 | + } |
| 136 | + |
| 137 | + // Recursive function that returns a list of type tree nodes for a type template including it's child type templates too. |
| 138 | + private static List<TypeTreeNode> TypeTemplateToTypeTreeNodes(AssetTypeTemplateField typeTemplate, int level = 0) |
| 139 | + { |
| 140 | + var typeTreeNodes = new List<TypeTreeNode> { |
| 141 | + new TypeTreeNode(typeTemplate.Type, typeTemplate.Name, level, typeTemplate.IsAligned) |
| 142 | + }; |
| 143 | + |
| 144 | + foreach (var child in typeTemplate.Children) |
| 145 | + typeTreeNodes.AddRange(TypeTemplateToTypeTreeNodes(child, level + 1)); |
| 146 | + |
| 147 | + return typeTreeNodes; |
| 148 | + } |
| 149 | + |
| 150 | + // Returns a list of the base types for a given type, ordered from top type to bottom type. |
| 151 | + private static List<TypeDefinition> GetBaseTypes(TypeDefinition type) |
| 152 | + { |
| 153 | + var baseTypes = new List<TypeDefinition>(); |
| 154 | + if (type.BaseType is null) return baseTypes; |
| 155 | + |
| 156 | + var baseType = type.BaseType.Resolve(); |
| 157 | + |
| 158 | + baseTypes.Insert(0, baseType); |
| 159 | + baseTypes.InsertRange(0, GetBaseTypes(baseType)); |
| 160 | + |
| 161 | + return baseTypes; |
| 162 | + } |
| 163 | + } |
| 164 | +} |
0 commit comments