Base files
This commit is contained in:
212
CodeGenerator/Program.cs
Normal file
212
CodeGenerator/Program.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using Kaitai;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using Microsoft.CSharp;
|
||||
using BlendFile = Kaitai.BlenderBlend;
|
||||
|
||||
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 StringBuilder();
|
||||
|
||||
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");
|
||||
|
||||
Log("Generating C# code");
|
||||
|
||||
CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions() {
|
||||
BlankLinesBetweenMembers = false,
|
||||
BracingStyle = "Block",
|
||||
ElseOnClosing = false,
|
||||
IndentString = " ",
|
||||
VerbatimOrder = false
|
||||
};
|
||||
|
||||
var provider = new CSharpCodeProvider();
|
||||
|
||||
//Create a new NameSpace
|
||||
CodeNamespace ns = new CodeNamespace("Blender");
|
||||
|
||||
Log("Pass 1: Generating structs");
|
||||
|
||||
foreach (var type in blendfile.SdnaStructs) {
|
||||
bool hasArrays = false;
|
||||
Log($"Generating struct {type.Type}");
|
||||
//Create a new structs
|
||||
var ctd = new CodeTypeDeclaration(type.Type) { 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}");
|
||||
hasArrays = true;
|
||||
cmf = CreateArrayMemberField(field);
|
||||
} else {
|
||||
Log($"Generating field {field.Name}");
|
||||
cmf = CreateMemberField(field);
|
||||
}
|
||||
|
||||
cmf.Attributes = MemberAttributes.Public;
|
||||
ctd.Members.Add(cmf);
|
||||
}
|
||||
|
||||
Log("Generating constructor");
|
||||
ctd.Members.Add(GenerateConstructor(type, ctd));
|
||||
|
||||
//If it has arrays, generate a static constructor for them
|
||||
if (hasArrays) {
|
||||
Log("Generating static constructor");
|
||||
ctd.Members.Add(GenerateStaticConstructor(ctd));
|
||||
}
|
||||
|
||||
Log("Finished generating struct");
|
||||
}
|
||||
using var sw = new StreamWriter("BlenderSharp.cs");
|
||||
provider.GenerateCodeFromNamespace(ns, sw, codeGeneratorOptions);
|
||||
Log("Finished generating C# code");
|
||||
Log("Code saved to BlenderSharp.cs");
|
||||
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) {
|
||||
Type t = Type.GetType(field.Type.ParseFType());
|
||||
CodeMemberField cmf;
|
||||
//Check if the type is a built-in type or a custom type
|
||||
if (t != null) cmf = new(t, field.Name.ParseFName()); //Built-in type
|
||||
else cmf = new(new CodeTypeReference(field.Type), field.Name.ParseFName()); //Custom type
|
||||
return cmf;
|
||||
}
|
||||
|
||||
private static CodeMemberField CreateArrayMemberField(BlenderBlend.DnaField field) {
|
||||
Type t = Type.GetType(field.Type.ParseFType());
|
||||
CodeMemberField cmf;
|
||||
|
||||
// Parse all array dimensions
|
||||
var dimensions = new List<int>();
|
||||
var name = field.Name.ParseFName();
|
||||
int startIndex = 0;
|
||||
|
||||
// Get all array dimensions
|
||||
while ((startIndex = name.IndexOf('[', startIndex)) != -1) {
|
||||
int endIndex = name.IndexOf(']', startIndex);
|
||||
string sizeStr = name.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||
if (int.TryParse(sizeStr, out int size)) {
|
||||
dimensions.Add(size);
|
||||
}
|
||||
startIndex = endIndex + 1;
|
||||
}
|
||||
|
||||
// Get clean field name (without array brackets)
|
||||
name = field.Name.ParseFName().Substring(0, field.Name.IndexOf('['));
|
||||
|
||||
//Check if the type is a built-in type or a custom type
|
||||
if (t != null) cmf = new(t, name); //Built-in type
|
||||
else cmf = new(field.Type, name); //Custom type
|
||||
|
||||
//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);
|
||||
return cmf;
|
||||
}
|
||||
|
||||
public static CodeExpression NewArray (CodeTypeReference type, IEnumerable<int> dimensions) {
|
||||
string dims = string.Concat(dimensions.Select(d => $"[{d}]"));
|
||||
return new CodeSnippetExpression($"new {type.BaseType}{dims}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user