Refactoring and reorg of Program file

This commit is contained in:
Samuele Lorefice
2025-01-22 02:12:27 +01:00
parent d1742775ad
commit 56c5654c52

View File

@@ -10,175 +10,99 @@ using System.Reflection.Emit;
using System.Text; using System.Text;
using Microsoft.CSharp; using Microsoft.CSharp;
using BlendFile = Kaitai.BlenderBlend; using BlendFile = Kaitai.BlenderBlend;
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags // ReSharper disable BitwiseOperatorOnEnumWithoutFlags
namespace CodeGenerator { 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 class Program {
public static BlendFile blendfile; public static BlendFile blendfile;
private static StringBuilder sb = new(); 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) { public static void Log(string message) {
sb.AppendLine(message); sb.AppendLine(message);
Console.WriteLine(message); Console.WriteLine(message);
} }
public static void Main(string[] args) { public static void Main(string[] args) {
Log("Reading empty.blend file"); ReadBlendFile();
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");
Log("Generating C# code"); Log("Generating C# code");
//Create a new NameSpace Log("Pass 1: Generating types");
CodeNamespace ns = new CodeNamespace("Blender"); 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) { foreach (var type in blendfile.SdnaStructs) {
Log($"Generating struct {type.Type}"); Log($"Generating struct {type.Type}");
bool referenceSelf = false; bool referenceSelf = false;
bool referencePointer = false; bool referencePointer = false;
//Create a new type declaration //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) { foreach (var field in type.Fields) {
if(field.Name.Contains("*")) { if (field.Name.Contains("*")) {
referencePointer = true; referencePointer = true;
} }
if(field.Type.Contains(type.Type)) { if (field.Type.Contains(type.Type)) {
referenceSelf = true; referenceSelf = true;
} }
} }
if(referenceSelf || referencePointer) { if (referenceSelf || referencePointer) {
Log("Struct contains references"); Log("Struct contains references");
ctd.IsClass = true; ctd.IsClass = true;
} else { }
else {
ctd.IsStruct = true; ctd.IsStruct = true;
} }
//Add the class to the namespace //Add the class to the namespace
ns.Types.Add(ctd); ns.Types.Add(ctd);
//Add the fields to the class //Add the fields to the class
Log($"Fields: {type.Fields.Count}"); Log($"Fields: {type.Fields.Count}");
foreach (var field in type.Fields) { foreach (var field in type.Fields) {
CodeMemberField cmf; CodeMemberField cmf;
string name = field.Name; string name = field.Name;
if (field.Name.Contains("[")) { if (field.Name.Contains("[")) {
Log($"Generating array field {field.Name}"); Log($"Generating array field {field.Name}");
cmf = CreateArrayMemberField(field); cmf = CreateArrayMemberField(field);
} else { }
else {
Log($"Generating field {field.Name}"); Log($"Generating field {field.Name}");
cmf = CreateMemberField(field); cmf = CreateMemberField(field);
} }
ctd.Members.Add(cmf); ctd.Members.Add(cmf);
} }
Log("Generating constructor"); Log("Generating constructor");
ctd.Members.Add(GenerateConstructor(type, ctd)); ctd.Members.Add(GenerateConstructor(type, ctd));
Log("Finished generating struct"); Log("Finished generating struct");
} }
return ns;
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<CodeTypeDeclaration>()) {
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<CodeMemberField>()
.Where(f => f.Type.ArrayRank > 0)
.Select(f => {
var dims = new List<int>();
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<CodeStatement>());
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<CodeMemberField>()
.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<CodeMemberField>()
.Select(f => new CodeAssignStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name),
new CodeArgumentReferenceExpression(f.Name))
).ToArray<CodeStatement>());
return cc;
} }
private static CodeMemberField CreateMemberField(BlenderBlend.DnaField field) { private static CodeMemberField CreateMemberField(BlenderBlend.DnaField field) {
@@ -222,15 +146,88 @@ namespace CodeGenerator {
//Define the array type //Define the array type
cmf.Type.ArrayElementType = new(field.Type.ParseFType() ?? field.Type); cmf.Type.ArrayElementType = new(field.Type.ParseFType() ?? field.Type);
cmf.Type.ArrayRank = dimensions.Count; cmf.Type.ArrayRank = dimensions.Count;
//Define the array initialization expression //Define the array initialization expression
cmf.InitExpression = NewArray(cmf.Type, dimensions); cmf.InitExpression = GenerateArrayInitExpression(cmf.Type, dimensions);
return cmf; return cmf;
} }
public static CodeExpression NewArray (CodeTypeReference type, IEnumerable<int> dimensions) { public static CodeExpression GenerateArrayInitExpression(CodeTypeReference type, IEnumerable<int> dimensions) {
string dims = string.Concat(dimensions.Select(d => $"[{d}]")); string dims = string.Concat(dimensions.Select(d => $"[{d}]"));
return new CodeSnippetExpression($"new {type.BaseType}{dims}"); 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<CodeMemberField>()
.Where(f => f.Type.ArrayRank > 0)
.Select(f =>
{
var dims = new List<int>();
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<CodeStatement>());
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<CodeMemberField>()
.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<CodeMemberField>()
.Select(f => new CodeAssignStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name),
new CodeArgumentReferenceExpression(f.Name))
).ToArray<CodeStatement>());
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<CodeTypeDeclaration>()) {
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);
}
}
} }
} }