diff --git a/CodeGenerator/Program.cs b/CodeGenerator/Program.cs index 7f10cd1..7a302ed 100644 --- a/CodeGenerator/Program.cs +++ b/CodeGenerator/Program.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Text; using System.Threading; using Kaitai; -using static Kaitai.BlendFile; +using static Kaitai.BlendFile; using Microsoft.CSharp; // ReSharper disable BitwiseOperatorOnEnumWithoutFlags @@ -25,26 +25,28 @@ namespace CodeGenerator { private const string OutPath = @"GeneratedOutput"; private const string Namespace = "BlendFile"; private static HashSet _customTypes; - - private static readonly string[] ListMarkerStr = {"list", "array"}; - private static readonly string[] ListLenghtStr = {"num", "len", "size"}; - + + private static readonly string[] ListMarkerStr = { "list", "array" }; + private static readonly string[] ListLenghtStr = { "num", "len", "size" }; + private static ConcurrentQueue> _logQueue = new(); - + enum LogType { Info, Warning, Error } - + private static void LogNow(string message, LogType type = LogType.Info) { string msg = $"{DateTime.Now:yy-MM-dd HH:mm:ss}|{type.ToString()}|{message}"; - lock (Sb){ Sb.AppendLine(msg); } + lock (Sb) { + Sb.AppendLine(msg); + } Console.WriteLine(msg); } - + private static void Log(string message, LogType type = LogType.Info) { - _logQueue.Enqueue(new (message, type)); + _logQueue.Enqueue(new(message, type)); } public static void Main(string[] args) { @@ -52,27 +54,28 @@ namespace CodeGenerator { Stopwatch sw = new(); sw.Start(); long start = 0; - + bool loggerExit = false; - Thread logger = new(() => { + Thread logger = new(() => + { start = sw.ElapsedTicks; LogNow($"Logger started! In {sw.ElapsedMilliseconds}ms"); // ReSharper disable once AccessToModifiedClosure LoopVariableIsNeverChangedInsideLoop - while (!loggerExit || !_logQueue.IsEmpty) + while (!loggerExit || !_logQueue.IsEmpty) if (_logQueue.TryDequeue(out var msg)) LogNow(msg.Item1, msg.Item2); - + LogNow("Logger exiting..."); }); logger.Start(); var initTime = sw.ElapsedTicks; - + Log("Reading blend file"); sw.Restart(); ReadBlendFile(); var readTime = sw.ElapsedTicks; - + Log("Generating C# code..."); Log("Pass 1: Generating types"); @@ -86,20 +89,21 @@ namespace CodeGenerator { OutputCodeFiles(rootNs, false); var writeTime = sw.ElapsedTicks; sw.Stop(); - + Log("Finished generating C# code!"); Log($""" Timings: - Initialization: {(decimal)initTime/ TimeSpan.TicksPerMillisecond,10:N4} ms + Initialization: {(decimal)initTime / TimeSpan.TicksPerMillisecond,10:N4} ms Logger Startup: {(decimal)start / TimeSpan.TicksPerMillisecond,10:N4} ms - Reading: {(decimal)readTime/ TimeSpan.TicksPerMillisecond,10:N4} ms - Generating: {(decimal)genTime/ TimeSpan.TicksPerMillisecond,10:N4} ms - Writing: {(decimal)writeTime/ TimeSpan.TicksPerMillisecond,10:N4} ms + Reading: {(decimal)readTime / TimeSpan.TicksPerMillisecond,10:N4} ms + Generating: {(decimal)genTime / TimeSpan.TicksPerMillisecond,10:N4} ms + Writing: {(decimal)writeTime / TimeSpan.TicksPerMillisecond,10:N4} ms ---------------------------- - Total: {(decimal)(initTime+readTime+genTime+writeTime) / TimeSpan.TicksPerMillisecond,10:N4} ms + Total: {(decimal)(initTime + readTime + genTime + writeTime) / TimeSpan.TicksPerMillisecond,10:N4} ms """); loggerExit = true; - while(logger.IsAlive) { } + while (logger.IsAlive) { + } Thread.Sleep(1000); lock (Sb) { File.AppendAllText("Log.txt", Sb.ToString()); @@ -115,8 +119,7 @@ namespace CodeGenerator { $"DNA1: {_blendfile.SdnaStructs.Count} structures\n"); } - private static int AddNormalField(DnaField field, ref CodeTypeDeclaration ctd, int index, int totalSize) - { + private static int AddNormalField(DnaField field, ref CodeTypeDeclaration ctd, int index, int totalSize) { CodeMemberField cmf; string name = field.Name; if (name.Contains("()")) return 0; @@ -143,8 +146,8 @@ namespace CodeGenerator { ctd.Members.Add(cmf); return size; } - - private static int AddListField(ref CodeTypeDeclaration ctd, int totalSize, int index, int listLenghtOffset, + + private static int AddListField(ref CodeTypeDeclaration ctd, int totalSize, int index, int listLenghtOffset, DnaField listPointer, DnaField listLength, int sizeIndex) { var cmf = CreateListMemberField(listPointer, listLength); @@ -153,19 +156,19 @@ namespace CodeGenerator { cmf.CustomAttributes.Add(attribute); ctd.Members.Add(cmf); - + return size; } - - + + private static CodeNamespace GenerateTypes(out CodeNamespace additionalNs) { //Initialize the namespaces CodeNamespace rootNs = new CodeNamespace(Namespace); - CodeNamespace ns = new CodeNamespace(Namespace+".DNA"); + CodeNamespace ns = new CodeNamespace(Namespace + ".DNA"); //Fill the attribute types then add them to the namespaces rootNs.Types.AddRange(GenerateTypeDeclarations()); ns.Imports.Add(new(rootNs.Name)); - + _customTypes = new(); foreach (var type in _blendfile.SdnaStructs) { @@ -174,18 +177,17 @@ namespace CodeGenerator { //Create a new type declaration var ctd = new CodeTypeDeclaration(type.Type); - ctd.CustomAttributes.Add(new ("DNAClassAttribute", + ctd.CustomAttributes.Add(new("DNAClassAttribute", new CodeAttributeArgument(new CodePrimitiveExpression(type.IdxType)), new CodeAttributeArgument(new CodePrimitiveExpression(type.Type)) )); - + Log($"Generating type from struct {type.Type}"); - - if(IsClass(type)) { + + if (IsClass(type)) { Log($"Marking {type.Type} as class"); ctd.IsClass = true; - } - else { + } else { Log($"Marking {type.Type} as struct"); ctd.IsStruct = !ctd.IsClass; } @@ -197,14 +199,14 @@ namespace CodeGenerator { List normalFields; //Fields that are not lists nor lengths of lists List<(DnaField, DnaField)> listFields; //Fields that are lists, and their corresponding length fields // ReSharper restore InlineOutVariableDeclaration - + //filter the fields we want to include in the class minus the lists FilterFields(type.Fields, out normalFields, out listFields); - + var totalSize = 0; - + Dictionary listCountOffsets = new(); - + //Add the fields to the class Log($"Fields: {type.Fields.Count}"); for (var index = 0; index < type.Fields.Count; index++) { @@ -212,7 +214,7 @@ namespace CodeGenerator { //Check if the field is a normal field or a list field if (normalFields.Contains(field) && !listFields.Select(f => f.Item2).Contains(field)) { //check if the field is an array - if (field.Name.Contains('[')) + if (field.Name.Contains('[')) totalSize += AddArrayField(ref ctd, field, index, totalSize); else totalSize += AddNormalField(field, ref ctd, index, totalSize); @@ -220,39 +222,42 @@ namespace CodeGenerator { //Retrieve the list pointer and the list length fields var (listPointer, listLength) = listFields.FirstOrDefault(x => x.Item1 == field); Log($"Generating list field {listPointer.Name}"); - + //retrieve the offset of the list length field if exists listCountOffsets.TryGetValue(listLength.Name.ParseFName(), out int listLenghtOffset); - + //Retrieve the index of the list length field int sizeIndex = type.Fields.IndexOf(listLength); - totalSize += AddListField(ref ctd, totalSize, index, listLenghtOffset, listPointer, listLength, sizeIndex); + totalSize += AddListField(ref ctd, totalSize, index, listLenghtOffset, listPointer, listLength, + sizeIndex); } else if (listFields.Select(f => f.Item2).Contains(field)) { //update the size of the list attribute string fName = field.Name.ParseFName(); //retrieve the name of the list pointer - string listPointerName = listFields.First(f => f.Item2.Name.ParseFName() == fName).Item1.Name.ParseFName(); + string listPointerName = + listFields.First(f => f.Item2.Name.ParseFName() == fName).Item1.Name.ParseFName(); //Try seeing if the list attribute is already present - var x = ctd.Members.OfType().FirstOrDefault(member => member.Name.ParseFName() == listPointerName); - + var x = ctd.Members.OfType() + .FirstOrDefault(member => member.Name.ParseFName() == listPointerName); + if (x != null) //Update the existing list attribute x.CustomAttributes[0].Arguments[9] = new(new CodePrimitiveExpression(totalSize)); else //Store the data for when the list attribute is made listCountOffsets.Add(fName, totalSize); - + totalSize += field.Type.ParseFSize(); } else { Log($"Field {field.Name} is of unknown or unsupported type"); } } - - ctd.CustomAttributes[0].Arguments.Add(new (new CodePrimitiveExpression(totalSize))); - + + ctd.CustomAttributes[0].Arguments.Add(new(new CodePrimitiveExpression(totalSize))); + Log("Generating Parameterless constructor"); - if(ctd.Members.Count > 0) + if (ctd.Members.Count > 0) ctd.Members.Add(GenerateParameterlessConstructor(type, ctd)); - + Log("Generating Default Constructor"); ctd.Members.Add(GenerateConstructor(type, ctd)); @@ -281,8 +286,8 @@ namespace CodeGenerator { Log($"No pointer or self reference detected in {type.Type}"); return false; } - - + + /// /// Filters the fields into normal fields and list fields pairs /// @@ -293,43 +298,42 @@ namespace CodeGenerator { out List normalFields, out List<(DnaField, DnaField)> listFields) { normalFields = new(); //Fields that are not lists nor lengths of lists listFields = new(); //Fields that are lists, and their corresponding length fields - + //Cast to array the fields to avoid multiple enumerations var dnaFields = fields as DnaField[] ?? fields.ToArray(); foreach (var field in dnaFields) { - if (ListMarkerStr.Any(s => field.Name.Contains(s)) && + if (ListMarkerStr.Any(s => field.Name.Contains(s)) && !ListLenghtStr.Any(s2 => field.Name.Contains(s2))) { - Log($"Found list field {field.Name}"); Log($"Searching for list length field"); - - var listLengthField = dnaFields.FirstOrDefault(f => f.Name.Contains(field.Name.ParseFName()) && - ListLenghtStr.Any(s2 => f.Name.Contains(s2))); + + var listLengthField = dnaFields.FirstOrDefault(f => f.Name.Contains(field.Name.ParseFName()) && + ListLenghtStr.Any(s2 => f.Name.Contains(s2))); if (listLengthField == null) Log($"No list length field found for {field.Name}"); else { Log($"Found list length field {listLengthField.Name}"); listFields.Add((field, listLengthField)); - + //Remove the list length field from the normal fields (if present) - if(normalFields.Remove(listLengthField)) + if (normalFields.Remove(listLengthField)) Log($"Removed list length field {listLengthField.Name}"); } continue; } - + //Skip fields that are recognized as listLengths if (listFields.Select(f => f.Item2).Contains(field)) { Log($"Skipping known list length field {field.Name}"); continue; } - + Log($"Adding normal field {field.Name}"); normalFields.Add(field); } } - + /// /// Generates the following attribute types DNAAttribute, DNAFieldAttribute, DNAClassAttribute, DNAListAttribute /// @@ -337,17 +341,17 @@ namespace CodeGenerator { /// This internally uses a single instance of and sequentially generates the various attributes. private static CodeTypeDeclaration[] GenerateTypeDeclarations() { var attributeBuilder = new AttributeBuilder(); - + var typeDeclarations = new[] { attributeBuilder.New().SetName("DNAAttribute") - .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.All"))) + .SetAttributeUsage(new(new CodeSnippetExpression("AttributeTargets.All"))) .DeriveFromClass() .AddAutoProperty(typeof(int), "OriginalIndex") .AddAutoProperty(typeof(string), "OriginalName") .AddPropertiesConstructor() .Build(), attributeBuilder.New().SetName("DNAFieldAttribute") - .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Field"))) + .SetAttributeUsage(new(new CodeSnippetExpression("AttributeTargets.Field"))) .DeriveFromClass($"{Namespace}.DNAAttribute") .AddAutoProperty(typeof(int), "Size") .AddAutoProperty(typeof(string), "OriginalType") @@ -360,7 +364,7 @@ namespace CodeGenerator { .AddBaseConstructorParams(["OriginalIndex", "OriginalName"]) .Build(), attributeBuilder.New().SetName("DNAArrayAttribute") - .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Field"))) + .SetAttributeUsage(new(new CodeSnippetExpression("AttributeTargets.Field"))) .DeriveFromClass($"{Namespace}.DNAAttribute") .AddAutoProperty(typeof(int), "Size") .AddAutoProperty(typeof(string), "OriginalType") @@ -373,7 +377,7 @@ namespace CodeGenerator { .AddBaseConstructorParams(["OriginalIndex", "OriginalName"]) .Build(), attributeBuilder.New().SetName("DNAClassAttribute") - .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Class | AttributeTargets.Struct"))) + .SetAttributeUsage(new(new CodeSnippetExpression("AttributeTargets.Class | AttributeTargets.Struct"))) .DeriveFromClass($"{Namespace}.DNAAttribute") .AddAutoProperty(typeof(int), "OriginalIndex") .AddAutoProperty(typeof(string), "OriginalName") @@ -382,7 +386,7 @@ namespace CodeGenerator { .AddBaseConstructorParams(["OriginalIndex", "OriginalName"]) .Build(), attributeBuilder.New().SetName("DNAListAttribute") - .SetAttributeUsage(new (new CodeSnippetExpression("AttributeTargets.Property | AttributeTargets.Field"))) + .SetAttributeUsage(new(new CodeSnippetExpression("AttributeTargets.Property | AttributeTargets.Field"))) .DeriveFromClass($"{Namespace}.DNAAttribute") .AddAutoProperty(typeof(int), "Size") .AddAutoProperty(typeof(string), "OriginalType") @@ -408,12 +412,11 @@ namespace CodeGenerator { size = body.Lengths[field.IdxType]; string t = field.Type; - if (field.Name.StartsWith('*')) - { + if (field.Name.StartsWith('*')) { size = 8; isPointer = true; } - + CodeAttributeDeclaration cad = new("DNAFieldAttribute"); cad.Arguments.AddRange(new CodeAttributeArgumentCollection() { new(new CodePrimitiveExpression(size)), @@ -433,24 +436,24 @@ namespace CodeGenerator { size = body.Lengths[field.IdxType]; //Generate the array declaration again... to grab the base type CodeMemberField amf = CreateArrayMemberField(field); - + //Generate the type string var sb = new StringBuilder(); sb.Append(amf.Type.BaseType); sb.Append('['); - for(int i=1; i)); - + //Check if the type is a built-in type or a custom type - if (t != null) { //Built-in type + if (t != null) { + //Built-in type ctr.TypeArguments.Add(t); cmf = new(ctr, field.Name.ParseFName()); - } else { //Custom type + } else { + //Custom type ctr.TypeArguments.Add(new CodeTypeReference(field.Type)); cmf = new(ctr, field.Name.ParseFName()); } cmf.Attributes = MemberAttributes.Public; return cmf; } - - private static List GetArrayDimensions(string name) - { + + private static List GetArrayDimensions(string name) { var dimensions = new List(); int startIndex = 0; // Get all array dimensions @@ -556,7 +559,7 @@ namespace CodeGenerator { } startIndex = endIndex + 1; } - + return dimensions; } @@ -617,7 +620,7 @@ namespace CodeGenerator { return cc; } - + private static CodeConstructor GenerateParameterlessConstructor(DnaStruct type, CodeTypeDeclaration ctd) { //Create a normal constructor CodeConstructor cc = new CodeConstructor { @@ -673,7 +676,7 @@ namespace CodeGenerator { provider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions); tempNs.Types.Remove(type); } - + if (!outputExtraTypes) return; _customTypes.ExceptWith(ns.Types.OfType().Select(t => t.Name)); foreach (var type in _customTypes) {