diff --git a/CodeGenerator/Program.cs b/CodeGenerator/Program.cs index 9fb5692..593fc28 100644 --- a/CodeGenerator/Program.cs +++ b/CodeGenerator/Program.cs @@ -10,175 +10,99 @@ using System.Reflection.Emit; using System.Text; using Microsoft.CSharp; using BlendFile = Kaitai.BlenderBlend; + // ReSharper disable BitwiseOperatorOnEnumWithoutFlags namespace CodeGenerator { - - public static class StrExt { - public static string ParseFName(this string str) { - str = str.Replace("*", "ptr_"); - return str; - } - - public static string ParseFType(this string str) { - return str switch { - "char" => typeof(char).AssemblyQualifiedName, - "short" => typeof(short).AssemblyQualifiedName, - "int" => typeof(int).AssemblyQualifiedName, - "float" => typeof(float).AssemblyQualifiedName, - "double" => typeof(double).AssemblyQualifiedName, - "string" => typeof(string).AssemblyQualifiedName, - "void" => typeof(object).AssemblyQualifiedName, - _ => str - }; - } - } - public class Program { public static BlendFile blendfile; private static StringBuilder sb = new(); - + private const string OutPath = @"Blendfile\DNA"; + private const string Namespace = "BlendFile.DNA"; + private static readonly string[] AdaptedTypes = new[] { "uchar" }; + public static void Log(string message) { sb.AppendLine(message); Console.WriteLine(message); } - + public static void Main(string[] args) { - Log("Reading empty.blend file"); - blendfile = BlendFile.FromFile("empty.blend"); - - Log($"Header: Blender v{blendfile.Hdr.Version} {blendfile.Hdr.Endian}\n" + - $"DataBlocks: {blendfile.Blocks.Count}\n" + - $"DNA1: {blendfile.SdnaStructs.Count} structures\n"); + ReadBlendFile(); Log("Generating C# code"); - //Create a new NameSpace - CodeNamespace ns = new CodeNamespace("Blender"); + Log("Pass 1: Generating types"); + CodeNamespace ns = GenerateTypes(); - Log("Pass 1: Generating structs"); + Log("Pass 2: Writing out code"); + OutputCodeFiles(ns); + Log("Finished generating C# code"); + File.AppendAllText("Log.txt", sb.ToString()); + } + + private static void ReadBlendFile() { + Log("Reading empty.blend file"); + blendfile = BlendFile.FromFile("empty.blend"); + + Log($"Header: Blender v{blendfile.Hdr.Version} {blendfile.Hdr.Endian}\n" + + $"DataBlocks: {blendfile.Blocks.Count}\n" + + $"DNA1: {blendfile.SdnaStructs.Count} structures\n"); + } + + private static CodeNamespace GenerateTypes() { + CodeNamespace ns = new CodeNamespace(Namespace); foreach (var type in blendfile.SdnaStructs) { Log($"Generating struct {type.Type}"); bool referenceSelf = false; bool referencePointer = false; - + //Create a new type declaration - var ctd = new CodeTypeDeclaration(type.Type);// { IsStruct = true }; - + var ctd = new CodeTypeDeclaration(type.Type); // { IsStruct = true }; + foreach (var field in type.Fields) { - if(field.Name.Contains("*")) { + if (field.Name.Contains("*")) { referencePointer = true; } - if(field.Type.Contains(type.Type)) { + if (field.Type.Contains(type.Type)) { referenceSelf = true; } } - - if(referenceSelf || referencePointer) { + + if (referenceSelf || referencePointer) { Log("Struct contains references"); ctd.IsClass = true; - } else { + } + else { ctd.IsStruct = true; } - + //Add the class to the namespace ns.Types.Add(ctd); + //Add the fields to the class Log($"Fields: {type.Fields.Count}"); - foreach (var field in type.Fields) { CodeMemberField cmf; string name = field.Name; if (field.Name.Contains("[")) { Log($"Generating array field {field.Name}"); cmf = CreateArrayMemberField(field); - } else { + } + else { Log($"Generating field {field.Name}"); cmf = CreateMemberField(field); } - + ctd.Members.Add(cmf); } - + Log("Generating constructor"); ctd.Members.Add(GenerateConstructor(type, ctd)); - + Log("Finished generating struct"); } - - Log("Pass 2: Writing out code"); - CodeNamespace tempNs = new CodeNamespace("BlendFile.DNA"); - if(!Path.Exists("Blendfile\\DNA")) { - Directory.CreateDirectory("Blendfile\\DNA"); - } - - CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions() { - BlankLinesBetweenMembers = false, - BracingStyle = "Block", - ElseOnClosing = false, - IndentString = " ", - VerbatimOrder = false - }; - var provider = new CSharpCodeProvider(); - - foreach (var type in ns.Types.OfType()) { - tempNs.Types.Add(type); - Log($"Writing out {(type.IsStruct ? "struct" : "class")} {type.Name}"); - using var sw = new StreamWriter($"Blender\\{type.Name}.cs"); - provider.GenerateCodeFromNamespace(tempNs, sw, codeGeneratorOptions); - tempNs.Types.Remove(type); - } - Log("Finished generating C# code"); - - File.AppendAllText("Log.txt", sb.ToString()); - } - - private static CodeTypeConstructor GenerateStaticConstructor(CodeTypeDeclaration ctd) { - CodeTypeConstructor ctc = new CodeTypeConstructor(); - ctc.Attributes = MemberAttributes.Static; - ctc.Statements.AddRange(ctd.Members - .OfType() - .Where(f => f.Type.ArrayRank > 0) - .Select(f => { - var dims = new List(); - for (int i = 0; i < f.Type.ArrayRank; i++) { - dims.Add(0); - } - return new CodeAssignStatement( - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name), - NewArray(f.Type, dims) - ); - }).ToArray()); - return ctc; - } - - private static CodeConstructor GenerateConstructor(BlenderBlend.DnaStruct type, CodeTypeDeclaration ctd) { - //Create a normal constructor - CodeConstructor cc = new CodeConstructor { - Name = type.Type, - Attributes = MemberAttributes.Public, - ReturnType = new(type.Type) - }; - - //Add the parameters to the constructor - cc.Parameters.AddRange(ctd.Members - .OfType() - .Select(f => { - var cpde = new CodeParameterDeclarationExpression(f.Type, f.Name); - cpde.Direction = FieldDirection.In; - return cpde; - }).ToArray()); - - //Assign the parameters to the respective fields - cc.Statements.AddRange(ctd.Members - .OfType() - .Select(f => new CodeAssignStatement( - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name), - new CodeArgumentReferenceExpression(f.Name)) - ).ToArray()); - - return cc; + return ns; } private static CodeMemberField CreateMemberField(BlenderBlend.DnaField field) { @@ -222,15 +146,88 @@ namespace CodeGenerator { //Define the array type cmf.Type.ArrayElementType = new(field.Type.ParseFType() ?? field.Type); cmf.Type.ArrayRank = dimensions.Count; - + //Define the array initialization expression - cmf.InitExpression = NewArray(cmf.Type, dimensions); + cmf.InitExpression = GenerateArrayInitExpression(cmf.Type, dimensions); return cmf; } - - public static CodeExpression NewArray (CodeTypeReference type, IEnumerable dimensions) { + + public static CodeExpression GenerateArrayInitExpression(CodeTypeReference type, IEnumerable dimensions) { string dims = string.Concat(dimensions.Select(d => $"[{d}]")); return new CodeSnippetExpression($"new {type.BaseType}{dims}"); } + + private static CodeTypeConstructor GenerateStaticConstructor(CodeTypeDeclaration ctd) { + CodeTypeConstructor ctc = new CodeTypeConstructor(); + ctc.Attributes = MemberAttributes.Static; + ctc.Statements.AddRange(ctd.Members + .OfType() + .Where(f => f.Type.ArrayRank > 0) + .Select(f => + { + var dims = new List(); + for (int i = 0; i < f.Type.ArrayRank; i++) { + dims.Add(0); + } + return new CodeAssignStatement( + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name), + GenerateArrayInitExpression(f.Type, dims) + ); + }).ToArray()); + return ctc; + } + + private static CodeConstructor GenerateConstructor(BlenderBlend.DnaStruct type, CodeTypeDeclaration ctd) { + //Create a normal constructor + CodeConstructor cc = new CodeConstructor { + Name = type.Type, + Attributes = MemberAttributes.Public, + ReturnType = new(type.Type) + }; + + //Add the parameters to the constructor + cc.Parameters.AddRange(ctd.Members + .OfType() + .Select(f => + { + var cpde = new CodeParameterDeclarationExpression(f.Type, f.Name); + cpde.Direction = FieldDirection.In; + return cpde; + }).ToArray()); + + //Assign the parameters to the respective fields + cc.Statements.AddRange(ctd.Members + .OfType() + .Select(f => new CodeAssignStatement( + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name), + new CodeArgumentReferenceExpression(f.Name)) + ).ToArray()); + + return cc; + } + + private static void OutputCodeFiles(CodeNamespace ns) { + CodeNamespace tempNs = new CodeNamespace(Namespace); + if (!Path.Exists(OutPath)) { + Directory.CreateDirectory(OutPath); + } + + CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions() { + BlankLinesBetweenMembers = false, + BracingStyle = "Block", + ElseOnClosing = false, + IndentString = " ", + VerbatimOrder = false + }; + var provider = new CSharpCodeProvider(); + + foreach (var type in ns.Types.OfType()) { + tempNs.Types.Add(type); + Log($"Writing out {(type.IsStruct ? "struct" : "class")} {type.Name}"); + using var sw = new StreamWriter($"{OutPath}\\{type.Name}.cs"); + provider.GenerateCodeFromNamespace(tempNs, sw, codeGeneratorOptions); + tempNs.Types.Remove(type); + } + } } } \ No newline at end of file