From 8a930fccc0159db7a775b7981786a8a3bbfb28f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lavergne?= Date: Tue, 25 Mar 2025 23:04:15 +0100 Subject: [PATCH 1/6] First working generator --- CMakeLists.txt | 2 + csharp-api/.editorconfig | 12 + .../AssemblyGenerator/ClassGenerator.cs | 792 ++++++++---------- csharp-api/AssemblyGenerator/EnumGenerator.cs | 4 +- csharp-api/AssemblyGenerator/Generator.cs | 566 +++---------- csharp-api/REFrameworkNET/TypeDefinition.cpp | 15 +- csharp-api/REFrameworkNET/TypeDefinition.hpp | 1 + 7 files changed, 473 insertions(+), 919 deletions(-) create mode 100644 csharp-api/.editorconfig diff --git a/CMakeLists.txt b/CMakeLists.txt index 70dc6313a..0291fc159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ project(reframework CSharp ) +include(CSharpUtilities) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") diff --git a/csharp-api/.editorconfig b/csharp-api/.editorconfig new file mode 100644 index 000000000..81a4983ff --- /dev/null +++ b/csharp-api/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*.cs] +indent_style = space +indent_size = 4 +csharp_new_line_before_open_brace = none +csharp_new_line_before_else = false +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_members_in_anonymous_types = false +csharp_new_line_between_query_expression_clauses = false \ No newline at end of file diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index ddbc9c325..25fa3c594 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -15,6 +15,8 @@ using System; using System.ComponentModel.DataAnnotations; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + public class ClassGenerator { public class PseudoProperty { public REFrameworkNET.Method? getter; @@ -26,15 +28,13 @@ public class PseudoProperty { private Dictionary pseudoProperties = []; - private string className; private REFrameworkNET.TypeDefinition t; private List methods = []; private List fields = []; - public List usingTypes = []; - private TypeDeclarationSyntax? typeDeclaration; + private InterfaceDeclarationSyntax typeDeclaration; private bool addedNewKeyword = false; + private bool generic = false; - private List internalFieldDeclarations = []; public TypeDeclarationSyntax? TypeDeclaration { get { @@ -48,13 +48,11 @@ public bool AddedNewKeyword { } } - public void Update(TypeDeclarationSyntax? typeDeclaration_) { - typeDeclaration = typeDeclaration_; - } - - public ClassGenerator(string className_, REFrameworkNET.TypeDefinition t_) { - className = REFrameworkNET.AssemblyGenerator.FixBadChars(className_); + public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { t = t_; + generic = pGeneric ?? t.IsGenericTypeDefinition(); + typeDeclaration = InterfaceDeclaration("type"); + foreach (var method in t_.Methods) { // Means we've entered the parent type @@ -149,90 +147,10 @@ public ClassGenerator(string className_, REFrameworkNET.TypeDefinition t_) { pseudoProperties.Remove(fieldName); } - typeDeclaration = Generate(); + Generate(); } - private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetType, REFrameworkNET.TypeDefinition? containingType) { - TypeSyntax outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)); - - string ogTargetTypename = targetType != null ? targetType.GetFullName() : ""; - string targetTypeName = REFrameworkNET.AssemblyGenerator.FixBadChars(ogTargetTypename); - - if (targetType == null || targetTypeName == "System.Void" || targetTypeName == "") { - return outSyntax; - } - - if (AssemblyGenerator.typeFullRenames.TryGetValue(targetType, out string? value)) { - targetTypeName = value; - } - - // Check for easily convertible types like System.Single, System.Int32, etc. - switch (targetTypeName) { - case "System.Single": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.FloatKeyword)); - break; - case "System.Double": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)); - break; - case "System.Int32": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)); - break; - case "System.UInt32": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UIntKeyword)); - break; - case "System.Int16": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ShortKeyword)); - break; - case "System.UInt16": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UShortKeyword)); - break; - case "System.Byte": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ByteKeyword)); - break; - case "System.SByte": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.SByteKeyword)); - break; - case "System.Char": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.CharKeyword)); - break; - case "System.Int64": - case "System.IntPtr": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.LongKeyword)); - break; - case "System.UInt64": - case "System.UIntPtr": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ULongKeyword)); - break; - case "System.Boolean": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)); - break; - case "System.String": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)); - break; - case "via.clr.ManagedObject": - case "System.Object": - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); - break; - default: - if (!REFrameworkNET.AssemblyGenerator.validTypes.Contains(ogTargetTypename)) { - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); - break; - } - - /*if (targetTypeName.Contains('<')) { - outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); - break; - }*/ - - targetTypeName = "global::" + REFrameworkNET.AssemblyGenerator.CorrectTypeName(targetTypeName); - - outSyntax = SyntaxFactory.ParseTypeName(targetTypeName); - break; - } - - return outSyntax; - } - + static readonly SortedSet invalidMethodNames = [ "Finalize", //"MemberwiseClone", @@ -255,23 +173,21 @@ private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTy ]; - private TypeDeclarationSyntax? Generate() { - usingTypes = []; - - var ogClassName = new string(className); + static string[] GenericNames = ["T", "U", "V", "W", "X", "Y", "Z", "P7", "P8", "P9"]; + private void Generate() { + var (name, count) = TypeHandler.BaseTypeName(t.Name); + typeDeclaration = InterfaceDeclaration(name).AddModifiers(Token(SyntaxKind.PublicKeyword)); - // Pull out the last part of the class name (split '.' till last) - if (t.DeclaringType == null) { - className = className.Split('.').Last(); + if (generic) { + var arguments = t.GenericArguments ?? []; + var parentGenericCount = Math.Max(0, arguments.Length - count); + var argumentList = new List(); + for (int i = parentGenericCount; i < arguments.Length; ++i) { + argumentList.Add(TypeParameter(GenericNames[i])); + } + typeDeclaration = typeDeclaration.AddTypeParameterListParameters([..argumentList]); } - - typeDeclaration = SyntaxFactory - .InterfaceDeclaration(REFrameworkNET.AssemblyGenerator.CorrectTypeName(className)) - .AddModifiers(new SyntaxToken[]{SyntaxFactory.Token(SyntaxKind.PublicKeyword)}); - if (typeDeclaration == null) { - return null; - } // Check if we need to add the new keyword to this. if (AssemblyGenerator.NestedTypeExistsInParent(t)) { @@ -280,92 +196,80 @@ private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTy } // Set up base types - List baseTypes = []; - - for (var parent = t.ParentType; parent != null; parent = parent.ParentType) { - // TODO: Fix this - if (!AssemblyGenerator.validTypes.Contains(parent.FullName)) { - continue; - } - - AssemblyGenerator.typeFullRenames.TryGetValue(parent, out string? parentName); - parentName = AssemblyGenerator.CorrectTypeName(parentName ?? parent.FullName ?? ""); - - if (parentName == null) { - break; - } - - if (parentName == "") { - break; - } - - if (parentName.Contains('[')) { - break; - } - - // Forces compiler to start at the global namespace - parentName = "global::" + parentName; + BaseTypeSyntax[] baseTypes = TypeHandler.ParentTypes(t); + typeDeclaration = typeDeclaration.AddBaseListTypes(baseTypes); - baseTypes.Add(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(parentName))); - usingTypes.Add(parent); - break; - } - - // Add a static field to the class that holds the REFrameworkNET.TypeDefinition - var refTypeVarDecl = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.TypeDefinition")) - .AddVariables(SyntaxFactory.VariableDeclarator("REFType").WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("global::REFrameworkNET.TDB.Get().FindType(\"" + t.FullName + "\")")))); - - var refTypeFieldDecl = SyntaxFactory.FieldDeclaration(refTypeVarDecl).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); // Add a static field that holds a NativeProxy to the class (for static methods) - var refProxyVarDecl = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName(REFrameworkNET.AssemblyGenerator.CorrectTypeName(className))) - .AddVariables(SyntaxFactory.VariableDeclarator("REFProxy").WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.As<" + REFrameworkNET.AssemblyGenerator.CorrectTypeName(t.FullName) + ">()")))); + var refProxyVarDecl = VariableDeclaration(TypeHandler.MakeProperType(t)) + .AddVariables( + VariableDeclarator("REFProxy") + .WithInitializer(EqualsValueClause(ParseExpression("REFType.As<" + REFrameworkNET.AssemblyGenerator.CorrectTypeName(t.FullName) + ">()")))); var refProxyFieldDecl = SyntaxFactory.FieldDeclaration(refProxyVarDecl).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); - typeDeclaration = GenerateMethods(baseTypes); - typeDeclaration = GenerateFields(baseTypes); - typeDeclaration = GenerateProperties(baseTypes); - if (baseTypes.Count > 0 && typeDeclaration != null) { + // Add a static field to the class that holds the REFrameworkNET.TypeDefinition + var refTypeFieldDecl = ParseMemberDeclaration( + $"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(\"{t.FullName}\");" + )!; + if (baseTypes.Length > 0) { refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - //fieldDeclaration2 = fieldDeclaration2.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - - typeDeclaration = (typeDeclaration as InterfaceDeclarationSyntax)?.AddBaseListTypes(baseTypes.ToArray()); } - - if (typeDeclaration != null) { - typeDeclaration = typeDeclaration.AddMembers(refTypeFieldDecl); - //typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl); - } - - // Logically needs to come after the REFType field is added as they reference it - if (internalFieldDeclarations.Count > 0 && typeDeclaration != null) { - typeDeclaration = typeDeclaration.AddMembers(internalFieldDeclarations.ToArray()); + typeDeclaration = typeDeclaration.AddMembers(refTypeFieldDecl); + //typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl); + + GenerateMethods(); + GenerateFields(); + GenerateProperties(); + + typeDeclaration = typeDeclaration.AddMembers(TypeHandler.GenerateNestedTypes(t)); + if (t.FullName == "System.Array") { + var decl = GenericArrayType(); + if (decl is not null) + typeDeclaration = typeDeclaration.AddMembers(decl); } - return GenerateNestedTypes(); } - private TypeDeclarationSyntax GenerateProperties(List baseTypes) { - if (typeDeclaration == null) { - throw new Exception("Type declaration is null"); // This should never happen - } + private StatementSyntax GenericStub(TypeSyntax? returnType, string[] paramNames, int index) { + var ret = returnType ?? TypeHandler.VoidType(); + var argumentList = string.Join(",", paramNames); + var stmt = ret switch { + var t when t.IsEquivalentTo(TypeHandler.VoidType()) => + $@"(this as REFrameworkNET.IObject) + .GetTypeDefinition() + .GetMethods()[{index}] + .Invoke(this, [{argumentList}]);", + _ => + $@"return ({ret.ToFullString()}) + (this as REFrameworkNET.IObject) + .GetTypeDefinition() + .GetMethods()[{index}] + .InvokeBoxed(typeof({ret.ToFullString()}), this, [{argumentList}]);", + }; + return ParseStatement(stmt); + } + private void GenerateProperties() { if (pseudoProperties.Count == 0) { - return typeDeclaration!; + return; } + List internalFieldDeclarations = []; + var matchingProperties = pseudoProperties .Select(property => { - var propertyType = MakeProperType(property.Value.type, t); + var propertyType = TypeHandler.MakeProperType(property.Value.type); var propertyName = new string(property.Key); BasePropertyDeclarationSyntax propertyDeclaration = SyntaxFactory.PropertyDeclaration(propertyType, propertyName) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]); if (property.Value.indexer) { - ParameterSyntax parameter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("index")).WithType(MakeProperType(property.Value.indexType, t)); + ParameterSyntax parameter = SyntaxFactory + .Parameter(SyntaxFactory.Identifier("index")) + .WithType(TypeHandler.MakeProperType(property.Value.indexType)); propertyDeclaration = SyntaxFactory.IndexerDeclaration(propertyType) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]) @@ -399,6 +303,11 @@ private TypeDeclarationSyntax GenerateProperties(List base bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + propertyType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + propertyType.GetText().ToString() + "), null, null);")); getter = getter.AddBodyStatements(bodyStatements.ToArray()); + } else if (generic) { + var index = t.Methods.IndexOf(property.Value.getter); + getter = getter + .AddBodyStatements(GenericStub(propertyType, [], index)) + .WithAttributeLists([]); } else { getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } @@ -406,33 +315,10 @@ private TypeDeclarationSyntax GenerateProperties(List base propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getter); var getterExtension = Il2CppDump.GetMethodExtension(property.Value.getter); - - if (baseTypes.Count > 0 && getterExtension != null && getterExtension.Override != null && getterExtension.Override == true) { - var matchingParentMethods = getterExtension.MatchingParentMethods; - - // Go through the parents, check if the parents are allowed to be generated - // and add the new keyword if the matching method is found in one allowed to be generated - foreach (var matchingMethod in matchingParentMethods) { - var parent = matchingMethod.DeclaringType; - if (!REFrameworkNET.AssemblyGenerator.validTypes.Contains(parent.FullName)) { - continue; - } - - shouldAddNewKeyword = true; - break; - } + if (getterExtension?.MatchingParentMethods.Any() ?? false) { + shouldAddNewKeyword = true; } - if (baseTypes.Count > 0 && !shouldAddNewKeyword) { - var declaringType = property.Value.getter.DeclaringType; - if (declaringType != null) { - var parent = declaringType.ParentType; - - if (parent != null && (parent.FindField(propertyName) != null || parent.FindField("<" + propertyName + ">k__BackingField") != null)) { - shouldAddNewKeyword = true; - } - } - } } if (property.Value.setter != null) { @@ -459,6 +345,11 @@ private TypeDeclarationSyntax GenerateProperties(List base bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {value});")); setter = setter.AddBodyStatements(bodyStatements.ToArray()); + } else if (t.IsGenericType()) { + var index = t.Methods.IndexOf(property.Value.setter); + setter = setter + .AddBodyStatements(GenericStub(null, [], index)) + .WithAttributeLists([]); } else { setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } @@ -466,32 +357,8 @@ private TypeDeclarationSyntax GenerateProperties(List base propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(setter); var setterExtension = Il2CppDump.GetMethodExtension(property.Value.setter); - - if (baseTypes.Count > 0 && setterExtension != null && setterExtension.Override != null && setterExtension.Override == true) { - var matchingParentMethods = setterExtension.MatchingParentMethods; - - // Go through the parents, check if the parents are allowed to be generated - // and add the new keyword if the matching method is found in one allowed to be generated - foreach (var matchingMethod in matchingParentMethods) { - var parent = matchingMethod.DeclaringType; - if (!REFrameworkNET.AssemblyGenerator.validTypes.Contains(parent.FullName)) { - continue; - } - - shouldAddNewKeyword = true; - break; - } - } - - if (baseTypes.Count > 0 && !shouldAddNewKeyword) { - var declaringType = property.Value.setter.DeclaringType; - if (declaringType != null) { - var parent = declaringType.ParentType; - - if (parent != null && (parent.FindField(propertyName) != null || parent.FindField("<" + propertyName + ">k__BackingField") != null)) { - shouldAddNewKeyword = true; - } - } + if (setterExtension?.MatchingParentMethods.Any() ?? false) { + shouldAddNewKeyword = true; } } @@ -504,18 +371,17 @@ private TypeDeclarationSyntax GenerateProperties(List base } return propertyDeclaration; - }); + }) + .ToArray(); - return typeDeclaration.AddMembers(matchingProperties.ToArray()); + typeDeclaration = typeDeclaration + .AddMembers([..internalFieldDeclarations]) + .AddMembers(matchingProperties); } - private TypeDeclarationSyntax GenerateFields(List baseTypes) { - if (typeDeclaration == null) { - throw new Exception("Type declaration is null"); // This should never happen - } - + private void GenerateFields() { if (fields.Count == 0) { - return typeDeclaration!; + return; } List validFields = []; @@ -559,10 +425,10 @@ private TypeDeclarationSyntax GenerateFields(List baseType break; } } - + List internalFieldDeclarations = []; var matchingFields = validFields .Select(field => { - var fieldType = MakeProperType(field.Type, t); + var fieldType = TypeHandler.MakeProperType(field.Type); var fieldName = new string(field.Name); // Replace the k backingfield crap @@ -611,6 +477,13 @@ private TypeDeclarationSyntax GenerateFields(List baseType getter = getter.AddBodyStatements(bodyStatementsGetter.ToArray()); setter = setter.AddBodyStatements(bodyStatementsSetter.ToArray()); + } else if (t.IsGenericType()) { + getter = getter + .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) + .WithAttributeLists([]); + setter = setter + .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) + .WithAttributeLists([]); } else { getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); @@ -624,58 +497,17 @@ private TypeDeclarationSyntax GenerateFields(List baseType matchingField ??= this.t.ParentType.FindField(field.Name); var matchingMethod = this.t.ParentType.FindMethod("get_" + fieldName); matchingMethod ??= this.t.ParentType.FindMethod("set_" + fieldName); - - bool added = false; - - if (matchingField != null) { - var parentT = matchingField.DeclaringType; - - if (parentT != null && REFrameworkNET.AssemblyGenerator.validTypes.Contains(parentT.FullName)) { - propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - added = true; - } - } - - if (!added && matchingMethod != null) { - var parentT = matchingMethod.DeclaringType; - - if (parentT != null && REFrameworkNET.AssemblyGenerator.validTypes.Contains(parentT.FullName)) { - propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } - } - - /*if ((this.t.ParentType.FindField(field.Name) != null || this.t.ParentType.FindField(fieldName) != null) || - (this.t.ParentType.FindMethod("get_" + fieldName) != null || this.t.ParentType.FindMethod("set_" + fieldName) != null)) - { - //propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - }*/ - } - - /*var fieldExtension = Il2CppDump.GetFieldExtension(field); - - if (fieldExtension != null && fieldExtension.MatchingParentFields.Count > 0) { - var matchingParentFields = fieldExtension.MatchingParentFields; - - // Go through the parents, check if the parents are allowed to be generated - // and add the new keyword if the matching field is found in one allowed to be generated - foreach (var matchingField in matchingParentFields) { - var parent = matchingField.DeclaringType; - if (parent == null) { - continue; - } - if (!REFrameworkNET.AssemblyGenerator.validTypes.Contains(parent.FullName)) { - continue; - } - + if (matchingMethod?.GetMatchingParentMethods().Any() ?? false) { propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - break; } - }*/ - + } return propertyDeclaration; - }); + }) + .ToArray(); - return typeDeclaration.AddMembers(matchingFields.ToArray()); + typeDeclaration = typeDeclaration + .AddMembers([..internalFieldDeclarations]) + .AddMembers(matchingFields); } private static readonly Dictionary operatorTokens = new() { @@ -705,18 +537,13 @@ private TypeDeclarationSyntax GenerateFields(List baseType ["op_Explicit"] = SyntaxFactory.Token(SyntaxKind.ExplicitKeyword), }; - private TypeDeclarationSyntax GenerateMethods(List baseTypes) { - if (typeDeclaration == null) { - throw new Exception("Type declaration is null"); // This should never happen - } - - if (methods.Count == 0) { - return typeDeclaration!; - } + private void GenerateMethods() { + if (methods.Count == 0) return; HashSet seenMethodSignatures = []; List validMethods = []; + List internalFieldDeclarations = []; try { foreach(REFrameworkNET.Method m in methods) { @@ -732,10 +559,6 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp continue; } - if (m.ReturnType.FullName.Contains('!')) { - continue; - } - validMethods.Add(m); } } catch (Exception e) { @@ -745,12 +568,16 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp var matchingMethods = validMethods .Select(method => { - var returnType = MakeProperType(method.ReturnType, t); + + var returnType = TypeHandler.MakeProperType(method.ReturnType); + //string simpleMethodSignature = returnType.GetText().ToString(); string simpleMethodSignature = ""; // Return types are not part of the signature. Return types are not overloaded. var methodName = new string(method.Name); + if (methodName.StartsWith("System.")) + methodName = "_" + methodName; var methodExtension = Il2CppDump.GetMethodExtension(method); // Hacky fix for MHR because parent classes have the same method names @@ -760,8 +587,7 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp return null; } - var methodDeclaration = SyntaxFactory.MethodDeclaration(returnType, methodName ?? "UnknownMethod") - .AddModifiers(new SyntaxToken[]{SyntaxFactory.Token(SyntaxKind.PublicKeyword)}) + var methodDeclaration = MethodDeclaration(returnType, methodName ?? "UnknownMethod").AddModifiers(Token(SyntaxKind.PublicKeyword)) /*.AddBodyStatements(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();"))*/; if (operatorTokens.ContainsKey(methodName ?? "UnknownMethod")) { @@ -786,11 +612,6 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp System.Collections.Generic.List paramNames = []; if (method.Parameters.Count > 0) { - // If any of the params have ! in them, skip this method - if (method.Parameters.Any(param => param != null && (param.Type == null || (param.Type != null && param.Type.FullName.Contains('!'))))) { - return null; - } - var runtimeMethod = method.GetRuntimeMethod(); if (runtimeMethod == null) { @@ -799,90 +620,92 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp } var runtimeParams = runtimeMethod.Call("GetParameters") as REFrameworkNET.ManagedObject; - + if (runtimeParams is null) { + return null; + } System.Collections.Generic.List parameters = []; bool anyUnsafeParams = false; - if (runtimeParams != null) { - var methodActualRetval = method.GetReturnType(); - UInt32 unknownArgCount = 0; - foreach (dynamic param in runtimeParams) { - /*if (param.get_IsRetval() == true) { - continue; - }*/ + var methodActualRetval = method.GetReturnType(); + UInt32 unknownArgCount = 0; - var paramDef = (REFrameworkNET.TypeDefinition)param.GetTypeDefinition(); - var paramName = param.get_Name(); + foreach (dynamic param in runtimeParams) { + /*if (param.get_IsRetval() == true) { + continue; + }*/ - if (paramName == null || paramName == "") { - //paramName = "UnknownParam"; - paramName = "arg" + unknownArgCount.ToString(); - ++unknownArgCount; - } + var paramDef = (REFrameworkNET.TypeDefinition)param.GetTypeDefinition(); + var paramName = param.get_Name(); - if (paramName == "object") { - paramName = "object_"; // object is a reserved keyword. - } + if (paramName == null || paramName == "") { + //paramName = "UnknownParam"; + paramName = "arg" + unknownArgCount.ToString(); + ++unknownArgCount; + } - var paramType = param.get_ParameterType(); + if (paramName == "object") { + paramName = "object_"; // object is a reserved keyword. + } - if (paramType == null) { - paramNames.Add(paramName); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName("object"))); - continue; - } + var paramType = param.get_ParameterType(); - var parsedParamName = new string(paramName as string); - - /*if (param.get_IsGenericParameter() == true) { - return null; // no generic parameters. - }*/ - - var isByRef = paramType.IsByRefImpl(); - var isPointer = paramType.IsPointerImpl(); - var isOut = paramDef != null && paramDef.FindMethod("get_IsOut") != null ? param.get_IsOut() : false; - var paramTypeDef = (REFrameworkNET.TypeDefinition)paramType.get_TypeHandle(); - - var paramTypeSyntax = MakeProperType(paramTypeDef, t); - - System.Collections.Generic.List modifiers = []; - - if (isOut == true) { - simpleMethodSignature += "out"; - modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword)); - anyOutParams = true; - } + if (paramType == null) { + paramNames.Add(paramName); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName("object"))); + continue; + } - if (isByRef == true) { - // can only be either ref or out. - if (!isOut) { - simpleMethodSignature += "ref " + paramTypeSyntax.GetText().ToString(); - modifiers.Add(SyntaxFactory.Token(SyntaxKind.RefKeyword)); - } - - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString())).AddModifiers(modifiers.ToArray())); - } else if (isPointer == true) { - simpleMethodSignature += "ptr " + paramTypeSyntax.GetText().ToString(); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString() + "*")).AddModifiers(modifiers.ToArray())); - anyUnsafeParams = true; - - parsedParamName = "(global::System.IntPtr) " + parsedParamName; - } else { - simpleMethodSignature += paramTypeSyntax.GetText().ToString(); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(paramTypeSyntax).AddModifiers(modifiers.ToArray())); - } + var parsedParamName = new string(paramName as string); + + /*if (param.get_IsGenericParameter() == true) { + return null; // no generic parameters. + }*/ - paramNames.Add(parsedParamName); + var isByRef = paramType.IsByRefImpl(); + var isPointer = paramType.IsPointerImpl(); + var isOut = paramDef != null && paramDef.FindMethod("get_IsOut") != null ? param.get_IsOut() : false; + var paramTypeDef = (REFrameworkNET.TypeDefinition)paramType.get_TypeHandle(); + + var paramTypeSyntax = TypeHandler.MakeProperType(paramTypeDef); + + System.Collections.Generic.List modifiers = []; + + if (isOut == true) { + simpleMethodSignature += "out"; + modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword)); + anyOutParams = true; } - methodDeclaration = methodDeclaration.AddParameterListParameters([.. parameters]); + if (isByRef == true) { + // can only be either ref or out. + if (!isOut) { + simpleMethodSignature += "ref " + paramTypeSyntax.GetText().ToString(); + modifiers.Add(SyntaxFactory.Token(SyntaxKind.RefKeyword)); + } + + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString())).AddModifiers(modifiers.ToArray())); + } else if (isPointer == true) { + simpleMethodSignature += "ptr " + paramTypeSyntax.GetText().ToString(); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString() + "*")).AddModifiers(modifiers.ToArray())); + anyUnsafeParams = true; - if (anyUnsafeParams) { - methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.UnsafeKeyword)); + parsedParamName = "(global::System.IntPtr) " + parsedParamName; + } else { + simpleMethodSignature += paramTypeSyntax.GetText().ToString(); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(paramTypeSyntax).AddModifiers(modifiers.ToArray())); } + + paramNames.Add(parsedParamName); } + + methodDeclaration = methodDeclaration.AddParameterListParameters([.. parameters]); + + if (anyUnsafeParams) { + methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.UnsafeKeyword)); + } + } else { simpleMethodSignature += "()"; } @@ -924,120 +747,199 @@ private TypeDeclarationSyntax GenerateMethods(List baseTyp methodDeclaration = methodDeclaration.AddBodyStatements( [.. bodyStatements] ); + } else if (t.IsGenericType()) { + var index = t.Methods.IndexOf(method); + methodDeclaration = methodDeclaration + .AddBodyStatements(GenericStub(returnType, [.. paramNames], index)) + .WithAttributeLists([]); + } else { + methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); } if (seenMethodSignatures.Contains(simpleMethodSignature)) { - Console.WriteLine("Skipping duplicate method: " + methodDeclaration.GetText().ToString()); + Console.WriteLine("Skipping duplicate method: " + methodDeclaration.NormalizeWhitespace().GetText().ToString()); return null; } seenMethodSignatures.Add(simpleMethodSignature); - // Add the rest of the modifiers here that would mangle the signature check - if (baseTypes.Count > 0 && methodExtension != null && methodExtension.Override != null && methodExtension.Override == true) { - var matchingParentMethods = methodExtension.MatchingParentMethods; - - // Go through the parents, check if the parents are allowed to be generated - // and add the new keyword if the matching method is found in one allowed to be generated - // TODO: We can get rid of this once we start properly generating generic classes. - // Since we just ignore any class that has '<' in it. - foreach (var matchingMethod in matchingParentMethods) { - var parent = matchingMethod.DeclaringType; - if (!REFrameworkNET.AssemblyGenerator.validTypes.Contains(parent.FullName)) { - continue; - } - - methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - break; - } + if (methodExtension?.MatchingParentMethods.Any() ?? false) { + methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.NewKeyword)); } - return methodDeclaration; - }).Where(method => method != null).Select(method => method!); - if (matchingMethods == null) { - return typeDeclaration; - } - - return typeDeclaration.AddMembers(matchingMethods.ToArray()); + return methodDeclaration; + }) + .Where(method => method != null) + .Select(method => method!) + .ToArray(); + + typeDeclaration = typeDeclaration + .AddMembers([..internalFieldDeclarations]) + .AddMembers(matchingMethods); } - private TypeDeclarationSyntax? GenerateNestedTypes() { - if (this.typeDeclaration == null) { - return null; - } - - HashSet? nestedTypes = Il2CppDump.GetTypeExtension(t)?.NestedTypes; - - foreach (var nestedT in nestedTypes ?? []) { - var nestedTypeName = nestedT.FullName ?? ""; + private static InterfaceDeclarationSyntax? GenericArrayType() { + var array_generic = TDB.Get().GetType("!0[]"); + if (array_generic is null) return null; - //System.Console.WriteLine("Nested type: " + nestedTypeName); + var decl = new ClassGenerator(array_generic).typeDeclaration; + return decl + .WithIdentifier(Identifier("Impl")) + .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter("T")))); + } - if (nestedTypeName == "") { - continue; - } - if (nestedTypeName.Contains("[") || nestedTypeName.Contains("]") || nestedTypeName.Contains('<')) { - continue; - } +} + +class TypeHandler { + public static TypeSyntax VoidType() => PredefinedType(Token(SyntaxKind.VoidKeyword)); + public static TypeSyntax ObjType() => PredefinedType(Token(SyntaxKind.ObjectKeyword)); + public static Dictionary Predefined = new() { + ["System.Single"] = ParseTypeName("float"), + ["System.Double"] = ParseTypeName("double"), + ["System.Int32"] = ParseTypeName("int"), + ["System.UInt32"] = ParseTypeName("uint"), + ["System.Int16"] = ParseTypeName("short"), + ["System.UInt16"] = ParseTypeName("ushort"), + ["System.Byte"] = ParseTypeName("byte"), + ["System.SByte"] = ParseTypeName("sbyte"), + ["System.Char"] = ParseTypeName("char"), + ["System.Int64"] = ParseTypeName("long"), + ["System.IntPtr"] = ParseTypeName("long"), + ["System.UInt64"] = ParseTypeName("ulong"), + ["System.UIntPtr"] = ParseTypeName("ulong"), + ["System.Boolean"] = ParseTypeName("bool"), + ["System.String"] = ParseTypeName("string"), + ["via.clr.ManagedObject"] = ObjType(), + ["System.Object"] = ObjType(), + ["System.Void"] = VoidType(), + ["!0"] = ParseTypeName("T"), + ["!1"] = ParseTypeName("U"), + ["!2"] = ParseTypeName("V"), + ["!3"] = ParseTypeName("W"), + ["!4"] = ParseTypeName("X"), + ["!5"] = ParseTypeName("Y"), + ["!6"] = ParseTypeName("Z"), + ["!7"] = ParseTypeName("P7"), + ["!8"] = ParseTypeName("P8"), + ["!9"] = ParseTypeName("P9"), + }; - if (nestedTypeName.Split('.').Last() == "file") { - nestedTypeName = nestedTypeName.Replace("file", "@file"); - } + public static Dictionary Cache = new(); - // Enum - if (nestedT.IsEnum()) { - var nestedEnumGenerator = new EnumGenerator(nestedTypeName.Split('.').Last(), nestedT); + public static (string, int) BaseTypeName(string baseName) { + if (baseName.Split('`').ToArray() is [var name, var count]) + return (name, int.Parse(count)); + return (baseName, 0); - AssemblyGenerator.ForEachArrayType(nestedT, (arrayType) => { - var arrayTypeName = AssemblyGenerator.typeRenames[arrayType]; + } - var arrayClassGenerator = new ClassGenerator( - arrayTypeName, - arrayType - ); + public static BaseTypeSyntax[] ParentTypes(REFrameworkNET.TypeDefinition type) { + List parents = new(); + var parentType = type.ParentType; + while (parentType != null) { + if (parentType.Name == "") break; + if (parentType.FullName == "System.Object") { + parents.Insert(0, SimpleBaseType(ParseTypeName("global::_System.Object"))); + break; + } + var baseType = SimpleBaseType(MakeProperType(parentType)); + parents.Insert(0, baseType); + parentType = parentType.ParentType; + } + return parents.ToArray(); + } - if (arrayClassGenerator.TypeDeclaration != null) { - this.Update(this.typeDeclaration.AddMembers(arrayClassGenerator.TypeDeclaration)); - } - }); + public static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetType) { - if (nestedEnumGenerator.EnumDeclaration != null) { - this.Update(this.typeDeclaration.AddMembers(nestedEnumGenerator.EnumDeclaration)); - } + if (targetType is null) return VoidType(); + if (targetType.Name.StartsWith("<")) return ObjType(); + if (targetType.Name.StartsWith("!!")) return ObjType(); - continue; - } + if (Predefined.ContainsKey(targetType.FullName)) + return Predefined[targetType.FullName]; + if (Cache.ContainsKey(targetType.Index)) + return Cache[targetType.Index]; - var nestedGenerator = new ClassGenerator( - nestedTypeName.Split('.').Last(), - nestedT + if (targetType.GetElementType() is TypeDefinition elemType) { + var elem = MakeProperType(elemType); + var arraySyntax = QualifiedName( + ParseName("global::_System.Array"), + GenericName("Impl") + .AddTypeArgumentListArguments([elem]) ); + Cache[targetType.Index] = arraySyntax; + return arraySyntax; + } + Cache[targetType.Index] = ObjType(); - if (nestedGenerator.TypeDeclaration == null) { - continue; + var typeList = new List(); + { + var type = targetType!; + while (true) { + typeList.Insert(0, type.Name ?? "UNKN"); + if (type.DeclaringType is null || type.DeclaringType == type) + break; + type = type.DeclaringType; + } + if (type.Namespace is not null && type.Namespace.Any()) { + typeList.Insert(0, type.Namespace); + } else { + typeList.Insert(0, "_"); } + } - AssemblyGenerator.ForEachArrayType(nestedT, (arrayType) => { - var arrayTypeName = AssemblyGenerator.typeRenames[arrayType]; + int genericIndex = 0; + var generics = targetType.GenericArguments ?? []; + var toParse = string.Join(".", typeList.Select(tName => { + var (name, count) = BaseTypeName(tName); + if (count == 0) return name; + name += "<"; + for (int i = 0; i < count; ++i) { + if (i > 0) name += ","; + if (i + genericIndex >= generics.Length) { + name += "UNKN"; + continue; + } + name += MakeProperType(generics[i + genericIndex]).ToFullString(); + } + name += ">"; + genericIndex += count; + return name; + } + )); + if (toParse.StartsWith("System")) + toParse = "_" + toParse; + var parsed = ParseTypeName($"global::{toParse}"); + Cache[targetType.Index] = parsed; + return parsed; + } - var arrayClassGenerator = new ClassGenerator( - arrayTypeName, - arrayType - ); + public static MemberDeclarationSyntax? GenerateType(TypeDefinition t) { - if (arrayClassGenerator.TypeDeclaration != null) { - //this.Update(this.typeDeclaration.AddMembers(arrayClassGenerator.TypeDeclaration)); - this.Update(this.typeDeclaration.AddMembers(arrayClassGenerator.TypeDeclaration)); - } - }); + if (t.Name == "") return null; + if (t.FullName.EndsWith("[]")) return null; + if (t.Name.StartsWith("<")) return null; + if (t.IsGenericType() && !t.IsGenericTypeDefinition()) return null; - if (nestedGenerator.TypeDeclaration != null) { - this.Update(this.typeDeclaration.AddMembers(nestedGenerator.TypeDeclaration)); - } + // Enum + if (t.IsEnum()) { + var (baseName, _) = TypeHandler.BaseTypeName(t.Name); + var nestedEnumGenerator = new EnumGenerator(baseName, t); + return nestedEnumGenerator.EnumDeclaration; } + var nestedGenerator = new ClassGenerator(t); + return nestedGenerator.TypeDeclaration; + } - return typeDeclaration; + public static MemberDeclarationSyntax[] GenerateNestedTypes(TypeDefinition t) { + var nestedTypes = Il2CppDump.GetTypeExtension(t)?.NestedTypes; + return nestedTypes? + .Select(GenerateType) + .Where(t => t is not null) + .Select(t => t!) + .ToArray() ?? []; } -} \ No newline at end of file +} + diff --git a/csharp-api/AssemblyGenerator/EnumGenerator.cs b/csharp-api/AssemblyGenerator/EnumGenerator.cs index 558ae2e91..6f773611d 100644 --- a/csharp-api/AssemblyGenerator/EnumGenerator.cs +++ b/csharp-api/AssemblyGenerator/EnumGenerator.cs @@ -54,9 +54,7 @@ public void Update(EnumDeclarationSyntax? typeDeclaration) { var declaringType = t.DeclaringType; if (declaringType != null) { - var existingField = declaringType.FindField(t.Name); - - if (existingField != null && AssemblyGenerator.validTypes.Contains(existingField.DeclaringType.FullName)) { + if (declaringType.FindField(t.Name) != null) { enumDeclaration = enumDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); } } diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index 42fc3b0cc..975bb6574 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -15,6 +15,10 @@ using System.Threading.Tasks; using System.Collections.Concurrent; using System.Reflection.Metadata; +using REFrameworkNET.Attributes; +using REFrameworkNET; +using REFrameworkNET.Callbacks; + public class Il2CppDump { public class Field { @@ -218,80 +222,10 @@ namespace REFrameworkNET { public class AssemblyGenerator { static Dictionary namespaces = []; - // Start with an empty CompilationUnitSyntax (represents an empty file) - //static CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit(); - - static readonly char[] invalidChars = [ - '<', - '>', - ',', - '!', - ' ', - ]; - - static readonly char[] invalidGenericChars = [ - '<', - '>', - ',', - '!', - ' ', - '`', - '\'' - ]; - public static string FixBadChars(string name) { - // Find the first <, and the last >, replace any dots in between with underscores - /*int first = name.IndexOf('<'); - int last = name.LastIndexOf('>'); - - if (first != -1 && last != -1) { - name = name.Substring(0, first) + name.Substring(first, last - first).Replace('.', '_') + name.Substring(last); - } - - // Replace any invalid characters with underscores - foreach (var c in invalidGenericChars) { - name = name.Replace(c, '_'); - }*/ - return name; } - public static string FixBadChars_Internal(string name) { - int first = name.IndexOf('<'); - int last = name.LastIndexOf('>'); - - if (first != -1 && last != -1) { - name = name.Substring(0, first) + name.Substring(first, last - first).Replace('.', '_') + name.Substring(last); - } - - // Replace any invalid characters with underscores - foreach (var c in invalidGenericChars) { - name = name.Replace(c, '_'); - } - - // Replace any "[[", "]]" with "_" - name = name.Replace("[[", "_").Replace("]]", "_"); - - return name; - } - - /*public static string FixBadCharsForGeneric(string name) { - // Find the first <, and the last >, replace any dots in between with underscores - int first = name.IndexOf('<'); - int last = name.LastIndexOf('>'); - - if (first != -1 && last != -1) { - name = name.Substring(0, first) + name.Substring(first, last - first).Replace('.', '_') + name.Substring(last); - } - - // Replace any invalid characters with underscores - foreach (var c in invalidGenericChars) { - name = name.Replace(c, '_'); - } - - return name; - }*/ - public static string CorrectTypeName(string fullName) { if (fullName.StartsWith("System.") || fullName.StartsWith("Internal.")) { return "_" + fullName; @@ -327,94 +261,11 @@ public static string CorrectTypeName(string fullName) { return value2; } - public static SortedSet validTypes = []; - public static SortedSet generatedTypes = []; - - // Array of System.Array derived types - public static List arrayTypes = []; - public static HashSet typesWithArrayTypes = []; - private static Dictionary> elementTypesToArrayTypes = []; - - public static Dictionary typeRenames = []; - public static Dictionary typeFullRenames = []; - public static readonly REFrameworkNET.TypeDefinition SystemArrayT = REFrameworkNET.API.GetTDB().GetType("System.Array"); - - public static void ForEachArrayType( - TypeDefinition elementType, Action action, - HashSet? visited = null, - HashSet? visitedArrayTypes = null - ) - { - if (visited == null) { - visited = new HashSet(); - } - - if (visitedArrayTypes == null) { - visitedArrayTypes = new HashSet(); - } - - if (visited.Contains(elementType)) { - return; - } - - visited.Add(elementType); - - if (!elementTypesToArrayTypes.TryGetValue(elementType, out List? arrayTypes)) { - return; - } - - foreach (var arrayType in arrayTypes) { - if (visitedArrayTypes.Contains(arrayType)) { - continue; - } - - action(arrayType); - visitedArrayTypes.Add(arrayType); - - // Array types can have array types themselves. - ForEachArrayType(arrayType, action, visited); - } - } + public static SortedSet generatedTypes = []; public static REFrameworkNET.TypeDefinition? GetEquivalentNestedTypeInParent(REFrameworkNET.TypeDefinition nestedT) { var isolatedNestedName = nestedT.FullName?.Split('.').Last(); - if (nestedT.DeclaringType == null && nestedT.IsDerivedFrom(SystemArrayT)) { - // Types derived from System.Array do not have a proper declaring type - // so we need to get the element type and find the declaring type of that - TypeDefinition? elementType = nestedT.GetElementType(); - - while (elementType != null && elementType.IsDerivedFrom(SystemArrayT)) { - elementType = elementType.GetElementType(); - } - - if (elementType != null) { - var equivalentElementType = GetEquivalentNestedTypeInParent(elementType); - - if (equivalentElementType != null) { - // Now go through all possible array types for that equivalent type - TypeDefinition? equivalentArray = null; - ForEachArrayType(equivalentElementType, (arrayType) => { - if (equivalentArray != null) { - return; - } - - var isolatedArrayTypeName = arrayType.FullName?.Split('.').Last(); - - if (isolatedArrayTypeName == isolatedNestedName) { - System.Console.WriteLine("Found equivalent array type for " + nestedT.FullName); - equivalentArray = arrayType; - return; - } - }); - - if (equivalentArray != null) { - return equivalentArray; - } - } - } - } - var t = nestedT.DeclaringType; if (t == null) { @@ -423,11 +274,6 @@ public static void ForEachArrayType( // Add the "new" keyword if this nested type is anywhere in the hierarchy for (var parent = t.ParentType; parent != null; parent = parent.ParentType) { - // TODO: Fix this - if (!validTypes.Contains(parent.FullName)) { - continue; - } - var parentNestedTypes = Il2CppDump.GetTypeExtension(parent)?.NestedTypes; // Look for same named nested types @@ -448,282 +294,26 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested return GetEquivalentNestedTypeInParent(nestedT) != null; } - private static string? GetOptionalPrefix(REFrameworkNET.TypeDefinition t) { - if (t.Namespace == null || t.Namespace.Length == 0) { - if (t.DeclaringType == null) { - return "_."; - } else { - var lastDeclaringType = t.DeclaringType; - - while (lastDeclaringType.DeclaringType != null) { - lastDeclaringType = lastDeclaringType.DeclaringType; - } - - if (lastDeclaringType.Namespace == null || lastDeclaringType.Namespace.Length == 0) { - return "_."; - } - } - } - - return null; - } - - private static string MakePrefixedTypeName(REFrameworkNET.TypeDefinition t) { - var prefix = GetOptionalPrefix(t); - - if (prefix != null) { - return prefix + t.GetFullName(); - } - - return t.GetFullName(); - } - - private static bool HandleArrayType(REFrameworkNET.TypeDefinition t) { - var elementTypeDef = t.GetElementType(); - - if (elementTypeDef == null || !t.IsDerivedFrom(SystemArrayT)) { - System.Console.WriteLine("Failed to get element type for " + t.FullName); - return false; - } - - string arrayDims = "1D"; - - // Look for the last "[]" in the type name - // however we can have stuff like "[,]" or "[,,]" etc - var tFullName = t.GetFullName(); - var lastDims = tFullName.LastIndexOf('['); - var lastDimsEnd = tFullName.LastIndexOf(']'); - - if (lastDims != -1 && lastDimsEnd != -1) { - // Count how many , there are - var dimCount = 0; - - for (int i = lastDims+1; i < lastDimsEnd; i++) { - if (tFullName[i] == ',') { - dimCount++; - } - } - - arrayDims = (dimCount + 1).ToString() + "D"; - } - - typesWithArrayTypes.Add(elementTypeDef); - - if (!elementTypesToArrayTypes.ContainsKey(elementTypeDef)) { - elementTypesToArrayTypes[elementTypeDef] = []; - } - - elementTypesToArrayTypes[elementTypeDef].Add(t); - - // Check if the element type is a System.Array derived type - if (elementTypeDef.IsDerivedFrom(SystemArrayT)) { - if (HandleArrayType(elementTypeDef)) { - typeRenames[t] = typeRenames[elementTypeDef] + "_Array" + arrayDims; - } else { - typeRenames[t] = elementTypeDef.Name + "_Array" + arrayDims; - } - - if (typeFullRenames.ContainsKey(elementTypeDef)) { - typeFullRenames[t] = typeFullRenames[elementTypeDef] + "_Array" + arrayDims; - } - } else { - typeRenames[t] = elementTypeDef.Name + "_Array" + arrayDims; - typeFullRenames[t] = MakePrefixedTypeName(elementTypeDef) + "_Array" + arrayDims; - - if (typeFullRenames.ContainsKey(elementTypeDef)) { - typeFullRenames[t] = typeFullRenames[elementTypeDef] + "_Array" + arrayDims; - } - } - - return true; - } - - static void FillValidEntries(REFrameworkNET.TDB context) { - if (validTypes.Count > 0) { - return; - } - - context.GetType(0).GetFullName(); // initialize the types - - foreach (REFrameworkNET.TypeDefinition t in context.Types) { - //var t = context.GetType((uint)i); - var typeName = t.GetFullName(); - - if (typeName.Length == 0) { - Console.WriteLine("Bad type name"); - continue; - } - - // Generics and arrays not yet supported - if (typeName.Contains("[[") || typeName.Contains('!')) { - continue; - } - - if (typeName.Contains('<') || typeName.Contains('`')) { - continue; - } - - if (typeName.Any(c => c > 127)) { - System.Console.WriteLine("Skipping type with non-ascii characters " + typeName); - continue; - } - - // Check if abstract type and skip - /*var runtimeType = t.GetRuntimeType(); - - if (runtimeType != null && (runtimeType as dynamic).get_IsInterface()) { - System.Console.WriteLine("Skipping interface " + typeName); - continue; - } - - var friendlyTypeName = FixBadChars_Internal(typeName);*/ - - if (t.Namespace == null || t.Namespace.Length == 0) { - if (typeName.Length == 0) { - continue; - } - - var optionalPrefix = GetOptionalPrefix(t); - - if (optionalPrefix != null) { - typeFullRenames[t] = optionalPrefix + typeName; - } - } - - if (t.IsDerivedFrom(SystemArrayT)) { - arrayTypes.Add(t); - - HandleArrayType(t); - } - - /*if (t.IsGenericType() && !t.IsGenericTypeDefinition()) { - continue; - }*/ - - validTypes.Add(typeName); - } - } - - static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, string typeName, REFrameworkNET.TypeDefinition? t) { - var compilationUnit = SyntaxFactory.CompilationUnit(); - - if (!validTypes.Contains(typeName)) { - return compilationUnit; - } + static CompilationUnitSyntax? MakeFromTypeEntry(REFrameworkNET.TDB context, REFrameworkNET.TypeDefinition? t) { if (t == null) { Console.WriteLine("Failed to find type"); - return compilationUnit; - } - - if (t.DeclaringType != null) { - //MakeFromTypeEntry(context, t.DeclaringType.Name ?? "", t.DeclaringType); - return compilationUnit; // We want to define it inside of its declaring type, not a second time - } - - if (generatedTypes.Contains(typeName)) { - //Console.WriteLine("Skipping already generated type " + typeName); - return compilationUnit; - } - - generatedTypes.Add(typeName); - - // do not generate array types directly, we do it manually per element type - if (typeName.Contains("[]")) { - Console.WriteLine("Skipping array type " + typeName); - return compilationUnit; - } - - if (typeFullRenames.TryGetValue(t, out string? renamedTypeName)) { - typeName = renamedTypeName; + return null; } - if (t.IsEnum()) { - var generator = new EnumGenerator(typeName, t); - - if (generator.EnumDeclaration == null) { - return compilationUnit; - } - - var generatedNamespace = ExtractNamespaceFromType(t); - - if (generatedNamespace != null) { - var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, generator.EnumDeclaration); - compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); - } else { - Console.WriteLine("Failed to create namespace for " + typeName); - } - - ForEachArrayType(t, (arrayType) => { - var arrayTypeName = typeFullRenames[arrayType]; - - var arrayClassGenerator = new ClassGenerator( - arrayTypeName, - arrayType - ); - - if (arrayClassGenerator.TypeDeclaration == null) { - return; - } - - // We can re-use the namespace from the original type - if (generatedNamespace != null) { - var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, arrayClassGenerator.TypeDeclaration); - compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); - } - }); - } else { - // Generate starting from topmost parent first - if (t.ParentType != null) { - compilationUnit = MakeFromTypeEntry(context, t.ParentType.FullName ?? "", t.ParentType); - } - - var generator = new ClassGenerator( - typeName.Split('.').Last() == "file" ? typeName.Replace("file", "@file") : typeName, - t - ); - - if (generator.TypeDeclaration == null) { - return compilationUnit; - } - - var generatedNamespace = ExtractNamespaceFromType(t); - - if (generatedNamespace != null) { - var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, generator.TypeDeclaration); - compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); - } else { - Console.WriteLine("Failed to create namespace for " + typeName); - } - - ForEachArrayType(t, (arrayType) => { - var arrayTypeName = typeFullRenames[arrayType]; - - System.Console.WriteLine("Generating array type " + arrayTypeName + " from " + t.FullName); - - if (arrayTypeName == "_.System.Array[]") { - typeFullRenames[arrayType] = "System.Array_Array1D"; - arrayTypeName = "System.Array_Array1D"; - } - - var arrayClassGenerator = new ClassGenerator( - arrayTypeName, - arrayType - ); - - if (arrayClassGenerator.TypeDeclaration == null) { - return; - } - - // We can re-use the namespace from the original type - if (generatedNamespace != null) { - var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, arrayClassGenerator.TypeDeclaration); - compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); - } - }); + if (t.DeclaringType != null) return null; + if (generatedTypes.Contains(t.Index)) return null; + generatedTypes.Add(t.Index); + var genNamespace = ExtractNamespaceFromType(t); + if (genNamespace is null) { + API.LogInfo($"Failed to create namespace for {t.FullName}"); + return null; } - return compilationUnit; + var member = TypeHandler.GenerateType(t); + if (member is null) return null; + genNamespace = genNamespace.AddMembers(member); + return SyntaxFactory.CompilationUnit().AddMembers(genNamespace); } [REFrameworkNET.Attributes.PluginEntryPoint] @@ -731,14 +321,15 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin try { return MainImpl(); } catch (Exception e) { - Console.WriteLine("Exception: " + e); + API.LogError("Exception: " + e); var ex = e; while (ex.InnerException != null) { ex = ex.InnerException; - Console.WriteLine("Inner Exception: " + ex); + API.LogError("Inner Exception: " + ex); } } + Environment.Exit(0); return []; } @@ -766,7 +357,6 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin REFrameworkNET.API.LogInfo("Generating assembly " + strippedAssemblyName); - List compilationUnits = []; var tdb = REFrameworkNET.API.GetTDB(); List typeList = []; @@ -792,45 +382,72 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin typeList.Add(runtimeType); } } + + HashSet delegateList = new(); + // Special case System.Action and System.Func, some of the generic implementations are dangling outside of modules + if (strippedAssemblyName.StartsWith("_System")) { + foreach (dynamic t in tdb.Types) { + if (t is not TypeDefinition type) continue; + var name = type.FullName ?? ""; + if (!(name.StartsWith("System.Action") || name.StartsWith("System.Func"))) + continue; + if (!type.IsGenericType()) continue; + var def = type.GetGenericTypeDefinition(); + if (delegateList.Add(def.GetRuntimeType())) { + API.LogInfo($"Added delegate {def.FullName}({def.Name}):{def.Index} ({def.GetNamespace()})"); + API.LogInfo($"Name: {TypeHandler.MakeProperType(def)}"); + } + + } + } + typeList.AddRange(delegateList); // Clean up all the local objects // Mainly because some of the older games don't play well with a ton of objects on the thread local heap REFrameworkNET.API.LocalFrameGC(); + int count = typeList.Count; + List syntaxTrees = new List(); + var syntaxTreeParseOption = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp12); // Is this parallelizable? foreach (dynamic reEngineT in typeList) { var th = reEngineT.get_TypeHandle(); + + if (count % 1000 == 0) Console.WriteLine($"{count} remaining"); + count -= 1; if (th == null) { - Console.WriteLine("Failed to get type handle for " + reEngineT.get_FullName()); + API.LogInfo("Failed to get type handle for " + reEngineT.get_FullName()); continue; } var t = th as REFrameworkNET.TypeDefinition; - if (t == null) { - Console.WriteLine("Failed to convert type handle for " + reEngineT.get_FullName()); + API.LogError("Failed to convert type handle for " + reEngineT.get_FullName()); continue; } - var typeName = t.GetFullName(); - var compilationUnit = MakeFromTypeEntry(tdb, typeName, t); - compilationUnits.Add(compilationUnit); + var properType = TypeHandler.MakeProperType(t); + var sanitizedTypeName = properType + .ToFullString() + .Replace('<', '_') + .Replace('>', '_') + .Replace(':', '_'); + var compilationUnit = MakeFromTypeEntry(tdb, t); + if (compilationUnit is null) continue; + syntaxTrees.Add(SyntaxFactory.SyntaxTree( + compilationUnit.NormalizeWhitespace(), + syntaxTreeParseOption, + $"{sanitizedTypeName}.cs" + )); // Clean up all the local objects // Mainly because some of the older games don't play well with a ton of objects on the thread local heap REFrameworkNET.API.LocalFrameGC(); } - System.Console.WriteLine("Compiling " + strippedAssemblyName + " with " + compilationUnits.Count + " compilation units..."); - List syntaxTrees = new List(); - var syntaxTreeParseOption = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp12); - - foreach (var cu in compilationUnits) { - syntaxTrees.Add(SyntaxFactory.SyntaxTree(cu, syntaxTreeParseOption)); - } string? assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); var references = REFrameworkNET.Compiler.GenerateExhaustiveMetadataReferences(typeof(REFrameworkNET.API).Assembly, new List()); @@ -849,7 +466,8 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin optimizationLevel: OptimizationLevel.Release, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default, platform: Platform.X64, - allowUnsafe: true); + allowUnsafe: true + ); // Create a compilation CSharpCompilation compilation = CSharpCompilation.Create(strippedAssemblyName) .WithOptions(csoptions) @@ -863,24 +481,36 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin if (!result.Success) { - //var textLines = syntaxTrees.GetText().Lines; - List sortedDiagnostics = result.Diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToList(); - sortedDiagnostics.Reverse(); - - foreach (Diagnostic diagnostic in sortedDiagnostics) - { - var textLines = diagnostic.Location.SourceTree?.GetText().Lines; - Console.WriteLine($"{diagnostic.Id}: {diagnostic.GetMessage()}"); - - var lineSpan = diagnostic.Location.GetLineSpan(); - var errorLineNumber = lineSpan.StartLinePosition.Line; - var errorLineText = textLines?[errorLineNumber].ToString(); - Console.WriteLine($"Error in line {errorLineNumber + 1}: {errorLineText}"); - //Console.WriteLine(diagnostic.Location.SourceTree?.GetText()); - //Console.WriteLine( - //$"Error in line {errorLineNumber + 1}: {lineSpan.StartLinePosition.Character + 1} - {lineSpan.EndLinePosition.Character + 1}"); + const string DEBUG_PATH = "reframework/debug-src"; + bool debugOut = Directory.Exists(DEBUG_PATH); + if (debugOut) { + foreach (var f in Directory.EnumerateFiles(DEBUG_PATH)) { + File.Delete(f); + } } + //var textLines = syntaxTrees.GetText().Lines; + List<(SyntaxTree, List)> sortedDiagnostics = result.Diagnostics + .OrderBy(d => (d.Location.SourceTree?.FilePath, d.Location.SourceSpan.Start)) + .GroupBy(d => d.Location.SourceTree) + .Select(g => (g.Key!, g.ToList())) + .ToList(); + foreach (var (tree, diags) in sortedDiagnostics) { + var textLines = tree.GetText().Lines; + var errors = "\n"; + + foreach (var diagnostic in diags) { + var lineSpan = diagnostic.Location.GetLineSpan(); + var errorLineNumber = lineSpan.StartLinePosition.Line; + var errorLineText = textLines?[errorLineNumber].ToString(); + API.LogError($"{diagnostic.Id}: {diagnostic.GetMessage()}"); + API.LogError($"Error in {tree.FilePath}:{errorLineNumber + 1}: {errorLineText}"); + errors += $"/* at {errorLineNumber + 1}: {diagnostic.Id}: {diagnostic.GetMessage()} */\n"; + } + if (debugOut) { + File.WriteAllText($"{DEBUG_PATH}/{tree.FilePath}", tree.GetText().ToString() + errors); + } + } REFrameworkNET.API.LogError("Failed to compile " + strippedAssemblyName); } else @@ -908,7 +538,6 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin public static List MainImpl() { var tdb = REFrameworkNET.API.GetTDB(); Il2CppDump.FillTypeExtensions(tdb); - FillValidEntries(tdb); List modules = []; @@ -919,7 +548,6 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin if (module == null) { continue; } - modules.Add(module); } @@ -934,9 +562,9 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin continue; } - if (assemblyName == "") { - continue; - } + if (assemblyName == "") continue; + if (assemblyName.Contains("application")) continue; + // if (assemblyName.Contains("viacore")) continue; REFrameworkNET.API.LogInfo("Assembly: " + assemblyName); REFrameworkNET.API.LogInfo("Location: " + location); @@ -944,15 +572,13 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin var bytecode = GenerateForAssembly(module, bytecodes); - if (bytecode != null) { - bytecodes.Add(bytecode); - } + if (bytecode is null) throw new Exception("Failed"); + bytecodes.Add(bytecode); // Clean up all the local objects // Mainly because some of the older games don't play well with a ton of objects on the thread local heap REFrameworkNET.API.LocalFrameGC(); } - return bytecodes; } }; diff --git a/csharp-api/REFrameworkNET/TypeDefinition.cpp b/csharp-api/REFrameworkNET/TypeDefinition.cpp index 8c48cd30d..fad9e1428 100644 --- a/csharp-api/REFrameworkNET/TypeDefinition.cpp +++ b/csharp-api/REFrameworkNET/TypeDefinition.cpp @@ -251,6 +251,19 @@ namespace REFrameworkNET { return (bool)runtimeType->Call("get_IsGenericType"); } + TypeDefinition ^ TypeDefinition::GetGenericTypeDefinition() { + auto runtimeType = this->GetRuntimeType(); + + if (runtimeType == nullptr) + return nullptr; + + auto genericTypeDefinition = (ManagedObject ^) runtimeType->Call("GetGenericTypeDefinition"); + if (genericTypeDefinition == nullptr) + return nullptr; + + return (TypeDefinition ^) genericTypeDefinition->Call("get_TypeHandle"); + } + array^ TypeDefinition::GetGenericArguments() { auto runtimeType = this->GetRuntimeType(); @@ -264,7 +277,7 @@ namespace REFrameworkNET { return nullptr; } - auto result = gcnew array((int)arguments->Call("get_Length", gcnew System::Int32(0))); + auto result = gcnew array((int)arguments->Call("get_Length")); for (int i = 0; i < result->Length; i++) { auto runtimeType = (ManagedObject^)arguments->Call("get_Item", gcnew System::Int32(i)); diff --git a/csharp-api/REFrameworkNET/TypeDefinition.hpp b/csharp-api/REFrameworkNET/TypeDefinition.hpp index 280500b5f..eb8d2a2f4 100644 --- a/csharp-api/REFrameworkNET/TypeDefinition.hpp +++ b/csharp-api/REFrameworkNET/TypeDefinition.hpp @@ -394,6 +394,7 @@ public ref class TypeDefinition : public System::Dynamic::DynamicObject, bool HasAttribute(REFrameworkNET::ManagedObject^ runtimeAttribute, bool inherit); bool IsGenericTypeDefinition(); bool IsGenericType(); + TypeDefinition ^ GetGenericTypeDefinition(); array^ GetGenericArguments(); property array^ GenericArguments { From 9e97fd97e79f0e69fb7c95fc9397b553bd8ab14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lavergne?= Date: Wed, 26 Mar 2025 00:05:44 +0100 Subject: [PATCH 2/6] Parallelization attempt --- .../AssemblyGenerator/ClassGenerator.cs | 387 ++++++++++-------- csharp-api/AssemblyGenerator/Generator.cs | 111 +++-- 2 files changed, 256 insertions(+), 242 deletions(-) diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index 25fa3c594..9c2c2160e 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -34,7 +34,7 @@ public class PseudoProperty { private InterfaceDeclarationSyntax typeDeclaration; private bool addedNewKeyword = false; private bool generic = false; - + public TypeDeclarationSyntax? TypeDeclaration { get { @@ -59,7 +59,7 @@ public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { if (method.DeclaringType != t_) { break; } - + if (method.Name == null) { continue; } @@ -150,7 +150,7 @@ public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { Generate(); } - + static readonly SortedSet invalidMethodNames = [ "Finalize", //"MemberwiseClone", @@ -185,7 +185,7 @@ private void Generate() { for (int i = parentGenericCount; i < arguments.Length; ++i) { argumentList.Add(TypeParameter(GenericNames[i])); } - typeDeclaration = typeDeclaration.AddTypeParameterListParameters([..argumentList]); + typeDeclaration = typeDeclaration.AddTypeParameterListParameters([.. argumentList]); } @@ -201,7 +201,7 @@ private void Generate() { // Add a static field that holds a NativeProxy to the class (for static methods) - var refProxyVarDecl = VariableDeclaration(TypeHandler.MakeProperType(t)) + var refProxyVarDecl = VariableDeclaration(TypeHandler.ProperType(t)) .AddVariables( VariableDeclarator("REFProxy") .WithInitializer(EqualsValueClause(ParseExpression("REFType.As<" + REFrameworkNET.AssemblyGenerator.CorrectTypeName(t.FullName) + ">()")))); @@ -260,7 +260,7 @@ private void GenerateProperties() { var matchingProperties = pseudoProperties .Select(property => { - var propertyType = TypeHandler.MakeProperType(property.Value.type); + var propertyType = TypeHandler.ProperType(property.Value.type); var propertyName = new string(property.Key); BasePropertyDeclarationSyntax propertyDeclaration = SyntaxFactory.PropertyDeclaration(propertyType, propertyName) @@ -269,7 +269,7 @@ private void GenerateProperties() { if (property.Value.indexer) { ParameterSyntax parameter = SyntaxFactory .Parameter(SyntaxFactory.Identifier("index")) - .WithType(TypeHandler.MakeProperType(property.Value.indexType)); + .WithType(TypeHandler.ProperType(property.Value.indexType)); propertyDeclaration = SyntaxFactory.IndexerDeclaration(propertyType) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]) @@ -303,7 +303,7 @@ private void GenerateProperties() { bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + propertyType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + propertyType.GetText().ToString() + "), null, null);")); getter = getter.AddBodyStatements(bodyStatements.ToArray()); - } else if (generic) { + } else if (generic) { var index = t.Methods.IndexOf(property.Value.getter); getter = getter .AddBodyStatements(GenericStub(propertyType, [], index)) @@ -313,7 +313,7 @@ private void GenerateProperties() { } propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getter); - + var getterExtension = Il2CppDump.GetMethodExtension(property.Value.getter); if (getterExtension?.MatchingParentMethods.Any() ?? false) { shouldAddNewKeyword = true; @@ -327,7 +327,7 @@ private void GenerateProperties() { SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.setter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")) )); - + if (property.Value.setter.IsStatic()) { shouldAddStaticKeyword = true; @@ -345,7 +345,7 @@ private void GenerateProperties() { bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {value});")); setter = setter.AddBodyStatements(bodyStatements.ToArray()); - } else if (t.IsGenericType()) { + } else if (t.IsGenericType()) { var index = t.Methods.IndexOf(property.Value.setter); setter = setter .AddBodyStatements(GenericStub(null, [], index)) @@ -353,7 +353,7 @@ private void GenerateProperties() { } else { setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } - + propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(setter); var setterExtension = Il2CppDump.GetMethodExtension(property.Value.setter); @@ -375,7 +375,7 @@ private void GenerateProperties() { .ToArray(); typeDeclaration = typeDeclaration - .AddMembers([..internalFieldDeclarations]) + .AddMembers([.. internalFieldDeclarations]) .AddMembers(matchingProperties); } @@ -414,7 +414,7 @@ private void GenerateFields() { System.Console.WriteLine("Skipping field with non-ASCII characters: " + field.Name + " " + field.Index); continue; } - + ++totalFields; validFields.Add(field); @@ -428,7 +428,7 @@ private void GenerateFields() { List internalFieldDeclarations = []; var matchingFields = validFields .Select(field => { - var fieldType = TypeHandler.MakeProperType(field.Type); + var fieldType = TypeHandler.ProperType(field.Type); var fieldName = new string(field.Name); // Replace the k backingfield crap @@ -477,7 +477,7 @@ private void GenerateFields() { getter = getter.AddBodyStatements(bodyStatementsGetter.ToArray()); setter = setter.AddBodyStatements(bodyStatementsSetter.ToArray()); - } else if (t.IsGenericType()) { + } else if (t.IsGenericType()) { getter = getter .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) .WithAttributeLists([]); @@ -506,7 +506,7 @@ private void GenerateFields() { .ToArray(); typeDeclaration = typeDeclaration - .AddMembers([..internalFieldDeclarations]) + .AddMembers([.. internalFieldDeclarations]) .AddMembers(matchingFields); } @@ -546,7 +546,7 @@ private void GenerateMethods() { List internalFieldDeclarations = []; try { - foreach(REFrameworkNET.Method m in methods) { + foreach (REFrameworkNET.Method m in methods) { if (m == null) { continue; } @@ -561,221 +561,221 @@ private void GenerateMethods() { validMethods.Add(m); } - } catch (Exception e) { + } + catch (Exception e) { Console.WriteLine("ASDF Error: " + e.Message); } var matchingMethods = validMethods - .Select(method => - { + .Select(method => { - var returnType = TypeHandler.MakeProperType(method.ReturnType); - + var returnType = TypeHandler.ProperType(method.ReturnType); - //string simpleMethodSignature = returnType.GetText().ToString(); - string simpleMethodSignature = ""; // Return types are not part of the signature. Return types are not overloaded. - var methodName = new string(method.Name); - if (methodName.StartsWith("System.")) - methodName = "_" + methodName; - var methodExtension = Il2CppDump.GetMethodExtension(method); + //string simpleMethodSignature = returnType.GetText().ToString(); + string simpleMethodSignature = ""; // Return types are not part of the signature. Return types are not overloaded. - // Hacky fix for MHR because parent classes have the same method names - // while we support that, we don't support constructed generic arguments yet, they are just "object" - if (methodName == "sortCountList") { - Console.WriteLine("Skipping sortCountList"); - return null; - } - - var methodDeclaration = MethodDeclaration(returnType, methodName ?? "UnknownMethod").AddModifiers(Token(SyntaxKind.PublicKeyword)) - /*.AddBodyStatements(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();"))*/; + var methodName = new string(method.Name); + if (methodName.StartsWith("System.")) + methodName = "_" + methodName; + var methodExtension = Il2CppDump.GetMethodExtension(method); - if (operatorTokens.ContainsKey(methodName ?? "UnknownMethod")) { - // Add SpecialName attribute to the method - methodDeclaration = methodDeclaration.AddAttributeLists( - SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::System.Runtime.CompilerServices.SpecialName")) - ) - ); - } - - simpleMethodSignature += methodName; - - // Add full method name as a MethodName attribute to the method - methodDeclaration = methodDeclaration.AddAttributeLists( - SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + method.GetIndex().ToString() + ", global::REFrameworkNET.FieldFacadeType.None)"))) - ); - - bool anyOutParams = false; - System.Collections.Generic.List paramNames = []; - - if (method.Parameters.Count > 0) { - var runtimeMethod = method.GetRuntimeMethod(); - - if (runtimeMethod == null) { - REFrameworkNET.API.LogWarning("Method " + method.DeclaringType.FullName + "." + method.Name + " has a null runtime method"); + // Hacky fix for MHR because parent classes have the same method names + // while we support that, we don't support constructed generic arguments yet, they are just "object" + if (methodName == "sortCountList") { + Console.WriteLine("Skipping sortCountList"); return null; } - var runtimeParams = runtimeMethod.Call("GetParameters") as REFrameworkNET.ManagedObject; - if (runtimeParams is null) { - return null; - } - System.Collections.Generic.List parameters = []; + var methodDeclaration = MethodDeclaration(returnType, methodName ?? "UnknownMethod").AddModifiers(Token(SyntaxKind.PublicKeyword)) + /*.AddBodyStatements(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();"))*/; - bool anyUnsafeParams = false; + if (operatorTokens.ContainsKey(methodName ?? "UnknownMethod")) { + // Add SpecialName attribute to the method + methodDeclaration = methodDeclaration.AddAttributeLists( + SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( + SyntaxFactory.ParseName("global::System.Runtime.CompilerServices.SpecialName")) + ) + ); + } + simpleMethodSignature += methodName; - var methodActualRetval = method.GetReturnType(); - UInt32 unknownArgCount = 0; + // Add full method name as a MethodName attribute to the method + methodDeclaration = methodDeclaration.AddAttributeLists( + SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( + SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), + SyntaxFactory.ParseAttributeArgumentList("(" + method.GetIndex().ToString() + ", global::REFrameworkNET.FieldFacadeType.None)"))) + ); - foreach (dynamic param in runtimeParams) { - /*if (param.get_IsRetval() == true) { - continue; - }*/ + bool anyOutParams = false; + System.Collections.Generic.List paramNames = []; - var paramDef = (REFrameworkNET.TypeDefinition)param.GetTypeDefinition(); - var paramName = param.get_Name(); + if (method.Parameters.Count > 0) { + var runtimeMethod = method.GetRuntimeMethod(); - if (paramName == null || paramName == "") { - //paramName = "UnknownParam"; - paramName = "arg" + unknownArgCount.ToString(); - ++unknownArgCount; + if (runtimeMethod == null) { + REFrameworkNET.API.LogWarning("Method " + method.DeclaringType.FullName + "." + method.Name + " has a null runtime method"); + return null; } - if (paramName == "object") { - paramName = "object_"; // object is a reserved keyword. + var runtimeParams = runtimeMethod.Call("GetParameters") as REFrameworkNET.ManagedObject; + if (runtimeParams is null) { + return null; } + System.Collections.Generic.List parameters = []; - var paramType = param.get_ParameterType(); + bool anyUnsafeParams = false; - if (paramType == null) { - paramNames.Add(paramName); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName("object"))); - continue; - } - var parsedParamName = new string(paramName as string); + var methodActualRetval = method.GetReturnType(); + UInt32 unknownArgCount = 0; - /*if (param.get_IsGenericParameter() == true) { - return null; // no generic parameters. - }*/ + foreach (dynamic param in runtimeParams) { + /*if (param.get_IsRetval() == true) { + continue; + }*/ - var isByRef = paramType.IsByRefImpl(); - var isPointer = paramType.IsPointerImpl(); - var isOut = paramDef != null && paramDef.FindMethod("get_IsOut") != null ? param.get_IsOut() : false; - var paramTypeDef = (REFrameworkNET.TypeDefinition)paramType.get_TypeHandle(); + var paramDef = (REFrameworkNET.TypeDefinition)param.GetTypeDefinition(); + var paramName = param.get_Name(); - var paramTypeSyntax = TypeHandler.MakeProperType(paramTypeDef); + if (paramName == null || paramName == "") { + //paramName = "UnknownParam"; + paramName = "arg" + unknownArgCount.ToString(); + ++unknownArgCount; + } - System.Collections.Generic.List modifiers = []; + if (paramName == "object") { + paramName = "object_"; // object is a reserved keyword. + } - if (isOut == true) { - simpleMethodSignature += "out"; - modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword)); - anyOutParams = true; - } + var paramType = param.get_ParameterType(); - if (isByRef == true) { - // can only be either ref or out. - if (!isOut) { - simpleMethodSignature += "ref " + paramTypeSyntax.GetText().ToString(); - modifiers.Add(SyntaxFactory.Token(SyntaxKind.RefKeyword)); + if (paramType == null) { + paramNames.Add(paramName); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName("object"))); + continue; } - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString())).AddModifiers(modifiers.ToArray())); - } else if (isPointer == true) { - simpleMethodSignature += "ptr " + paramTypeSyntax.GetText().ToString(); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString() + "*")).AddModifiers(modifiers.ToArray())); - anyUnsafeParams = true; - - parsedParamName = "(global::System.IntPtr) " + parsedParamName; - } else { - simpleMethodSignature += paramTypeSyntax.GetText().ToString(); - parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(paramTypeSyntax).AddModifiers(modifiers.ToArray())); - } + var parsedParamName = new string(paramName as string); - paramNames.Add(parsedParamName); - } + /*if (param.get_IsGenericParameter() == true) { + return null; // no generic parameters. + }*/ - methodDeclaration = methodDeclaration.AddParameterListParameters([.. parameters]); + var isByRef = paramType.IsByRefImpl(); + var isPointer = paramType.IsPointerImpl(); + var isOut = paramDef != null && paramDef.FindMethod("get_IsOut") != null ? param.get_IsOut() : false; + var paramTypeDef = (REFrameworkNET.TypeDefinition)paramType.get_TypeHandle(); - if (anyUnsafeParams) { - methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.UnsafeKeyword)); - } + var paramTypeSyntax = TypeHandler.ProperType(paramTypeDef); - } else { - simpleMethodSignature += "()"; - } + System.Collections.Generic.List modifiers = []; - if (method.IsStatic()) { - // lets see what happens if we just make it static - methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); + if (isOut == true) { + simpleMethodSignature += "out"; + modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword)); + anyOutParams = true; + } - // Now we must add a body to it that actually calls the method - // We have our REFType field, so we can lookup the method and call it - // Make a private static field to hold the REFrameworkNET.Method - var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString(); - var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method")) - .AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")")))); + if (isByRef == true) { + // can only be either ref or out. + if (!isOut) { + simpleMethodSignature += "ref " + paramTypeSyntax.GetText().ToString(); + modifiers.Add(SyntaxFactory.Token(SyntaxKind.RefKeyword)); + } + + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString())).AddModifiers(modifiers.ToArray())); + } else if (isPointer == true) { + simpleMethodSignature += "ptr " + paramTypeSyntax.GetText().ToString(); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(SyntaxFactory.ParseTypeName(paramTypeSyntax.ToString() + "*")).AddModifiers(modifiers.ToArray())); + anyUnsafeParams = true; + + parsedParamName = "(global::System.IntPtr) " + parsedParamName; + } else { + simpleMethodSignature += paramTypeSyntax.GetText().ToString(); + parameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier(paramName)).WithType(paramTypeSyntax).AddModifiers(modifiers.ToArray())); + } - var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); - internalFieldDeclarations.Add(methodFieldDeclaration); + paramNames.Add(parsedParamName); + } - List bodyStatements = []; + methodDeclaration = methodDeclaration.AddParameterListParameters([.. parameters]); - if (method.ReturnType.FullName == "System.Void") { - if (method.Parameters.Count == 0) { - bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);")); - } else if (!anyOutParams) { - bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", paramNames) + "});")); - } else { - bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this + if (anyUnsafeParams) { + methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.UnsafeKeyword)); } + } else { - if (method.Parameters.Count == 0) { - bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);")); - } else if (!anyOutParams) { - bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, new object[] {" + string.Join(", ", paramNames) + "});")); + simpleMethodSignature += "()"; + } + + if (method.IsStatic()) { + // lets see what happens if we just make it static + methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); + + // Now we must add a body to it that actually calls the method + // We have our REFType field, so we can lookup the method and call it + // Make a private static field to hold the REFrameworkNET.Method + var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString(); + var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method")) + .AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")")))); + + var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); + internalFieldDeclarations.Add(methodFieldDeclaration); + + List bodyStatements = []; + + if (method.ReturnType.FullName == "System.Void") { + if (method.Parameters.Count == 0) { + bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);")); + } else if (!anyOutParams) { + bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", paramNames) + "});")); + } else { + bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this + } } else { - bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this + if (method.Parameters.Count == 0) { + bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);")); + } else if (!anyOutParams) { + bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, new object[] {" + string.Join(", ", paramNames) + "});")); + } else { + bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this + } } - } - methodDeclaration = methodDeclaration.AddBodyStatements( - [.. bodyStatements] - ); - } else if (t.IsGenericType()) { - var index = t.Methods.IndexOf(method); - methodDeclaration = methodDeclaration - .AddBodyStatements(GenericStub(returnType, [.. paramNames], index)) - .WithAttributeLists([]); - } else { - methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); - } + methodDeclaration = methodDeclaration.AddBodyStatements( + [.. bodyStatements] + ); + } else if (t.IsGenericType()) { + var index = t.Methods.IndexOf(method); + methodDeclaration = methodDeclaration + .AddBodyStatements(GenericStub(returnType, [.. paramNames], index)) + .WithAttributeLists([]); + } else { + methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + } - if (seenMethodSignatures.Contains(simpleMethodSignature)) { - Console.WriteLine("Skipping duplicate method: " + methodDeclaration.NormalizeWhitespace().GetText().ToString()); - return null; - } + if (seenMethodSignatures.Contains(simpleMethodSignature)) { + Console.WriteLine("Skipping duplicate method: " + methodDeclaration.NormalizeWhitespace().GetText().ToString()); + return null; + } - seenMethodSignatures.Add(simpleMethodSignature); + seenMethodSignatures.Add(simpleMethodSignature); - if (methodExtension?.MatchingParentMethods.Any() ?? false) { - methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.NewKeyword)); - } + if (methodExtension?.MatchingParentMethods.Any() ?? false) { + methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.NewKeyword)); + } - return methodDeclaration; - }) + return methodDeclaration; + }) .Where(method => method != null) .Select(method => method!) .ToArray(); typeDeclaration = typeDeclaration - .AddMembers([..internalFieldDeclarations]) + .AddMembers([.. internalFieldDeclarations]) .AddMembers(matchingMethods); } @@ -835,7 +835,7 @@ public static (string, int) BaseTypeName(string baseName) { } - public static BaseTypeSyntax[] ParentTypes(REFrameworkNET.TypeDefinition type) { + public static BaseTypeSyntax[] ParentTypes(TypeDefinition type) { List parents = new(); var parentType = type.ParentType; while (parentType != null) { @@ -844,14 +844,26 @@ public static BaseTypeSyntax[] ParentTypes(REFrameworkNET.TypeDefinition type) { parents.Insert(0, SimpleBaseType(ParseTypeName("global::_System.Object"))); break; } - var baseType = SimpleBaseType(MakeProperType(parentType)); + var baseType = SimpleBaseType(ProperType(parentType)); parents.Insert(0, baseType); parentType = parentType.ParentType; } return parents.ToArray(); } - public static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetType) { + public static TypeSyntax ProperType(TypeDefinition type) { + if (type is null) return VoidType(); + if (type.Name.StartsWith("<")) return ObjType(); + if (type.Name.StartsWith("!!")) return ObjType(); + + if (Predefined.ContainsKey(type.FullName)) + return Predefined[type.FullName]; + if (Cache.ContainsKey(type.Index)) + return Cache[type.Index]; + return ObjType(); + } + + static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) { if (targetType is null) return VoidType(); if (targetType.Name.StartsWith("<")) return ObjType(); @@ -863,7 +875,7 @@ public static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTyp return Cache[targetType.Index]; if (targetType.GetElementType() is TypeDefinition elemType) { - var elem = MakeProperType(elemType); + var elem = BuildProperType(elemType); var arraySyntax = QualifiedName( ParseName("global::_System.Array"), GenericName("Impl") @@ -902,7 +914,7 @@ public static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTyp name += "UNKN"; continue; } - name += MakeProperType(generics[i + genericIndex]).ToFullString(); + name += ProperType(generics[i + genericIndex]).ToFullString(); } name += ">"; genericIndex += count; @@ -916,6 +928,17 @@ public static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTyp return parsed; } + public static void BuildProperTypes() { + foreach (TypeDefinition type in API.GetTDB().Types) { + BuildProperType(type); + // Special case delegates again + if (!(type.FullName.StartsWith("System.Action") || type.FullName.StartsWith("System.Func"))) { + if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition()); + } + } + API.LogInfo("Built proper types"); + } + public static MemberDeclarationSyntax? GenerateType(TypeDefinition t) { if (t.Name == "") return null; diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index 975bb6574..17acf1fe7 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -18,6 +18,7 @@ using REFrameworkNET.Attributes; using REFrameworkNET; using REFrameworkNET.Callbacks; +using System.Threading; public class Il2CppDump { @@ -234,34 +235,19 @@ public static string CorrectTypeName(string fullName) { return FixBadChars(fullName); } - static public NamespaceDeclarationSyntax? ExtractNamespaceFromType(REFrameworkNET.TypeDefinition t) { + static public NamespaceDeclarationSyntax ExtractNamespaceFromType(REFrameworkNET.TypeDefinition t) { var ns = t.GetNamespace(); - if (ns != null && ns.Length > 0) { - if (ns.StartsWith("System.") || ns == "System" || ns.StartsWith("Internal.") || ns == "Internal") { - ns = "_" + ns; - } - - if (!namespaces.TryGetValue(ns, out NamespaceDeclarationSyntax? value)) { - //ns = Regex.Replace(ns, @"[^a-zA-Z0-9.]", "_"); - Console.WriteLine("Creating namespace " + ns); - value = SyntaxTreeBuilder.CreateNamespace(ns); - namespaces[ns] = value; - } - - return value; - } - - //Console.WriteLine("Failed to extract namespace from " + t.GetFullName()); - if (!namespaces.TryGetValue("_", out NamespaceDeclarationSyntax? value2)) { - value2 = SyntaxTreeBuilder.CreateNamespace("_"); - namespaces["_"] = value2; + if (ns is null || !ns.Any()) { + return SyntaxTreeBuilder.CreateNamespace("_"); } - return value2; + if (ns.StartsWith("System") || ns.StartsWith("Internal")) + ns = "_" + ns; + return SyntaxTreeBuilder.CreateNamespace(ns); } - public static SortedSet generatedTypes = []; + public static ConcurrentDictionary generatedTypes = []; public static REFrameworkNET.TypeDefinition? GetEquivalentNestedTypeInParent(REFrameworkNET.TypeDefinition nestedT) { var isolatedNestedName = nestedT.FullName?.Split('.').Last(); @@ -302,8 +288,9 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } if (t.DeclaringType != null) return null; - if (generatedTypes.Contains(t.Index)) return null; - generatedTypes.Add(t.Index); + if (!generatedTypes.TryAdd(t.Index, true)) return null; + // if (generatedTypes.Contains(t.Index)) return null; + // generatedTypes.Add(t.Index); var genNamespace = ExtractNamespaceFromType(t); if (genNamespace is null) { API.LogInfo($"Failed to create namespace for {t.FullName}"); @@ -395,7 +382,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested var def = type.GetGenericTypeDefinition(); if (delegateList.Add(def.GetRuntimeType())) { API.LogInfo($"Added delegate {def.FullName}({def.Name}):{def.Index} ({def.GetNamespace()})"); - API.LogInfo($"Name: {TypeHandler.MakeProperType(def)}"); + API.LogInfo($"Name: {TypeHandler.ProperType(def)}"); } } @@ -407,44 +394,47 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested REFrameworkNET.API.LocalFrameGC(); int count = typeList.Count; - List syntaxTrees = new List(); var syntaxTreeParseOption = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp12); - // Is this parallelizable? - foreach (dynamic reEngineT in typeList) { - var th = reEngineT.get_TypeHandle(); - - if (count % 1000 == 0) Console.WriteLine($"{count} remaining"); - count -= 1; - - if (th == null) { - API.LogInfo("Failed to get type handle for " + reEngineT.get_FullName()); - continue; - } - var t = th as REFrameworkNET.TypeDefinition; - if (t == null) { - API.LogError("Failed to convert type handle for " + reEngineT.get_FullName()); - continue; - } + var syntaxTrees = typeList + // .AsParallel() /// Causes memory violations for now, not exactly sure why + .Select((dynamic reEngineT) => { + var th = reEngineT.get_TypeHandle(); + var thisCount = Interlocked.Decrement(ref count); + if (thisCount % 1000 == 0) Console.WriteLine($"{thisCount} remaining"); - var properType = TypeHandler.MakeProperType(t); - var sanitizedTypeName = properType - .ToFullString() - .Replace('<', '_') - .Replace('>', '_') - .Replace(':', '_'); - var compilationUnit = MakeFromTypeEntry(tdb, t); - if (compilationUnit is null) continue; - syntaxTrees.Add(SyntaxFactory.SyntaxTree( - compilationUnit.NormalizeWhitespace(), - syntaxTreeParseOption, - $"{sanitizedTypeName}.cs" - )); + if (th == null) { + API.LogInfo("Failed to get type handle for " + reEngineT.get_FullName()); + return null; + } - // Clean up all the local objects - // Mainly because some of the older games don't play well with a ton of objects on the thread local heap - REFrameworkNET.API.LocalFrameGC(); - } + var t = th as REFrameworkNET.TypeDefinition; + if (t == null) { + API.LogError("Failed to convert type handle for " + reEngineT.get_FullName()); + return null; + } + + var properType = TypeHandler.ProperType(t); + var sanitizedTypeName = properType + .ToFullString() + .Replace('<', '_') + .Replace('>', '_') + .Replace(':', '_'); + var compilationUnit = MakeFromTypeEntry(tdb, t); + if (compilationUnit is null) return null; + + // Clean up all the local objects + // Mainly because some of the older games don't play well with a ton of objects on the thread local heap + // REFrameworkNET.API.LocalFrameGC(); + return SyntaxFactory.SyntaxTree( + compilationUnit.NormalizeWhitespace(), + syntaxTreeParseOption, + $"{sanitizedTypeName}.cs" + ); + }) + .Where(s => s is not null) + .Select(s => s!) + .ToList(); @@ -538,6 +528,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested public static List MainImpl() { var tdb = REFrameworkNET.API.GetTDB(); Il2CppDump.FillTypeExtensions(tdb); + TypeHandler.BuildProperTypes(); List modules = []; @@ -563,7 +554,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } if (assemblyName == "") continue; - if (assemblyName.Contains("application")) continue; + // if (assemblyName.Contains("application")) continue; // if (assemblyName.Contains("viacore")) continue; REFrameworkNET.API.LogInfo("Assembly: " + assemblyName); From 7dd71e6d2ea128b64dd6f535981665d37cff3ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lavergne?= Date: Wed, 26 Mar 2025 00:22:20 +0100 Subject: [PATCH 3/6] Generic fixes --- csharp-api/AssemblyGenerator/ClassGenerator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index 9c2c2160e..b82e3df09 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -345,7 +345,7 @@ private void GenerateProperties() { bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {value});")); setter = setter.AddBodyStatements(bodyStatements.ToArray()); - } else if (t.IsGenericType()) { + } else if (generic) { var index = t.Methods.IndexOf(property.Value.setter); setter = setter .AddBodyStatements(GenericStub(null, [], index)) @@ -477,7 +477,7 @@ private void GenerateFields() { getter = getter.AddBodyStatements(bodyStatementsGetter.ToArray()); setter = setter.AddBodyStatements(bodyStatementsSetter.ToArray()); - } else if (t.IsGenericType()) { + } else if (generic) { getter = getter .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) .WithAttributeLists([]); @@ -747,7 +747,7 @@ private void GenerateMethods() { methodDeclaration = methodDeclaration.AddBodyStatements( [.. bodyStatements] ); - } else if (t.IsGenericType()) { + } else if (generic) { var index = t.Methods.IndexOf(method); methodDeclaration = methodDeclaration .AddBodyStatements(GenericStub(returnType, [.. paramNames], index)) @@ -783,7 +783,7 @@ [.. bodyStatements] var array_generic = TDB.Get().GetType("!0[]"); if (array_generic is null) return null; - var decl = new ClassGenerator(array_generic).typeDeclaration; + var decl = new ClassGenerator(array_generic, true).typeDeclaration; return decl .WithIdentifier(Identifier("Impl")) .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter("T")))); From 5e784e5972ed1443621a6c9c4496a4ef43a269a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lavergne?= Date: Wed, 26 Mar 2025 23:58:19 +0100 Subject: [PATCH 4/6] Handle generics through type name regeneration --- .../AssemblyGenerator/ClassGenerator.cs | 194 ++++++++++-------- csharp-api/AssemblyGenerator/Generator.cs | 15 +- 2 files changed, 118 insertions(+), 91 deletions(-) diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index b82e3df09..598ababf5 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -16,6 +16,7 @@ using System.ComponentModel.DataAnnotations; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using REFrameworkNET.Attributes; public class ClassGenerator { public class PseudoProperty { @@ -209,9 +210,15 @@ private void Generate() { var refProxyFieldDecl = SyntaxFactory.FieldDeclaration(refProxyVarDecl).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); + // var typeIdExpr = GenericDictExpr((t) => t.Index); + var refTypeName = (FieldDeclarationSyntax)ParseMemberDeclaration($"public static readonly string REFTypeName = {GenericTypeNameExpr()};")!; + if (baseTypes.Length > 0) + refTypeName = refTypeName.AddModifiers(Token(SyntaxKind.NewKeyword)); + typeDeclaration = typeDeclaration.AddMembers(refTypeName); + // Add a static field to the class that holds the REFrameworkNET.TypeDefinition var refTypeFieldDecl = ParseMemberDeclaration( - $"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(\"{t.FullName}\");" + $"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(REFTypeName);" )!; if (baseTypes.Length > 0) { refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); @@ -219,6 +226,8 @@ private void Generate() { typeDeclaration = typeDeclaration.AddMembers(refTypeFieldDecl); //typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl); + + GenerateMethods(); GenerateFields(); GenerateProperties(); @@ -251,6 +260,38 @@ var t when t.IsEquivalentTo(TypeHandler.VoidType()) => return ParseStatement(stmt); } + // This is a fun one + private string GenericTypeNameExpr() { + if (!generic) + return $"\"{t.FullName}\""; + if (t.FullName == "!0[]") + return $"(string)typeof(T).GetField(\"REFTypeName\").GetValue(null) + \"[]\""; + var hierarchy = TypeHandler.NameHierarchy(t); + int genericCount = 0; + var expr = "\"\""; + bool dot = false; + foreach (var elem in hierarchy) { + if (dot) expr += "+ \".\""; + dot = true; + + var (name, count) = TypeHandler.BaseTypeName(elem); + if (count == 0) { + expr += $" + \"{name}\""; + } + + if (count != 0) { + expr += $"+ \"{elem}<\""; + for (int i = 0; i < count; ++i) { + if (i > 0) expr += "+ \",\""; + var genericParamName = GenericNames[genericCount++]; + expr += $"+ (string) typeof({genericParamName}).GetField(\"REFTypeName\").GetValue(null)"; + } + expr += $"+ \">\""; + } + } + return expr; + } + private void GenerateProperties() { if (pseudoProperties.Count == 0) { return; @@ -285,10 +326,12 @@ private void GenerateProperties() { SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.getter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")) )); - + if (property.Value.getter.IsStatic()) { shouldAddStaticKeyword = true; + } + if (generic | property.Value.getter.IsStatic()) { // Now we must add a body to it that actually calls the method // We have our REFType field, so we can lookup the method and call it // Make a private static field to hold the REFrameworkNET.Method @@ -299,15 +342,9 @@ private void GenerateProperties() { var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); internalFieldDeclarations.Add(methodFieldDeclaration); - List bodyStatements = []; - bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + propertyType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + propertyType.GetText().ToString() + "), null, null);")); - - getter = getter.AddBodyStatements(bodyStatements.ToArray()); - } else if (generic) { - var index = t.Methods.IndexOf(property.Value.getter); - getter = getter - .AddBodyStatements(GenericStub(propertyType, [], index)) - .WithAttributeLists([]); + var instance = property.Value.getter.IsStatic() ? "null" : "this"; + var stmt = $"return ({propertyType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({propertyType.ToFullString()}), {instance}, null);"; + getter = getter.AddBodyStatements(ParseStatement(stmt)); } else { getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } @@ -327,9 +364,11 @@ private void GenerateProperties() { SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.setter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")) )); - + if (property.Value.setter.IsStatic()) { shouldAddStaticKeyword = true; + } + if (generic | property.Value.setter.IsStatic()) { // Now we must add a body to it that actually calls the method // We have our REFType field, so we can lookup the method and call it @@ -341,15 +380,9 @@ private void GenerateProperties() { var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); internalFieldDeclarations.Add(methodFieldDeclaration); - List bodyStatements = []; - bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {value});")); - - setter = setter.AddBodyStatements(bodyStatements.ToArray()); - } else if (generic) { - var index = t.Methods.IndexOf(property.Value.setter); - setter = setter - .AddBodyStatements(GenericStub(null, [], index)) - .WithAttributeLists([]); + var instance = property.Value.setter.IsStatic() ? "null" : "this"; + var stmt = $"{internalFieldName}.Invoke({instance}, [value]);"; + setter = setter.AddBodyStatements(ParseStatement(stmt)); } else { setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); } @@ -455,8 +488,9 @@ private void GenerateFields() { var propertyDeclaration = SyntaxFactory.PropertyDeclaration(fieldType, fieldName) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]); - if (field.IsStatic()) { - propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); + if (field.IsStatic() || generic) { + if (field.IsStatic()) + propertyDeclaration = propertyDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword)); // Now we must add a body to it that actually calls the method // We have our REFType field, so we can lookup the method and call it @@ -471,19 +505,15 @@ private void GenerateFields() { List bodyStatementsSetter = []; List bodyStatementsGetter = []; + var instance = field.IsStatic() + ? "0" + : "(this as REFrameworkNET.IObject).GetAddress()"; - bodyStatementsGetter.Add(SyntaxFactory.ParseStatement("return (" + fieldType.GetText().ToString() + ")" + internalFieldName + ".GetDataBoxed(typeof(" + fieldType.GetText().ToString() + "), 0, false);")); - bodyStatementsSetter.Add(SyntaxFactory.ParseStatement(internalFieldName + ".SetDataBoxed(0, new object[] {value}, false);")); + var getterStatement = ParseStatement(@$" return ({fieldType.ToFullString()}) {internalFieldName} .GetDataBoxed(typeof({fieldType.ToFullString()}), {instance}, false);"); + var setterStatement = ParseStatement($"{internalFieldName}.SetDataBoxed({instance}, new object[] {{value}}, false);"); - getter = getter.AddBodyStatements(bodyStatementsGetter.ToArray()); - setter = setter.AddBodyStatements(bodyStatementsSetter.ToArray()); - } else if (generic) { - getter = getter - .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) - .WithAttributeLists([]); - setter = setter - .AddBodyStatements(ParseStatement("throw new System.NotImplementedException();")) - .WithAttributeLists([]); + getter = getter.AddBodyStatements(getterStatement); + setter = setter.AddBodyStatements(setterStatement); } else { getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); @@ -609,7 +639,7 @@ private void GenerateMethods() { ); bool anyOutParams = false; - System.Collections.Generic.List paramNames = []; + List paramNames = []; if (method.Parameters.Count > 0) { var runtimeMethod = method.GetRuntimeMethod(); @@ -710,48 +740,46 @@ private void GenerateMethods() { simpleMethodSignature += "()"; } - if (method.IsStatic()) { + if (method.IsStatic() || generic) { + // lets see what happens if we just make it static - methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); + if (method.IsStatic()) + methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword)); // Now we must add a body to it that actually calls the method // We have our REFType field, so we can lookup the method and call it // Make a private static field to hold the REFrameworkNET.Method + var index = t.Methods.IndexOf(method); var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString(); - var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method")) - .AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")")))); - - var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); + internalFieldName = internalFieldName.Replace(".", "_"); + var methodVariableDeclaration = VariableDeclaration( + ParseTypeName("global::REFrameworkNET.Method")) + .AddVariables(VariableDeclarator(internalFieldName) + .WithInitializer( + EqualsValueClause(ParseExpression($"REFType.GetMethods()[{index}]")))); + + var methodFieldDeclaration = FieldDeclaration(methodVariableDeclaration) + .AddModifiers( + Token(SyntaxKind.PrivateKeyword), + Token(SyntaxKind.StaticKeyword), + Token(SyntaxKind.ReadOnlyKeyword)); internalFieldDeclarations.Add(methodFieldDeclaration); List bodyStatements = []; - if (method.ReturnType.FullName == "System.Void") { - if (method.Parameters.Count == 0) { - bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);")); - } else if (!anyOutParams) { - bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", paramNames) + "});")); - } else { - bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this - } - } else { - if (method.Parameters.Count == 0) { - bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);")); - } else if (!anyOutParams) { - bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, new object[] {" + string.Join(", ", paramNames) + "});")); - } else { - bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this - } + var instance = "this"; + if (method.IsStatic()) { + instance = "null"; } - methodDeclaration = methodDeclaration.AddBodyStatements( - [.. bodyStatements] - ); - } else if (generic) { - var index = t.Methods.IndexOf(method); - methodDeclaration = methodDeclaration - .AddBodyStatements(GenericStub(returnType, [.. paramNames], index)) - .WithAttributeLists([]); + var statement = (method.ReturnType.FullName, method.Parameters) switch { + _ when anyOutParams => "throw new System.NotImplementedException();", + ("System.Void", []) => $"{internalFieldName}.Invoke({instance}, null);", + ("System.Void", [..]) => $"{internalFieldName}.Invoke({instance}, [{string.Join(",", paramNames)}]);", + (_, []) => $"return ({returnType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({returnType.ToFullString()}), {instance}, null);", + (_, [..]) => $"return ({returnType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({returnType.ToFullString()}), {instance}, [{string.Join(",", paramNames)}]);" + }; + methodDeclaration = methodDeclaration.AddBodyStatements(ParseStatement(statement)); } else { methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); } @@ -863,6 +891,22 @@ public static TypeSyntax ProperType(TypeDefinition type) { return ObjType(); } + public static string[] NameHierarchy(TypeDefinition type) { + var typeList = new List(); + while (true) { + typeList.Insert(0, type.Name); + if (type.DeclaringType is null || type.DeclaringType == type) + break; + type = type.DeclaringType; + } + if (type.Namespace is not null && type.Namespace.Any()) { + typeList.Insert(0, type.Namespace); + } else { + typeList.Insert(0, "_"); + } + return [.. typeList]; + } + static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) { if (targetType is null) return VoidType(); @@ -886,22 +930,7 @@ static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) { } Cache[targetType.Index] = ObjType(); - var typeList = new List(); - { - var type = targetType!; - while (true) { - typeList.Insert(0, type.Name ?? "UNKN"); - if (type.DeclaringType is null || type.DeclaringType == type) - break; - type = type.DeclaringType; - } - if (type.Namespace is not null && type.Namespace.Any()) { - typeList.Insert(0, type.Namespace); - } else { - typeList.Insert(0, "_"); - } - } - + var typeList = NameHierarchy(targetType); int genericIndex = 0; var generics = targetType.GenericArguments ?? []; var toParse = string.Join(".", typeList.Select(tName => { @@ -933,7 +962,7 @@ public static void BuildProperTypes() { BuildProperType(type); // Special case delegates again if (!(type.FullName.StartsWith("System.Action") || type.FullName.StartsWith("System.Func"))) { - if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition()); + if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition()); } } API.LogInfo("Built proper types"); @@ -965,4 +994,3 @@ public static MemberDeclarationSyntax[] GenerateNestedTypes(TypeDefinition t) { .ToArray() ?? []; } } - diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index 17acf1fe7..27fca79d8 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -348,6 +348,9 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested List typeList = []; + const string DEBUG_PATH = "reframework/debug-src"; + bool debugOut = Directory.Exists(DEBUG_PATH); + foreach (var tIndex in assembly.Types) { var t = tdb.GetType(tIndex); @@ -420,9 +423,12 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested .Replace('<', '_') .Replace('>', '_') .Replace(':', '_'); - var compilationUnit = MakeFromTypeEntry(tdb, t); + var compilationUnit = MakeFromTypeEntry(tdb, t)?.NormalizeWhitespace(); if (compilationUnit is null) return null; + if (debugOut && t.IsGenericType()) { + File.WriteAllText($"{DEBUG_PATH}/{sanitizedTypeName}.cs", compilationUnit.ToFullString()); + } // Clean up all the local objects // Mainly because some of the older games don't play well with a ton of objects on the thread local heap // REFrameworkNET.API.LocalFrameGC(); @@ -471,13 +477,6 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested if (!result.Success) { - const string DEBUG_PATH = "reframework/debug-src"; - bool debugOut = Directory.Exists(DEBUG_PATH); - if (debugOut) { - foreach (var f in Directory.EnumerateFiles(DEBUG_PATH)) { - File.Delete(f); - } - } //var textLines = syntaxTrees.GetText().Lines; List<(SyntaxTree, List)> sortedDiagnostics = result.Diagnostics .OrderBy(d => (d.Location.SourceTree?.FilePath, d.Location.SourceSpan.Start)) From 27870da47fdb82be245204d789c7de42fe914754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lavergne?= Date: Thu, 27 Mar 2025 00:35:09 +0100 Subject: [PATCH 5/6] Typename generation for aliased types --- .../AssemblyGenerator/ClassGenerator.cs | 82 +++++++++++-------- csharp-api/AssemblyGenerator/Generator.cs | 8 +- csharp-api/CMakeLists.txt | 1 + csharp-api/REFCoreDeps/Compiler.cs | 1 - csharp-api/REFCoreDeps/TypeName.cs | 31 +++++++ 5 files changed, 87 insertions(+), 36 deletions(-) create mode 100644 csharp-api/REFCoreDeps/TypeName.cs diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index 598ababf5..71ac32a4e 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -265,7 +265,7 @@ private string GenericTypeNameExpr() { if (!generic) return $"\"{t.FullName}\""; if (t.FullName == "!0[]") - return $"(string)typeof(T).GetField(\"REFTypeName\").GetValue(null) + \"[]\""; + return $"REFrameworkNET.TypeName.Get() + \"[]\""; var hierarchy = TypeHandler.NameHierarchy(t); int genericCount = 0; var expr = "\"\""; @@ -284,7 +284,7 @@ private string GenericTypeNameExpr() { for (int i = 0; i < count; ++i) { if (i > 0) expr += "+ \",\""; var genericParamName = GenericNames[genericCount++]; - expr += $"+ (string) typeof({genericParamName}).GetField(\"REFTypeName\").GetValue(null)"; + expr += $"+ REFrameworkNET.TypeName.Get<{genericParamName}>()"; } expr += $"+ \">\""; } @@ -321,12 +321,16 @@ private void GenerateProperties() { bool shouldAddStaticKeyword = false; if (property.Value.getter != null) { - var getter = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) - .AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.getter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")) + var getter = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration); + if (!generic) { + getter = getter.AddAttributeLists( + AttributeList() + .AddAttributes(Attribute( + ParseName("global::REFrameworkNET.Attributes.Method"), + ParseAttributeArgumentList($"({property.Value.getter.Index}, global::REFrameworkNET.FieldFacadeType.None)")) )); - + } + if (property.Value.getter.IsStatic()) { shouldAddStaticKeyword = true; } @@ -359,12 +363,16 @@ private void GenerateProperties() { } if (property.Value.setter != null) { - var setter = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) - .AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.setter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")) + var setter = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration); + if (!generic) { + setter = setter.AddAttributeLists( + AttributeList() + .AddAttributes(Attribute( + ParseName("global::REFrameworkNET.Attributes.Method"), + ParseAttributeArgumentList($"({property.Value.setter.Index}, global::REFrameworkNET.FieldFacadeType.None)")) )); - + } + if (property.Value.setter.IsStatic()) { shouldAddStaticKeyword = true; } @@ -469,21 +477,24 @@ private void GenerateFields() { fieldName = fieldName[1..fieldName.IndexOf(">k__")]; } - // So this is actually going to be made a property with get/set instead of an actual field - // 1. Because interfaces can't have fields - // 2. Because we don't actually have a concrete reference to the field in our VM, so we'll be a facade for the field - var fieldFacadeGetter = SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + field.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.Getter)")) - ); - - var fieldFacadeSetter = SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + field.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.Setter)")) - ); - - AccessorDeclarationSyntax getter = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).AddAttributeLists(fieldFacadeGetter); - AccessorDeclarationSyntax setter = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).AddAttributeLists(fieldFacadeSetter); + AccessorDeclarationSyntax getter = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration); + AccessorDeclarationSyntax setter = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration); + if (!generic) { + // So this is actually going to be made a property with get/set instead of an actual field + // 1. Because interfaces can't have fields + // 2. Because we don't actually have a concrete reference to the field in our VM, so we'll be a facade for the field + var fieldFacadeGetter = AttributeList() + .AddAttributes(Attribute( + ParseName("global::REFrameworkNET.Attributes.Method"), + ParseAttributeArgumentList("(" + field.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.Getter)"))); + + var fieldFacadeSetter = AttributeList() + .AddAttributes(Attribute( + ParseName("global::REFrameworkNET.Attributes.Method"), + ParseAttributeArgumentList("(" + field.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.Setter)"))); + getter = getter.AddAttributeLists(fieldFacadeGetter); + setter = setter.AddAttributeLists(fieldFacadeSetter); + } var propertyDeclaration = SyntaxFactory.PropertyDeclaration(fieldType, fieldName) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]); @@ -505,7 +516,7 @@ private void GenerateFields() { List bodyStatementsSetter = []; List bodyStatementsGetter = []; - var instance = field.IsStatic() + var instance = field.IsStatic() ? "0" : "(this as REFrameworkNET.IObject).GetAddress()"; @@ -631,12 +642,15 @@ private void GenerateMethods() { simpleMethodSignature += methodName; + // Add full method name as a MethodName attribute to the method - methodDeclaration = methodDeclaration.AddAttributeLists( - SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute( - SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"), - SyntaxFactory.ParseAttributeArgumentList("(" + method.GetIndex().ToString() + ", global::REFrameworkNET.FieldFacadeType.None)"))) - ); + if (!generic) { + methodDeclaration = methodDeclaration.AddAttributeLists( + AttributeList() + .AddAttributes(Attribute( + ParseName("global::REFrameworkNET.Attributes.Method"), + ParseAttributeArgumentList("(" + method.GetIndex().ToString() + ", global::REFrameworkNET.FieldFacadeType.None)")))); + } bool anyOutParams = false; List paramNames = []; @@ -741,7 +755,7 @@ private void GenerateMethods() { } if (method.IsStatic() || generic) { - + // lets see what happens if we just make it static if (method.IsStatic()) methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword)); diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index 27fca79d8..82e96db7f 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -446,7 +446,13 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested string? assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); - var references = REFrameworkNET.Compiler.GenerateExhaustiveMetadataReferences(typeof(REFrameworkNET.API).Assembly, new List()); + var references = REFrameworkNET.Compiler.GenerateExhaustiveMetadataReferences( + typeof(REFrameworkNET.API).Assembly, + [typeof(REFrameworkNET.TypeName).Assembly] + ); + foreach (PortableExecutableReference dep in references) { + API.LogInfo($"compile dependency: {dep.Display}"); + } // Add the previous compilations as references foreach (var compilationbc in previousCompilations) { diff --git a/csharp-api/CMakeLists.txt b/csharp-api/CMakeLists.txt index 8440a7a18..0503a12cd 100644 --- a/csharp-api/CMakeLists.txt +++ b/csharp-api/CMakeLists.txt @@ -156,6 +156,7 @@ set(REFCoreDeps_SOURCES "REFCoreDeps/Compiler.cs" "REFCoreDeps/GarbageCollectionDisplay.cs" "REFCoreDeps/HashHelper.cs" + "REFCoreDeps/TypeName.cs" cmake.toml ) diff --git a/csharp-api/REFCoreDeps/Compiler.cs b/csharp-api/REFCoreDeps/Compiler.cs index 867e5a807..42ed56700 100644 --- a/csharp-api/REFCoreDeps/Compiler.cs +++ b/csharp-api/REFCoreDeps/Compiler.cs @@ -140,7 +140,6 @@ public static List GenerateExhaustiveMetadataRefere return false; }); - return referencesStr.Select(r => MetadataReference.CreateFromFile(r)).ToList(); } diff --git a/csharp-api/REFCoreDeps/TypeName.cs b/csharp-api/REFCoreDeps/TypeName.cs new file mode 100644 index 000000000..67f556ec2 --- /dev/null +++ b/csharp-api/REFCoreDeps/TypeName.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace REFrameworkNET; + +public static class TypeName { + static Dictionary Predefined = new() { + [typeof(float)] = "System.Single", + [typeof(double)] = "System.Double", + [typeof(int)] = "System.Int32", + [typeof(uint)] = "System.UInt32", + [typeof(short)] = "System.Int16", + [typeof(ushort)] = "System.UInt16", + [typeof(byte)] = "System.Byte", + [typeof(sbyte)] = "System.SByte", + [typeof(char)] = "System.Char", + [typeof(long)] = "System.Int64", + [typeof(long)] = "System.IntPtr", + [typeof(ulong)] = "System.UInt64", + [typeof(ulong)] = "System.UIntPtr", + [typeof(bool)] = "System.Boolean", + [typeof(string)] = "System.String", + [typeof(object)] = "System.Object", + }; + public static string Get() { + if (Predefined.ContainsKey(typeof(T))) { + return Predefined[typeof(T)]; + } + return typeof(T).GetField("REFTypeName")?.GetValue(null) as string ?? ""; + } +} \ No newline at end of file From 9536ccf217d140e7b53ecae33948b0ad4ede3033 Mon Sep 17 00:00:00 2001 From: Strackeror Date: Sun, 30 Mar 2025 18:53:43 +0200 Subject: [PATCH 6/6] Fallback when generics are unavailable --- .../AssemblyGenerator/ClassGenerator.cs | 258 +----------------- csharp-api/AssemblyGenerator/EnumGenerator.cs | 18 +- csharp-api/AssemblyGenerator/Generator.cs | 75 +++-- csharp-api/AssemblyGenerator/TypeHandler.cs | 208 ++++++++++++++ csharp-api/CMakeLists.txt | 1 + 5 files changed, 260 insertions(+), 300 deletions(-) create mode 100644 csharp-api/AssemblyGenerator/TypeHandler.cs diff --git a/csharp-api/AssemblyGenerator/ClassGenerator.cs b/csharp-api/AssemblyGenerator/ClassGenerator.cs index 71ac32a4e..d1489ff44 100644 --- a/csharp-api/AssemblyGenerator/ClassGenerator.cs +++ b/csharp-api/AssemblyGenerator/ClassGenerator.cs @@ -19,11 +19,10 @@ using REFrameworkNET.Attributes; public class ClassGenerator { - public class PseudoProperty { + public class PseudoProperty(TypeDefinition type) { public REFrameworkNET.Method? getter; public REFrameworkNET.Method? setter; - public REFrameworkNET.TypeDefinition? type; - public bool indexer = false; + public REFrameworkNET.TypeDefinition type = type; public REFrameworkNET.TypeDefinition? indexType; }; @@ -33,7 +32,6 @@ public class PseudoProperty { private List methods = []; private List fields = []; private InterfaceDeclarationSyntax typeDeclaration; - private bool addedNewKeyword = false; private bool generic = false; @@ -43,11 +41,6 @@ public TypeDeclarationSyntax? TypeDeclaration { } } - public bool AddedNewKeyword { - get { - return addedNewKeyword; - } - } public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { t = t_; @@ -75,21 +68,17 @@ public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { // Add the getter to the pseudo property (create if it doesn't exist) var propertyName = method.Name[4..]; if (!pseudoProperties.ContainsKey(propertyName)) { - pseudoProperties[propertyName] = new PseudoProperty(); + pseudoProperties[propertyName] = new PseudoProperty(method.ReturnType); } pseudoProperties[propertyName].getter = method; - pseudoProperties[propertyName].type = method.ReturnType; } else if (method.Parameters.Count == 1 && method.Name == "get_Item") { // This is an indexer property var propertyName = method.Name[4..]; if (!pseudoProperties.ContainsKey(propertyName)) { - pseudoProperties[propertyName] = new PseudoProperty(); + pseudoProperties[propertyName] = new PseudoProperty(method.ReturnType); } - pseudoProperties[propertyName].getter = method; - pseudoProperties[propertyName].type = method.ReturnType; - pseudoProperties[propertyName].indexer = true; pseudoProperties[propertyName].indexType = method.Parameters[0].Type; } } else if (method.Name.StartsWith("set_")) { @@ -97,21 +86,17 @@ public ClassGenerator(REFrameworkNET.TypeDefinition t_, bool? pGeneric = null) { // Add the setter to the pseudo property (create if it doesn't exist) var propertyName = method.Name[4..]; if (!pseudoProperties.ContainsKey(propertyName)) { - pseudoProperties[propertyName] = new PseudoProperty(); + pseudoProperties[propertyName] = new PseudoProperty(method.Parameters[0].Type); } - pseudoProperties[propertyName].setter = method; - pseudoProperties[propertyName].type = method.Parameters[0].Type; } else if (method.Parameters.Count == 2 && method.Name == "set_Item") { // This is an indexer property var propertyName = method.Name[4..]; if (!pseudoProperties.ContainsKey(propertyName)) { - pseudoProperties[propertyName] = new PseudoProperty(); + pseudoProperties[propertyName] = new PseudoProperty(method.Parameters[1].Type); } pseudoProperties[propertyName].setter = method; - pseudoProperties[propertyName].type = method.Parameters[1].Type; - pseudoProperties[propertyName].indexer = true; pseudoProperties[propertyName].indexType = method.Parameters[0].Type; } } else { @@ -181,21 +166,14 @@ private void Generate() { if (generic) { var arguments = t.GenericArguments ?? []; - var parentGenericCount = Math.Max(0, arguments.Length - count); + var parentGenericCount = Math.Max(0, arguments.Count() - count); var argumentList = new List(); - for (int i = parentGenericCount; i < arguments.Length; ++i) { + for (int i = parentGenericCount; i < arguments.Count(); ++i) { argumentList.Add(TypeParameter(GenericNames[i])); } typeDeclaration = typeDeclaration.AddTypeParameterListParameters([.. argumentList]); } - - // Check if we need to add the new keyword to this. - if (AssemblyGenerator.NestedTypeExistsInParent(t)) { - typeDeclaration = typeDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - addedNewKeyword = true; - } - // Set up base types BaseTypeSyntax[] baseTypes = TypeHandler.ParentTypes(t); typeDeclaration = typeDeclaration.AddBaseListTypes(baseTypes); @@ -207,25 +185,15 @@ private void Generate() { VariableDeclarator("REFProxy") .WithInitializer(EqualsValueClause(ParseExpression("REFType.As<" + REFrameworkNET.AssemblyGenerator.CorrectTypeName(t.FullName) + ">()")))); - var refProxyFieldDecl = SyntaxFactory.FieldDeclaration(refProxyVarDecl).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); - - // var typeIdExpr = GenericDictExpr((t) => t.Index); var refTypeName = (FieldDeclarationSyntax)ParseMemberDeclaration($"public static readonly string REFTypeName = {GenericTypeNameExpr()};")!; - if (baseTypes.Length > 0) - refTypeName = refTypeName.AddModifiers(Token(SyntaxKind.NewKeyword)); typeDeclaration = typeDeclaration.AddMembers(refTypeName); // Add a static field to the class that holds the REFrameworkNET.TypeDefinition var refTypeFieldDecl = ParseMemberDeclaration( $"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(REFTypeName);" )!; - if (baseTypes.Length > 0) { - refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } typeDeclaration = typeDeclaration.AddMembers(refTypeFieldDecl); - //typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl); - GenerateMethods(); @@ -307,7 +275,7 @@ private void GenerateProperties() { BasePropertyDeclarationSyntax propertyDeclaration = SyntaxFactory.PropertyDeclaration(propertyType, propertyName) .AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]); - if (property.Value.indexer) { + if (property.Value.indexType is not null) { ParameterSyntax parameter = SyntaxFactory .Parameter(SyntaxFactory.Identifier("index")) .WithType(TypeHandler.ProperType(property.Value.indexType)); @@ -317,7 +285,6 @@ private void GenerateProperties() { .AddParameterListParameters(parameter); } - bool shouldAddNewKeyword = false; bool shouldAddStaticKeyword = false; if (property.Value.getter != null) { @@ -356,10 +323,6 @@ private void GenerateProperties() { propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getter); var getterExtension = Il2CppDump.GetMethodExtension(property.Value.getter); - if (getterExtension?.MatchingParentMethods.Any() ?? false) { - shouldAddNewKeyword = true; - } - } if (property.Value.setter != null) { @@ -398,19 +361,11 @@ private void GenerateProperties() { propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(setter); var setterExtension = Il2CppDump.GetMethodExtension(property.Value.setter); - if (setterExtension?.MatchingParentMethods.Any() ?? false) { - shouldAddNewKeyword = true; - } } if (shouldAddStaticKeyword) { propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); } - - if (shouldAddNewKeyword) { - propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } - return propertyDeclaration; }) .ToArray(); @@ -463,7 +418,8 @@ private void GenerateFields() { // Some kind of limitation in the runtime prevents too many methods in the class if (totalFields >= (ushort.MaxValue - 15) / 2) { System.Console.WriteLine("Skipping fields in " + t.FullName + " because it has too many fields (" + fields.Count + ")"); - break; + // break; + return; } } List internalFieldDeclarations = []; @@ -531,17 +487,6 @@ private void GenerateFields() { } propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getter, setter); - - // Search for k__BackingField version and the corrected version - if (this.t.ParentType != null) { - var matchingField = this.t.ParentType.FindField(fieldName); - matchingField ??= this.t.ParentType.FindField(field.Name); - var matchingMethod = this.t.ParentType.FindMethod("get_" + fieldName); - matchingMethod ??= this.t.ParentType.FindMethod("set_" + fieldName); - if (matchingMethod?.GetMatchingParentMethods().Any() ?? false) { - propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } - } return propertyDeclaration; }) .ToArray(); @@ -804,12 +749,6 @@ private void GenerateMethods() { } seenMethodSignatures.Add(simpleMethodSignature); - - if (methodExtension?.MatchingParentMethods.Any() ?? false) { - methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.NewKeyword)); - } - - return methodDeclaration; }) .Where(method => method != null) @@ -833,178 +772,3 @@ private void GenerateMethods() { } - -class TypeHandler { - public static TypeSyntax VoidType() => PredefinedType(Token(SyntaxKind.VoidKeyword)); - public static TypeSyntax ObjType() => PredefinedType(Token(SyntaxKind.ObjectKeyword)); - public static Dictionary Predefined = new() { - ["System.Single"] = ParseTypeName("float"), - ["System.Double"] = ParseTypeName("double"), - ["System.Int32"] = ParseTypeName("int"), - ["System.UInt32"] = ParseTypeName("uint"), - ["System.Int16"] = ParseTypeName("short"), - ["System.UInt16"] = ParseTypeName("ushort"), - ["System.Byte"] = ParseTypeName("byte"), - ["System.SByte"] = ParseTypeName("sbyte"), - ["System.Char"] = ParseTypeName("char"), - ["System.Int64"] = ParseTypeName("long"), - ["System.IntPtr"] = ParseTypeName("long"), - ["System.UInt64"] = ParseTypeName("ulong"), - ["System.UIntPtr"] = ParseTypeName("ulong"), - ["System.Boolean"] = ParseTypeName("bool"), - ["System.String"] = ParseTypeName("string"), - ["via.clr.ManagedObject"] = ObjType(), - ["System.Object"] = ObjType(), - ["System.Void"] = VoidType(), - ["!0"] = ParseTypeName("T"), - ["!1"] = ParseTypeName("U"), - ["!2"] = ParseTypeName("V"), - ["!3"] = ParseTypeName("W"), - ["!4"] = ParseTypeName("X"), - ["!5"] = ParseTypeName("Y"), - ["!6"] = ParseTypeName("Z"), - ["!7"] = ParseTypeName("P7"), - ["!8"] = ParseTypeName("P8"), - ["!9"] = ParseTypeName("P9"), - }; - - public static Dictionary Cache = new(); - - public static (string, int) BaseTypeName(string baseName) { - if (baseName.Split('`').ToArray() is [var name, var count]) - return (name, int.Parse(count)); - return (baseName, 0); - - } - - public static BaseTypeSyntax[] ParentTypes(TypeDefinition type) { - List parents = new(); - var parentType = type.ParentType; - while (parentType != null) { - if (parentType.Name == "") break; - if (parentType.FullName == "System.Object") { - parents.Insert(0, SimpleBaseType(ParseTypeName("global::_System.Object"))); - break; - } - var baseType = SimpleBaseType(ProperType(parentType)); - parents.Insert(0, baseType); - parentType = parentType.ParentType; - } - return parents.ToArray(); - } - - public static TypeSyntax ProperType(TypeDefinition type) { - if (type is null) return VoidType(); - if (type.Name.StartsWith("<")) return ObjType(); - if (type.Name.StartsWith("!!")) return ObjType(); - - if (Predefined.ContainsKey(type.FullName)) - return Predefined[type.FullName]; - if (Cache.ContainsKey(type.Index)) - return Cache[type.Index]; - return ObjType(); - } - - public static string[] NameHierarchy(TypeDefinition type) { - var typeList = new List(); - while (true) { - typeList.Insert(0, type.Name); - if (type.DeclaringType is null || type.DeclaringType == type) - break; - type = type.DeclaringType; - } - if (type.Namespace is not null && type.Namespace.Any()) { - typeList.Insert(0, type.Namespace); - } else { - typeList.Insert(0, "_"); - } - return [.. typeList]; - } - - static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) { - - if (targetType is null) return VoidType(); - if (targetType.Name.StartsWith("<")) return ObjType(); - if (targetType.Name.StartsWith("!!")) return ObjType(); - - if (Predefined.ContainsKey(targetType.FullName)) - return Predefined[targetType.FullName]; - if (Cache.ContainsKey(targetType.Index)) - return Cache[targetType.Index]; - - if (targetType.GetElementType() is TypeDefinition elemType) { - var elem = BuildProperType(elemType); - var arraySyntax = QualifiedName( - ParseName("global::_System.Array"), - GenericName("Impl") - .AddTypeArgumentListArguments([elem]) - ); - Cache[targetType.Index] = arraySyntax; - return arraySyntax; - } - Cache[targetType.Index] = ObjType(); - - var typeList = NameHierarchy(targetType); - int genericIndex = 0; - var generics = targetType.GenericArguments ?? []; - var toParse = string.Join(".", typeList.Select(tName => { - var (name, count) = BaseTypeName(tName); - if (count == 0) return name; - name += "<"; - for (int i = 0; i < count; ++i) { - if (i > 0) name += ","; - if (i + genericIndex >= generics.Length) { - name += "UNKN"; - continue; - } - name += ProperType(generics[i + genericIndex]).ToFullString(); - } - name += ">"; - genericIndex += count; - return name; - } - )); - if (toParse.StartsWith("System")) - toParse = "_" + toParse; - var parsed = ParseTypeName($"global::{toParse}"); - Cache[targetType.Index] = parsed; - return parsed; - } - - public static void BuildProperTypes() { - foreach (TypeDefinition type in API.GetTDB().Types) { - BuildProperType(type); - // Special case delegates again - if (!(type.FullName.StartsWith("System.Action") || type.FullName.StartsWith("System.Func"))) { - if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition()); - } - } - API.LogInfo("Built proper types"); - } - - public static MemberDeclarationSyntax? GenerateType(TypeDefinition t) { - - if (t.Name == "") return null; - if (t.FullName.EndsWith("[]")) return null; - if (t.Name.StartsWith("<")) return null; - if (t.IsGenericType() && !t.IsGenericTypeDefinition()) return null; - - // Enum - if (t.IsEnum()) { - var (baseName, _) = TypeHandler.BaseTypeName(t.Name); - var nestedEnumGenerator = new EnumGenerator(baseName, t); - return nestedEnumGenerator.EnumDeclaration; - } - var nestedGenerator = new ClassGenerator(t); - return nestedGenerator.TypeDeclaration; - } - - public static MemberDeclarationSyntax[] GenerateNestedTypes(TypeDefinition t) { - var nestedTypes = Il2CppDump.GetTypeExtension(t)?.NestedTypes; - return nestedTypes? - .Select(GenerateType) - .Where(t => t is not null) - .Select(t => t!) - .ToArray() ?? []; - } -} diff --git a/csharp-api/AssemblyGenerator/EnumGenerator.cs b/csharp-api/AssemblyGenerator/EnumGenerator.cs index 6f773611d..c71baf346 100644 --- a/csharp-api/AssemblyGenerator/EnumGenerator.cs +++ b/csharp-api/AssemblyGenerator/EnumGenerator.cs @@ -47,19 +47,6 @@ public void Update(EnumDeclarationSyntax? typeDeclaration) { enumDeclaration = SyntaxFactory.EnumDeclaration(enumName) .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); - // Check if we need to add the new keyword to this. - if (AssemblyGenerator.NestedTypeExistsInParent(t)) { - enumDeclaration = enumDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } else { - var declaringType = t.DeclaringType; - - if (declaringType != null) { - if (declaringType.FindField(t.Name) != null) { - enumDeclaration = enumDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); - } - } - } - if (t.HasAttribute(s_FlagsAttribute, true)) { enumDeclaration = enumDeclaration.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute(SyntaxFactory.ParseName("System.FlagsAttribute")))); } @@ -73,7 +60,10 @@ public void Update(EnumDeclarationSyntax? typeDeclaration) { continue; } - var underlyingType = field.Type.GetUnderlyingType(); + var underlyingType = field.Type?.GetUnderlyingType(); + if (underlyingType is null) { + continue; + } SyntaxToken literalToken; bool foundRightType = true; diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index 82e96db7f..cc6d52def 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -19,6 +19,7 @@ using REFrameworkNET; using REFrameworkNET.Callbacks; using System.Threading; +using System.Security.Cryptography.X509Certificates; public class Il2CppDump { @@ -138,6 +139,8 @@ public static void FillTypeExtensions(REFrameworkNET.TDB context) { continue; } + if (t.IsGenericType() && !t.IsGenericTypeDefinition()) continue; + var tDeclaringType = t.DeclaringType; if (tDeclaringType != null) { var ext = GetOrAddTypeExtension(tDeclaringType); @@ -215,7 +218,9 @@ public static void FillTypeExtensions(REFrameworkNET.TDB context) { } } } - } + } + API.LocalFrameGC(); + API.LogInfo($"Loaded {typeExtensions.Count} types into db"); } } @@ -346,7 +351,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested var tdb = REFrameworkNET.API.GetTDB(); - List typeList = []; + List typeList = []; const string DEBUG_PATH = "reframework/debug-src"; bool debugOut = Directory.Exists(DEBUG_PATH); @@ -360,20 +365,16 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } dynamic runtimeType = t.GetRuntimeType(); - - if (runtimeType == null) { - Console.WriteLine("Failed to get runtime type for " + t.GetFullName()); - continue; - } - - // We don't want array types, pointers, etc - // Assembly.GetTypes usually filters this out but we have to manually do it - if (runtimeType.IsPointerImpl() == false && runtimeType.IsByRefImpl() == false) { - typeList.Add(runtimeType); + if (runtimeType is not null) { + // We don't want array types, pointers, etc + // Assembly.GetTypes usually filters this out but we have to manually do it + if (runtimeType.IsPointerImpl() || runtimeType.IsByRefImpl()) + continue; } + typeList.Add(t); } - HashSet delegateList = new(); + HashSet delegateList = new(); // Special case System.Action and System.Func, some of the generic implementations are dangling outside of modules if (strippedAssemblyName.StartsWith("_System")) { foreach (dynamic t in tdb.Types) { @@ -383,7 +384,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested continue; if (!type.IsGenericType()) continue; var def = type.GetGenericTypeDefinition(); - if (delegateList.Add(def.GetRuntimeType())) { + if (delegateList.Add(def)) { API.LogInfo($"Added delegate {def.FullName}({def.Name}):{def.Index} ({def.GetNamespace()})"); API.LogInfo($"Name: {TypeHandler.ProperType(def)}"); } @@ -401,21 +402,10 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested var syntaxTrees = typeList // .AsParallel() /// Causes memory violations for now, not exactly sure why - .Select((dynamic reEngineT) => { - var th = reEngineT.get_TypeHandle(); + .Select((t) => { var thisCount = Interlocked.Decrement(ref count); if (thisCount % 1000 == 0) Console.WriteLine($"{thisCount} remaining"); - - if (th == null) { - API.LogInfo("Failed to get type handle for " + reEngineT.get_FullName()); - return null; - } - - var t = th as REFrameworkNET.TypeDefinition; - if (t == null) { - API.LogError("Failed to convert type handle for " + reEngineT.get_FullName()); - return null; - } + if (t == null) return null; var properType = TypeHandler.ProperType(t); var sanitizedTypeName = properType @@ -423,6 +413,9 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested .Replace('<', '_') .Replace('>', '_') .Replace(':', '_'); + if (sanitizedTypeName.Count() > 50) { + sanitizedTypeName = sanitizedTypeName[..50]; + } var compilationUnit = MakeFromTypeEntry(tdb, t)?.NormalizeWhitespace(); if (compilationUnit is null) return null; @@ -431,7 +424,7 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } // Clean up all the local objects // Mainly because some of the older games don't play well with a ton of objects on the thread local heap - // REFrameworkNET.API.LocalFrameGC(); + REFrameworkNET.API.LocalFrameGC(); return SyntaxFactory.SyntaxTree( compilationUnit.NormalizeWhitespace(), syntaxTreeParseOption, @@ -450,9 +443,6 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested typeof(REFrameworkNET.API).Assembly, [typeof(REFrameworkNET.TypeName).Assembly] ); - foreach (PortableExecutableReference dep in references) { - API.LogInfo($"compile dependency: {dep.Display}"); - } // Add the previous compilations as references foreach (var compilationbc in previousCompilations) { @@ -460,12 +450,14 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested references.Add(MetadataReference.CreateFromStream(ms)); } - //compilationUnit = compilationUnit.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System"))); - System.Console.WriteLine("Compiling " + strippedAssemblyName + " with " + syntaxTrees.Count + " syntax trees..."); var csoptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, + specificDiagnosticOptions: new Dictionary{ + // Ignore missing 'new' keyword, because it makes things much easier and we do want shadowing + ["CS0108"]=ReportDiagnostic.Suppress, + }, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default, platform: Platform.X64, allowUnsafe: true @@ -532,11 +524,15 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested public static List MainImpl() { var tdb = REFrameworkNET.API.GetTDB(); - Il2CppDump.FillTypeExtensions(tdb); - TypeHandler.BuildProperTypes(); + + // // var test = tdb.GetType("snow.enemy.EnemyCarryManagerBase`1.CarryInfo[[T, application, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]"); + // var test = tdb.GetType("snow.enemy.EnemyCarryManagerBase`1.CarryInfo"); + + // API.LogInfo($"{test.GenericArguments?.Count().ToString() ?? "NOPARAMS"} {test.IsGenericType()} {test.IsGenericTypeDefinition()} {test.RuntimeType?.ToString() ?? "NORUNTIME"}"); + // throw new Exception("TestDone"); + API.LogInfo($"Start generating assemblies"); List modules = []; - // First module is an invalid module for (uint i = 0; i < tdb.GetNumModules(); i++) { var module = tdb.GetModule(i); @@ -546,7 +542,8 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } modules.Add(module); } - + Il2CppDump.FillTypeExtensions(tdb); + TypeHandler.BuildProperTypes(); List bytecodes = []; foreach (Module module in modules) { @@ -577,5 +574,5 @@ public static bool NestedTypeExistsInParent(REFrameworkNET.TypeDefinition nested } return bytecodes; } -}; -} \ No newline at end of file +} +} diff --git a/csharp-api/AssemblyGenerator/TypeHandler.cs b/csharp-api/AssemblyGenerator/TypeHandler.cs new file mode 100644 index 000000000..bf48886c2 --- /dev/null +++ b/csharp-api/AssemblyGenerator/TypeHandler.cs @@ -0,0 +1,208 @@ +#nullable enable + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; +using REFrameworkNET; + +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using System; + +class TypeHandler { + public static TypeSyntax VoidType() => PredefinedType(Token(SyntaxKind.VoidKeyword)); + public static TypeSyntax ObjType() => PredefinedType(Token(SyntaxKind.ObjectKeyword)); + public static Dictionary Predefined = new() { + ["System.Single"] = ParseTypeName("float"), + ["System.Double"] = ParseTypeName("double"), + ["System.Int32"] = ParseTypeName("int"), + ["System.UInt32"] = ParseTypeName("uint"), + ["System.Int16"] = ParseTypeName("short"), + ["System.UInt16"] = ParseTypeName("ushort"), + ["System.Byte"] = ParseTypeName("byte"), + ["System.SByte"] = ParseTypeName("sbyte"), + ["System.Char"] = ParseTypeName("char"), + ["System.Int64"] = ParseTypeName("long"), + ["System.IntPtr"] = ParseTypeName("long"), + ["System.UInt64"] = ParseTypeName("ulong"), + ["System.UIntPtr"] = ParseTypeName("ulong"), + ["System.Boolean"] = ParseTypeName("bool"), + ["System.String"] = ParseTypeName("string"), + ["via.clr.ManagedObject"] = ObjType(), + ["System.Object"] = ObjType(), + ["System.Void"] = VoidType(), + ["!0"] = ParseTypeName("T"), + ["!1"] = ParseTypeName("U"), + ["!2"] = ParseTypeName("V"), + ["!3"] = ParseTypeName("W"), + ["!4"] = ParseTypeName("X"), + ["!5"] = ParseTypeName("Y"), + ["!6"] = ParseTypeName("Z"), + ["!7"] = ParseTypeName("P7"), + ["!8"] = ParseTypeName("P8"), + ["!9"] = ParseTypeName("P9"), + }; + + public static Dictionary Cache = new(); + + public static (string, int) BaseTypeName(string baseName) { + if (baseName == "file") { + return ("_file", 0); + } + if (baseName.Split('`').ToArray() is [var name, var count]) + return (name, int.Parse(count)); + + return (baseName, 0); + + } + public static bool SkipType(TypeDefinition type) { + if (type.Name == "") return true; + if (type.Name.StartsWith("<")) return true; + if (type.Name.Contains("!!")) return true; + + if (!type.IsGenericType()) { + if (NameHierarchy(type).Any(name => name.Contains('`'))) return true; + } + return false; + } + + public static BaseTypeSyntax[] ParentTypes(TypeDefinition type) { + List parents = new(); + var parentType = type.ParentType; + while (parentType != null) { + if (parentType.Name == "") break; + if (parentType.FullName == "System.Object") { + parents.Insert(0, SimpleBaseType(ParseTypeName("global::_System.Object"))); + break; + } + + var properType = ProperType(parentType); + if (!properType.IsEquivalentTo(ObjType())) { + parents.Insert(0, SimpleBaseType(properType)); + } + parentType = parentType.ParentType; + } + return parents.ToArray(); + } + + + public static TypeSyntax ProperType(TypeDefinition type) { + if (Predefined.ContainsKey(type.FullName)) + return Predefined[type.FullName]; + if (Cache.ContainsKey(type.Index)) + return Cache[type.Index]; + return ObjType(); + } + + public static string[] NameHierarchy(TypeDefinition type) { + var typeList = new List(); + while (true) { + typeList.Insert(0, type.Name); + if (type.DeclaringType is null || type.DeclaringType == type) + break; + type = type.DeclaringType; + } + if (type.Namespace is not null && type.Namespace.Any()) { + typeList.Insert(0, type.Namespace); + } else { + typeList.Insert(0, "_"); + } + return [.. typeList]; + } + + static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) { + if (targetType is null) return VoidType(); + if (Predefined.ContainsKey(targetType.FullName)) + return Predefined[targetType.FullName]; + if (Cache.ContainsKey(targetType.Index)) + return Cache[targetType.Index]; + + Cache[targetType.Index] = ObjType(); + if (targetType.GetElementType() is TypeDefinition elemType) { + Cache[targetType.Index] = ObjType(); + var elem = BuildProperType(elemType); + var arraySyntax = QualifiedName( + ParseName("global::_System.Array"), + GenericName("Impl") + .AddTypeArgumentListArguments([elem]) + ); + Cache[targetType.Index] = arraySyntax; + return arraySyntax; + } + if (SkipType(targetType)) return ObjType(); + + var typeList = NameHierarchy(targetType); + int genericIndex = 0; + var generics = targetType.GenericArguments ?? []; + var toParse = string.Join(".", typeList.Select(tName => { + var (name, count) = BaseTypeName(tName); + if (count == 0) return name; + name += "<"; + for (int i = 0; i < count; ++i) { + if (i > 0) name += ","; + if (i + genericIndex >= generics.Count()) { + name += "UNKN"; + continue; + } + var generic = generics[i + genericIndex]; + if (generic is null) + name += "object"; + else + name += ProperType(generics[i + genericIndex]).ToFullString(); + } + name += ">"; + genericIndex += count; + return name; + } + )); + if (toParse.StartsWith("System")) + toParse = "_" + toParse; + var parsed = ParseTypeName($"global::{toParse}"); + Cache[targetType.Index] = parsed; + return parsed; + } + + public static void BuildProperTypes() { + foreach (TypeDefinition type in API.GetTDB().Types) { + BuildProperType(type); + // Special case delegates again + if (!(type.FullName.StartsWith("System.Action") || type.FullName.StartsWith("System.Func"))) { + if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition()); + } + } + API.LogInfo($"Built {Cache.Count} proper types"); + } + + public static MemberDeclarationSyntax? GenerateType(TypeDefinition t) { + if (t.Name == "") return null; + if (t.FullName.EndsWith("[]")) return null; + if (SkipType(t)) return null; + if (t.IsGenericType() && !t.IsGenericTypeDefinition()) return null; + + try { + + // Enum + if (t.IsEnum()) { + var (baseName, _) = TypeHandler.BaseTypeName(t.Name); + var nestedEnumGenerator = new EnumGenerator(baseName, t); + return nestedEnumGenerator.EnumDeclaration; + } + var nestedGenerator = new ClassGenerator(t); + return nestedGenerator.TypeDeclaration; + } + catch (Exception e) { + throw new Exception($"{e.Message}\nWhile handling {t.FullName}", e); + + } + } + + public static MemberDeclarationSyntax[] GenerateNestedTypes(TypeDefinition t) { + var nestedTypes = Il2CppDump.GetTypeExtension(t)?.NestedTypes; + return nestedTypes? + .Select(GenerateType) + .Where(t => t is not null) + .Select(t => t!) + .ToArray() ?? []; + } +} diff --git a/csharp-api/CMakeLists.txt b/csharp-api/CMakeLists.txt index 0503a12cd..2ae6e2019 100644 --- a/csharp-api/CMakeLists.txt +++ b/csharp-api/CMakeLists.txt @@ -333,6 +333,7 @@ set(AssemblyGenerator_SOURCES "AssemblyGenerator/EnumGenerator.cs" "AssemblyGenerator/Generator.cs" "AssemblyGenerator/SyntaxTreeBuilder.cs" + "AssemblyGenerator/TypeHandler.cs" cmake.toml )