Added runtime generation of the attribute classes and marking of field metadata
This commit is contained in:
14
.idea/.idea.BlenderSharp/.idea/statistic.xml
generated
Normal file
14
.idea/.idea.BlenderSharp/.idea/statistic.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Statistic">
|
||||||
|
<option name="excludeDotArtifactDirectory" value="true" />
|
||||||
|
<option name="excludedDirectories">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/CodeGenerator/bin" />
|
||||||
|
<option value="$PROJECT_DIR$/CodeGenerator/obj" />
|
||||||
|
<option value="$PROJECT_DIR$/BlendFile/bin" />
|
||||||
|
<option value="$PROJECT_DIR$/BlendFile/obj" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -17,11 +17,16 @@ using BlendFile = Kaitai.BlenderBlend;
|
|||||||
|
|
||||||
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
|
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
|
||||||
namespace CodeGenerator {
|
namespace CodeGenerator {
|
||||||
|
using CParamDeclExp = CodeParameterDeclarationExpression;
|
||||||
|
using CArgRefExp = CodeArgumentReferenceExpression;
|
||||||
|
using CThisRefExp = CodeThisReferenceExpression;
|
||||||
|
using CFieldRefExp = CodeFieldReferenceExpression;
|
||||||
|
|
||||||
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 OutPath = @"GeneratedOutput\";
|
||||||
private const string Namespace = "BlendFile.DNA";
|
private const string Namespace = "BlendFile";
|
||||||
private static readonly string[] AdaptedTypes = new[] { "uchar" };
|
private static readonly string[] AdaptedTypes = new[] { "uchar" };
|
||||||
private static HashSet<string> customTypes;
|
private static HashSet<string> customTypes;
|
||||||
|
|
||||||
@@ -37,10 +42,12 @@ namespace CodeGenerator {
|
|||||||
Log("Generating C# code...");
|
Log("Generating C# code...");
|
||||||
|
|
||||||
Log("Pass 1: Generating types");
|
Log("Pass 1: Generating types");
|
||||||
CodeNamespace ns = GenerateTypes();
|
CodeNamespace rootNs;
|
||||||
|
CodeNamespace ns = GenerateTypes(out rootNs);
|
||||||
|
|
||||||
Log("Pass 2: Writing out code");
|
Log("Pass 2: Writing out code");
|
||||||
OutputCodeFiles(ns);
|
OutputCodeFiles(ns);
|
||||||
|
OutputCodeFiles(rootNs, false);
|
||||||
|
|
||||||
Log("Finished generating C# code!");
|
Log("Finished generating C# code!");
|
||||||
File.AppendAllText("Log.txt", sb.ToString());
|
File.AppendAllText("Log.txt", sb.ToString());
|
||||||
@@ -54,10 +61,14 @@ namespace CodeGenerator {
|
|||||||
$"DataBlocks: {blendfile.Blocks.Count}\n" +
|
$"DataBlocks: {blendfile.Blocks.Count}\n" +
|
||||||
$"DNA1: {blendfile.SdnaStructs.Count} structures\n");
|
$"DNA1: {blendfile.SdnaStructs.Count} structures\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CodeNamespace GenerateTypes() {
|
private static CodeNamespace GenerateTypes(out CodeNamespace additionalNs) {
|
||||||
CodeNamespace ns = new CodeNamespace(Namespace);
|
CodeNamespace rootNs = new CodeNamespace(Namespace);
|
||||||
|
rootNs.Types.Add(GenerateDNAFieldAttributeType());
|
||||||
|
rootNs.Types.Add(GenerateDNAClassAttributeType());
|
||||||
|
CodeNamespace ns = new CodeNamespace(Namespace+".DNA");
|
||||||
|
ns.Imports.Add(new(rootNs.Name));
|
||||||
|
|
||||||
customTypes = new();
|
customTypes = new();
|
||||||
|
|
||||||
foreach (var type in blendfile.SdnaStructs) {
|
foreach (var type in blendfile.SdnaStructs) {
|
||||||
@@ -93,7 +104,8 @@ namespace CodeGenerator {
|
|||||||
|
|
||||||
//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) {
|
for (var index = 0; index < type.Fields.Count; index++) {
|
||||||
|
var field = type.Fields[index];
|
||||||
CodeMemberField cmf;
|
CodeMemberField cmf;
|
||||||
string name = field.Name;
|
string name = field.Name;
|
||||||
if (name.Contains("()")) continue;
|
if (name.Contains("()")) continue;
|
||||||
@@ -105,7 +117,7 @@ namespace CodeGenerator {
|
|||||||
Log($"Generating field {field.Name}");
|
Log($"Generating field {field.Name}");
|
||||||
cmf = CreateMemberField(field);
|
cmf = CreateMemberField(field);
|
||||||
}
|
}
|
||||||
|
cmf.CustomAttributes.Add(GenerateDNAFieldAttribute(index, field, field.M_Parent.M_Parent));
|
||||||
ctd.Members.Add(cmf);
|
ctd.Members.Add(cmf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +126,196 @@ namespace CodeGenerator {
|
|||||||
|
|
||||||
Log("Finished generating struct");
|
Log("Finished generating struct");
|
||||||
}
|
}
|
||||||
|
additionalNs = rootNs;
|
||||||
return ns;
|
return ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CodeTypeDeclaration GenerateDNAFieldAttributeType() {
|
||||||
|
var ctd = new CodeTypeDeclaration("DNAFieldAttribute") {
|
||||||
|
IsClass = true,
|
||||||
|
Attributes = MemberAttributes.Public
|
||||||
|
};
|
||||||
|
ctd.BaseTypes.Add(new CodeTypeReference(typeof(Attribute)));
|
||||||
|
ctd.CustomAttributes.Add(new("AttributeUsage",
|
||||||
|
new CodeAttributeArgument(new CodeSnippetExpression("AttributeTargets.Field"))));
|
||||||
|
|
||||||
|
var cmf = new CodeMemberField(typeof(int), "_size") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
var cmp = new CodeMemberProperty() {
|
||||||
|
Name = "Size",
|
||||||
|
Type = new CodeTypeReference(typeof(int)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(new CFieldRefExp(new CThisRefExp(), "_size"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_size"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
cmf = new CodeMemberField(typeof(string), "_originalType") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
cmp = new CodeMemberProperty() {
|
||||||
|
Name = "OriginalType",
|
||||||
|
Type = new CodeTypeReference(typeof(string)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(new CFieldRefExp(new CThisRefExp(), "_originalType"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_originalType"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
cmf = new CodeMemberField(typeof(string), "_originalName") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
cmp = new CodeMemberProperty() {
|
||||||
|
Name = "OriginalName",
|
||||||
|
Type = new CodeTypeReference(typeof(string)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(
|
||||||
|
new CFieldRefExp(new CThisRefExp(), "_originalName"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_originalName"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
cmf = new CodeMemberField(typeof(int), "_originalIndex") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
cmp = new CodeMemberProperty() {
|
||||||
|
Name = "OriginalIndex",
|
||||||
|
Type = new CodeTypeReference(typeof(int)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(new CFieldRefExp(new CThisRefExp(), "_originalIndex"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_originalIndex"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
var cc = new CodeConstructor() { Attributes = MemberAttributes.Public };
|
||||||
|
|
||||||
|
cc.Parameters.AddRange(new CParamDeclExp[] {
|
||||||
|
new(typeof(int), "originalIndex"),
|
||||||
|
new(typeof(string), "originalType"),
|
||||||
|
new(typeof(string), "originalName"),
|
||||||
|
new(typeof(int), "size")
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.Statements.AddRange(new CodeAssignStatement[] {
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "OriginalIndex"), new CArgRefExp("originalIndex")),
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "OriginalType"), new CArgRefExp("originalType")),
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "OriginalName"), new CArgRefExp("originalName")),
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "Size"), new CArgRefExp("size"))
|
||||||
|
});
|
||||||
|
|
||||||
|
ctd.Members.Add(cc);
|
||||||
|
return ctd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CodeTypeDeclaration GenerateDNAClassAttributeType() {
|
||||||
|
var ctd = new CodeTypeDeclaration("DNAClassAttribute") {
|
||||||
|
IsClass = true,
|
||||||
|
Attributes = MemberAttributes.Public
|
||||||
|
};
|
||||||
|
ctd.CustomAttributes.Add(new("AttributeUsage",
|
||||||
|
new CodeAttributeArgument(new CodeSnippetExpression("AttributeTargets.Class"))));
|
||||||
|
|
||||||
|
var cmf = new CodeMemberField(typeof(int), "_originalIndex") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
var cmp = new CodeMemberProperty() {
|
||||||
|
Name = "OriginalIndex",
|
||||||
|
Type = new CodeTypeReference(typeof(int)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(new CFieldRefExp(new CThisRefExp(), "_originalIndex"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_originalIndex"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
cmf = new CodeMemberField(typeof(string), "_originalName") {
|
||||||
|
Attributes = MemberAttributes.Private
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmf);
|
||||||
|
|
||||||
|
cmp = new CodeMemberProperty() {
|
||||||
|
Name = "OriginalName",
|
||||||
|
Type = new CodeTypeReference(typeof(string)),
|
||||||
|
Attributes = MemberAttributes.Public,
|
||||||
|
HasSet = true,
|
||||||
|
SetStatements = {
|
||||||
|
new CodeAssignStatement(new CFieldRefExp(new CThisRefExp(), "_originalName"), new CArgRefExp("value"))
|
||||||
|
},
|
||||||
|
HasGet = true,
|
||||||
|
GetStatements = {
|
||||||
|
new CodeMethodReturnStatement(new CFieldRefExp(new CThisRefExp(), "_originalName"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctd.Members.Add(cmp);
|
||||||
|
|
||||||
|
var cc = new CodeConstructor() { Attributes = MemberAttributes.Public };
|
||||||
|
|
||||||
|
cc.Parameters.AddRange(new CParamDeclExp[] {
|
||||||
|
new(typeof(int), "originalIndex"),
|
||||||
|
new(typeof(string), "originalName")
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.Statements.AddRange(new CodeAssignStatement[] {
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "OriginalIndex"), new CArgRefExp("originalIndex")),
|
||||||
|
new(new CFieldRefExp(new CThisRefExp(), "OriginalName"), new CArgRefExp("originalName"))
|
||||||
|
});
|
||||||
|
|
||||||
|
ctd.Members.Add(cc);
|
||||||
|
return ctd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CodeAttributeDeclaration GenerateDNAFieldAttribute(int index, BlendFile.DnaField field,
|
||||||
|
BlendFile.Dna1Body body) {
|
||||||
|
CodeAttributeDeclaration cad = new("DNAFieldAttribute");
|
||||||
|
cad.Arguments.AddRange(new CodeAttributeArgumentCollection() {
|
||||||
|
new(new CodePrimitiveExpression(index)),
|
||||||
|
new(new CodePrimitiveExpression(field.Type)),
|
||||||
|
new(new CodePrimitiveExpression(field.Name)),
|
||||||
|
new(new CodePrimitiveExpression((int)body.Lengths[field.IdxType]))
|
||||||
|
});
|
||||||
|
return cad;
|
||||||
|
}
|
||||||
|
|
||||||
private static CodeMemberField CreateMemberField(BlenderBlend.DnaField field) {
|
private static CodeMemberField CreateMemberField(BlenderBlend.DnaField field) {
|
||||||
Type t = Type.GetType(field.Type.ParseFType());
|
Type t = Type.GetType(field.Type.ParseFType());
|
||||||
CodeMemberField cmf;
|
CodeMemberField cmf;
|
||||||
@@ -173,7 +372,7 @@ namespace CodeGenerator {
|
|||||||
public static CodeExpression GenerateArrayInitExpression(CodeTypeReference type, IEnumerable<int> dimensions) {
|
public static CodeExpression GenerateArrayInitExpression(CodeTypeReference type, IEnumerable<int> dimensions) {
|
||||||
var dimValues = dimensions as int[] ?? dimensions.ToArray();
|
var dimValues = dimensions as int[] ?? dimensions.ToArray();
|
||||||
string dims = string.Concat(dimValues.Take(dimValues.Count() - 1).Select(d => $"{d},"));
|
string dims = string.Concat(dimValues.Take(dimValues.Count() - 1).Select(d => $"{d},"));
|
||||||
dims+= dimValues.Last();
|
dims += dimValues.Last();
|
||||||
return new CodeSnippetExpression($"new {type.BaseType}[{dims}]");
|
return new CodeSnippetExpression($"new {type.BaseType}[{dims}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +389,7 @@ namespace CodeGenerator {
|
|||||||
dims.Add(0);
|
dims.Add(0);
|
||||||
}
|
}
|
||||||
return new CodeAssignStatement(
|
return new CodeAssignStatement(
|
||||||
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name),
|
new CFieldRefExp(new CThisRefExp(), f.Name),
|
||||||
GenerateArrayInitExpression(f.Type, dims)
|
GenerateArrayInitExpression(f.Type, dims)
|
||||||
);
|
);
|
||||||
}).ToArray<CodeStatement>());
|
}).ToArray<CodeStatement>());
|
||||||
@@ -210,7 +409,7 @@ namespace CodeGenerator {
|
|||||||
.OfType<CodeMemberField>()
|
.OfType<CodeMemberField>()
|
||||||
.Select(f =>
|
.Select(f =>
|
||||||
{
|
{
|
||||||
var cpde = new CodeParameterDeclarationExpression(f.Type, f.Name);
|
var cpde = new CParamDeclExp(f.Type, f.Name);
|
||||||
cpde.Direction = FieldDirection.In;
|
cpde.Direction = FieldDirection.In;
|
||||||
return cpde;
|
return cpde;
|
||||||
}).ToArray());
|
}).ToArray());
|
||||||
@@ -219,8 +418,8 @@ namespace CodeGenerator {
|
|||||||
cc.Statements.AddRange(ctd.Members
|
cc.Statements.AddRange(ctd.Members
|
||||||
.OfType<CodeMemberField>()
|
.OfType<CodeMemberField>()
|
||||||
.Select(f => new CodeAssignStatement(
|
.Select(f => new CodeAssignStatement(
|
||||||
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), f.Name),
|
new CFieldRefExp(new CThisRefExp(), f.Name),
|
||||||
new CodeArgumentReferenceExpression(f.Name))
|
new CArgRefExp(f.Name))
|
||||||
).ToArray<CodeStatement>());
|
).ToArray<CodeStatement>());
|
||||||
|
|
||||||
return cc;
|
return cc;
|
||||||
@@ -248,18 +447,22 @@ namespace CodeGenerator {
|
|||||||
ccu.Namespaces.Add(globalNs);
|
ccu.Namespaces.Add(globalNs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OutputCodeFiles(CodeNamespace ns) {
|
private static void OutputCodeFiles(CodeNamespace ns, bool outputExtraTypes = true) {
|
||||||
if (!Path.Exists(OutPath)) Directory.CreateDirectory(OutPath);
|
string rootPath = Path.GetDirectoryName(GetOutputPath(ns, ""));
|
||||||
|
if (!Path.Exists(rootPath)) Directory.CreateDirectory(rootPath!);
|
||||||
SetupCCU(out var codeGeneratorOptions, out var provider, out var ccu);
|
SetupCCU(out var codeGeneratorOptions, out var provider, out var ccu);
|
||||||
CodeNamespace tempNs = new CodeNamespace(Namespace);
|
CodeNamespace tempNs = new CodeNamespace(ns.Name);
|
||||||
|
tempNs.Imports.AddRange(ns.Imports.Cast<CodeNamespaceImport>().ToArray());
|
||||||
ccu.Namespaces.Add(tempNs);
|
ccu.Namespaces.Add(tempNs);
|
||||||
foreach (var type in ns.Types.OfType<CodeTypeDeclaration>()) {
|
foreach (var type in ns.Types.OfType<CodeTypeDeclaration>()) {
|
||||||
tempNs.Types.Add(type);
|
tempNs.Types.Add(type);
|
||||||
Log($"Writing out {(type.IsStruct ? "struct" : "class")} {type.Name}");
|
Log($"Writing out {(type.IsStruct ? "struct" : "class")} {type.Name}");
|
||||||
using var sw = new StreamWriter($"{OutPath}\\{type.Name}.cs");
|
using var sw = new StreamWriter(GetOutputPath(ns, type.Name));
|
||||||
provider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions);
|
provider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions);
|
||||||
tempNs.Types.Remove(type);
|
tempNs.Types.Remove(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!outputExtraTypes) return;
|
||||||
customTypes.ExceptWith(ns.Types.OfType<CodeTypeDeclaration>().Select(t => t.Name));
|
customTypes.ExceptWith(ns.Types.OfType<CodeTypeDeclaration>().Select(t => t.Name));
|
||||||
foreach (var type in customTypes) {
|
foreach (var type in customTypes) {
|
||||||
Log($"Creating empty struct for missing {type}");
|
Log($"Creating empty struct for missing {type}");
|
||||||
@@ -269,8 +472,12 @@ namespace CodeGenerator {
|
|||||||
};
|
};
|
||||||
tempNs.Types.Add(ctd);
|
tempNs.Types.Add(ctd);
|
||||||
}
|
}
|
||||||
using var finalsw = new StreamWriter($"{OutPath}\\_ExtraTypes.cs");
|
using var finalsw = new StreamWriter(GetOutputPath(ns, "_ExtraTypes"));
|
||||||
provider.GenerateCodeFromCompileUnit(ccu, finalsw, codeGeneratorOptions);
|
provider.GenerateCodeFromCompileUnit(ccu, finalsw, codeGeneratorOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetOutputPath(CodeNamespace ns, string typeName) {
|
||||||
|
return $"{OutPath}\\{string.Concat(ns.Name.Split('.').Skip(1))}\\{typeName}.cs";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user