diff --git a/csharp-api/AssemblyGenerator/EnumGenerator.cs b/csharp-api/AssemblyGenerator/EnumGenerator.cs new file mode 100644 index 000000000..456a260ee --- /dev/null +++ b/csharp-api/AssemblyGenerator/EnumGenerator.cs @@ -0,0 +1,96 @@ +#nullable enable + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Reflection; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Emit; +using System.IO; +using System.Dynamic; +using System.Security.Cryptography; +using System.Linq; +using Microsoft.CodeAnalysis.Operations; +using REFrameworkNET; + +public class EnumGenerator { + private string enumName; + private REFrameworkNET.TypeDefinition t; + private EnumDeclarationSyntax? enumDeclaration; + + public EnumDeclarationSyntax? EnumDeclaration { + get { + return enumDeclaration; + } + } + + public EnumGenerator(string enumName, REFrameworkNET.TypeDefinition t) { + this.enumName = enumName; + this.t = t; + + enumDeclaration = Generate(); + } + + public void Update(EnumDeclarationSyntax? typeDeclaration) { + this.enumDeclaration = typeDeclaration; + } + static readonly REFrameworkNET.ManagedObject s_FlagsAttribute = REFrameworkNET.TDB.Get().FindType("System.FlagsAttribute").GetRuntimeType(); + + public EnumDeclarationSyntax? Generate() { + var ogEnumName = new string(enumName); + + // Pull out the last part of the class name (split '.' till last) + if (t.DeclaringType == null) { + enumName = enumName.Split('.').Last(); + } + + enumDeclaration = SyntaxFactory.EnumDeclaration(enumName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); + + if (t.HasAttribute(s_FlagsAttribute, true)) { + enumDeclaration = enumDeclaration.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute(SyntaxFactory.ParseName("System.FlagsAttribute")))); + } + + foreach (REFrameworkNET.Field field in t.Fields) { + if (!field.IsStatic() || !field.IsLiteral()) { + continue; + } + + if (field.GetDeclaringType() != t) { + continue; + } + + var underlyingType = field.Type.GetUnderlyingType(); + + SyntaxToken literalToken; + + switch (underlyingType.GetValueTypeSize()) { + case 1: + enumDeclaration = enumDeclaration.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("byte"))); + literalToken = SyntaxFactory.Literal(field.GetDataT(0, false)); + break; + case 2: + enumDeclaration = enumDeclaration.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("short"))); + literalToken = SyntaxFactory.Literal(field.GetDataT(0, false)); + break; + case 4: + enumDeclaration = enumDeclaration.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("int"))); + literalToken = SyntaxFactory.Literal(field.GetDataT(0, false)); + break; + case 8: + enumDeclaration = enumDeclaration.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("long"))); + literalToken = SyntaxFactory.Literal(field.GetDataT(0, false)); + break; + default: + throw new System.Exception("Unknown enum underlying type size"); + } + + var fieldDeclaration = SyntaxFactory.EnumMemberDeclaration(field.Name); + var valueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, literalToken); + fieldDeclaration = fieldDeclaration.WithEqualsValue(SyntaxFactory.EqualsValueClause(valueExpr)); + enumDeclaration = enumDeclaration.AddMembers(fieldDeclaration); + } + + return enumDeclaration; + } +} \ No newline at end of file diff --git a/csharp-api/AssemblyGenerator/Generator.cs b/csharp-api/AssemblyGenerator/Generator.cs index b2f6f37a6..fae6eea35 100644 --- a/csharp-api/AssemblyGenerator/Generator.cs +++ b/csharp-api/AssemblyGenerator/Generator.cs @@ -277,70 +277,57 @@ static CompilationUnitSyntax MakeFromTypeEntry(REFrameworkNET.TDB context, strin generatedTypes.Add(typeName); - // Generate starting from topmost parent first - if (t.ParentType != null) { - compilationUnit = MakeFromTypeEntry(context, t.ParentType.FullName ?? "", t.ParentType); - } + if (t.IsEnum()) { + var generator = new EnumGenerator(typeName, t); - /*var methods = t.Methods; - var fixedMethods = methods? - .Select(methodPair => { - var method = methodPair.Value; - var methodName = Il2CppDump.StripMethodName(method); - return (methodName, method); - }) - .GroupBy(pair => pair.methodName) - .Select(group => group.First()) // Selects the first method of each group - .ToDictionary(pair => pair.methodName, pair => pair.method);*/ - - // Make methods a SortedSet of method names - HashSet methods = []; - - foreach (var method in t.Methods) { - //methods.Add(method); - if (!methods.Select(m => m.Name).Contains(method.Name)) { - if (method.DeclaringType == t) { // really important - methods.Add(method); - } + if (generator.EnumDeclaration == null) { + return compilationUnit; } - } - var generator = new ClassGenerator( - typeName.Split('.').Last() == "file" ? typeName.Replace("file", "@file") : typeName, - t, - [.. methods] - ); + var generatedNamespace = ExtractNamespaceFromType(t); - if (generator.TypeDeclaration == 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); + } + } else { + // Generate starting from topmost parent first + if (t.ParentType != null) { + compilationUnit = MakeFromTypeEntry(context, t.ParentType.FullName ?? "", t.ParentType); + } - if (generatedNamespace != null) { - // Split the using types by their namespace - /*foreach(var ut in usingTypes) { - var ns = ExtractNamespaceFromTypeName(context, ut.Name ?? ""); + // Make methods a SortedSet of method names + HashSet methods = []; - if (ns != null) { - generatedNamespace = generatedNamespace.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ut.Name ?? ""))); + foreach (var method in t.Methods) { + //methods.Add(method); + if (!methods.Select(m => m.Name).Contains(method.Name)) { + if (method.DeclaringType == t) { // really important + methods.Add(method); + } } - }*/ + } - var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, generator.TypeDeclaration); + var generator = new ClassGenerator( + typeName.Split('.').Last() == "file" ? typeName.Replace("file", "@file") : typeName, + t, + [.. methods] + ); - /*compilationUnit = compilationUnit.AddUsings(usingTypes.Select( - type => { - var ret = SyntaxFactory.UsingDirective (SyntaxFactory.ParseName(type.Name ?? "")); - System.Console.WriteLine(ret.GetText()); + if (generator.TypeDeclaration == null) { + return compilationUnit; + } - return ret; - } - ).ToArray());*/ + var generatedNamespace = ExtractNamespaceFromType(t); - compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); - } else { - Console.WriteLine("Failed to create namespace for " + typeName); + if (generatedNamespace != null) { + var myNamespace = SyntaxTreeBuilder.AddMembersToNamespace(generatedNamespace, generator.TypeDeclaration); + compilationUnit = SyntaxTreeBuilder.AddMembersToCompilationUnit(compilationUnit, myNamespace); + } else { + Console.WriteLine("Failed to create namespace for " + typeName); + } } return compilationUnit; diff --git a/csharp-api/CMakeLists.txt b/csharp-api/CMakeLists.txt index eebfce4b1..b313892fe 100644 --- a/csharp-api/CMakeLists.txt +++ b/csharp-api/CMakeLists.txt @@ -239,6 +239,7 @@ set_target_properties(csharp-api PROPERTIES VS_PACKAGE_REFERENCES "REFCSharpComp # Target: AssemblyGenerator set(AssemblyGenerator_SOURCES "AssemblyGenerator/ClassGenerator.cs" + "AssemblyGenerator/EnumGenerator.cs" "AssemblyGenerator/Generator.cs" "AssemblyGenerator/SyntaxTreeBuilder.cs" cmake.toml