commit 2a051cf098e1aec0ab92b12d252366ec4610fd40 Author: Samuele Lorefice Date: Wed Jan 22 00:43:53 2025 +0100 Base files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.BlenderSharp/.idea/.gitignore b/.idea/.idea.BlenderSharp/.idea/.gitignore new file mode 100644 index 0000000..81e775a --- /dev/null +++ b/.idea/.idea.BlenderSharp/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.BlenderSharp.iml +/contentModel.xml +/modules.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.BlenderSharp/.idea/indexLayout.xml b/.idea/.idea.BlenderSharp/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.BlenderSharp/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.BlenderSharp/.idea/vcs.xml b/.idea/.idea.BlenderSharp/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.BlenderSharp/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/BlendTypes/BlendTypes.csproj b/BlendTypes/BlendTypes.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/BlendTypes/BlendTypes.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/BlenderSharp.sln b/BlenderSharp.sln new file mode 100644 index 0000000..94ab229 --- /dev/null +++ b/BlenderSharp.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{F7A0CD58-F691-4EFC-AC04-D63DB372DA31}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlendTypes", "BlendTypes\BlendTypes.csproj", "{E2D22AE2-A31A-453C-8A3F-1D4066F0C55D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7A0CD58-F691-4EFC-AC04-D63DB372DA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7A0CD58-F691-4EFC-AC04-D63DB372DA31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7A0CD58-F691-4EFC-AC04-D63DB372DA31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7A0CD58-F691-4EFC-AC04-D63DB372DA31}.Release|Any CPU.Build.0 = Release|Any CPU + {E2D22AE2-A31A-453C-8A3F-1D4066F0C55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2D22AE2-A31A-453C-8A3F-1D4066F0C55D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2D22AE2-A31A-453C-8A3F-1D4066F0C55D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2D22AE2-A31A-453C-8A3F-1D4066F0C55D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/CodeGenerator/BlenderBlend.cs b/CodeGenerator/BlenderBlend.cs new file mode 100644 index 0000000..1cb3db9 --- /dev/null +++ b/CodeGenerator/BlenderBlend.cs @@ -0,0 +1,575 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +using System.Collections.Generic; + +// ReSharper disable once CheckNamespace +namespace Kaitai { + /// + /// Blender is an open source suite for 3D modelling, sculpting, + /// animation, compositing, rendering, preparation of assets for its own + /// game engine and exporting to others, etc. `.blend` is its own binary + /// format that saves whole state of suite: current scene, animations, + /// all software settings, extensions, etc. + /// + /// Internally, .blend format is a hybrid semi-self-descriptive + /// format. On top level, it contains a simple header and a sequence of + /// file blocks, which more or less follow typical [TLV + /// pattern](https://en.wikipedia.org/wiki/Type-length-value). Pre-last + /// block would be a structure with code `DNA1`, which is a essentially + /// a machine-readable schema of all other structures used in this file. + /// + public partial class BlenderBlend : KaitaiStruct { + public static BlenderBlend FromFile(string fileName) { + return new BlenderBlend(new KaitaiStream(fileName)); + } + + + public enum PtrSize { + Bits64 = 45, + Bits32 = 95, + } + + public enum Endian { + Be = 86, + Le = 118, + } + + public BlenderBlend(KaitaiStream p__io, KaitaiStruct p__parent = null, BlenderBlend p__root = null) : base(p__io) { + m_parent = p__parent; + m_root = p__root ?? this; + f_sdnaStructs = false; + _read(); + } + + private void _read() { + _hdr = new Header(m_io, this, m_root); + _blocks = new List(); + { + var i = 0; + while (!m_io.IsEof) { + _blocks.Add(new FileBlock(m_io, this, m_root)); + i++; + } + } + } + + /// + /// DNA struct contains a `type` (type name), which is specified as + /// an index in types table, and sequence of fields. + /// + public partial class DnaStruct : KaitaiStruct { + public static DnaStruct FromFile(string fileName) { + return new DnaStruct(new KaitaiStream(fileName)); + } + + public DnaStruct(KaitaiStream p__io, BlenderBlend.Dna1Body p__parent = null, BlenderBlend p__root = null) : + base(p__io) { + m_parent = p__parent; + m_root = p__root; + f_type = false; + _read(); + } + + private void _read() { + _idxType = m_io.ReadU2le(); + _numFields = m_io.ReadU2le(); + _fields = new List(); + for (var i = 0; i < NumFields; i++) { + _fields.Add(new DnaField(m_io, this, m_root)); + } + } + + private bool f_type; + private string _type; + + public string Type { + get { + if (f_type) + return _type; + _type = (string)(M_Parent.Types[IdxType]); + f_type = true; + return _type; + } + } + + private ushort _idxType; + private ushort _numFields; + private List _fields; + private BlenderBlend m_root; + private BlenderBlend.Dna1Body m_parent; + + public ushort IdxType { + get { return _idxType; } + } + + public ushort NumFields { + get { return _numFields; } + } + + public List Fields { + get { return _fields; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public BlenderBlend.Dna1Body M_Parent { + get { return m_parent; } + } + } + + public partial class FileBlock : KaitaiStruct { + public static FileBlock FromFile(string fileName) { + return new FileBlock(new KaitaiStream(fileName)); + } + + public FileBlock(KaitaiStream p__io, BlenderBlend p__parent = null, BlenderBlend p__root = null) : base(p__io) { + m_parent = p__parent; + m_root = p__root; + f_sdnaStruct = false; + _read(); + } + + private void _read() { + _code = System.Text.Encoding.GetEncoding("ASCII").GetString(m_io.ReadBytes(4)); + _lenBody = m_io.ReadU4le(); + _memAddr = m_io.ReadBytes(M_Root.Hdr.Psize); + _sdnaIndex = m_io.ReadU4le(); + _count = m_io.ReadU4le(); + switch (Code) { + case "DNA1": + { + __raw_body = m_io.ReadBytes(LenBody); + var io___raw_body = new KaitaiStream(__raw_body); + _body = new Dna1Body(io___raw_body, this, m_root); + break; + } + default: + { + _body = m_io.ReadBytes(LenBody); + break; + } + } + } + + private bool f_sdnaStruct; + private DnaStruct _sdnaStruct; + + public DnaStruct SdnaStruct { + get { + if (f_sdnaStruct) + return _sdnaStruct; + if (SdnaIndex != 0) { + _sdnaStruct = (DnaStruct)(M_Root.SdnaStructs[(int)SdnaIndex]); + } + f_sdnaStruct = true; + return _sdnaStruct; + } + } + + private string _code; + private uint _lenBody; + private byte[] _memAddr; + private uint _sdnaIndex; + private uint _count; + private object _body; + private BlenderBlend m_root; + private BlenderBlend m_parent; + private byte[] __raw_body; + + /// + /// Identifier of the file block + /// + public string Code { + get { return _code; } + } + + /// + /// Total length of the data after the header of file block + /// + public uint LenBody { + get { return _lenBody; } + } + + /// + /// Memory address the structure was located when written to disk + /// + public byte[] MemAddr { + get { return _memAddr; } + } + + /// + /// Index of the SDNA structure + /// + public uint SdnaIndex { + get { return _sdnaIndex; } + } + + /// + /// Number of structure located in this file-block + /// + public uint Count { + get { return _count; } + } + + public object Body { + get { return _body; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public BlenderBlend M_Parent { + get { return m_parent; } + } + + public byte[] M_RawBody { + get { return __raw_body; } + } + } + + /// + /// DNA1, also known as "Structure DNA", is a special block in + /// .blend file, which contains machine-readable specifications of + /// all other structures used in this .blend file. + /// + /// Effectively, this block contains: + /// + /// * a sequence of "names" (strings which represent field names) + /// * a sequence of "types" (strings which represent type name) + /// * a sequence of "type lengths" + /// * a sequence of "structs" (which describe contents of every + /// structure, referring to types and names by index) + /// + /// + /// Reference: Source + /// + public partial class Dna1Body : KaitaiStruct { + public static Dna1Body FromFile(string fileName) { + return new Dna1Body(new KaitaiStream(fileName)); + } + + public Dna1Body(KaitaiStream p__io, BlenderBlend.FileBlock p__parent = null, BlenderBlend p__root = null) : + base(p__io) { + m_parent = p__parent; + m_root = p__root; + _read(); + } + + private void _read() { + _id = m_io.ReadBytes(4); + if (!((KaitaiStream.ByteArrayCompare(Id, new byte[] { 83, 68, 78, 65 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 83, 68, 78, 65 }, Id, M_Io, "/types/dna1_body/seq/0"); + } + _nameMagic = m_io.ReadBytes(4); + if (!((KaitaiStream.ByteArrayCompare(NameMagic, new byte[] { 78, 65, 77, 69 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 78, 65, 77, 69 }, NameMagic, M_Io, "/types/dna1_body/seq/1"); + } + _numNames = m_io.ReadU4le(); + _names = new List(); + for (var i = 0; i < NumNames; i++) { + _names.Add(System.Text.Encoding.GetEncoding("UTF-8").GetString(m_io.ReadBytesTerm(0, false, true, true))); + } + _padding1 = m_io.ReadBytes(KaitaiStream.Mod((4 - M_Io.Pos), 4)); + _typeMagic = m_io.ReadBytes(4); + if (!((KaitaiStream.ByteArrayCompare(TypeMagic, new byte[] { 84, 89, 80, 69 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 84, 89, 80, 69 }, TypeMagic, M_Io, "/types/dna1_body/seq/5"); + } + _numTypes = m_io.ReadU4le(); + _types = new List(); + for (var i = 0; i < NumTypes; i++) { + _types.Add(System.Text.Encoding.GetEncoding("UTF-8").GetString(m_io.ReadBytesTerm(0, false, true, true))); + } + _padding2 = m_io.ReadBytes(KaitaiStream.Mod((4 - M_Io.Pos), 4)); + _tlenMagic = m_io.ReadBytes(4); + if (!((KaitaiStream.ByteArrayCompare(TlenMagic, new byte[] { 84, 76, 69, 78 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 84, 76, 69, 78 }, TlenMagic, M_Io, "/types/dna1_body/seq/9"); + } + _lengths = new List(); + for (var i = 0; i < NumTypes; i++) { + _lengths.Add(m_io.ReadU2le()); + } + _padding3 = m_io.ReadBytes(KaitaiStream.Mod((4 - M_Io.Pos), 4)); + _strcMagic = m_io.ReadBytes(4); + if (!((KaitaiStream.ByteArrayCompare(StrcMagic, new byte[] { 83, 84, 82, 67 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 83, 84, 82, 67 }, StrcMagic, M_Io, + "/types/dna1_body/seq/12"); + } + _numStructs = m_io.ReadU4le(); + _structs = new List(); + for (var i = 0; i < NumStructs; i++) { + _structs.Add(new DnaStruct(m_io, this, m_root)); + } + } + + private byte[] _id; + private byte[] _nameMagic; + private uint _numNames; + private List _names; + private byte[] _padding1; + private byte[] _typeMagic; + private uint _numTypes; + private List _types; + private byte[] _padding2; + private byte[] _tlenMagic; + private List _lengths; + private byte[] _padding3; + private byte[] _strcMagic; + private uint _numStructs; + private List _structs; + private BlenderBlend m_root; + private BlenderBlend.FileBlock m_parent; + + public byte[] Id { + get { return _id; } + } + + public byte[] NameMagic { + get { return _nameMagic; } + } + + public uint NumNames { + get { return _numNames; } + } + + public List Names { + get { return _names; } + } + + public byte[] Padding1 { + get { return _padding1; } + } + + public byte[] TypeMagic { + get { return _typeMagic; } + } + + public uint NumTypes { + get { return _numTypes; } + } + + public List Types { + get { return _types; } + } + + public byte[] Padding2 { + get { return _padding2; } + } + + public byte[] TlenMagic { + get { return _tlenMagic; } + } + + public List Lengths { + get { return _lengths; } + } + + public byte[] Padding3 { + get { return _padding3; } + } + + public byte[] StrcMagic { + get { return _strcMagic; } + } + + public uint NumStructs { + get { return _numStructs; } + } + + public List Structs { + get { return _structs; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public BlenderBlend.FileBlock M_Parent { + get { return m_parent; } + } + } + + public partial class Header : KaitaiStruct { + public static Header FromFile(string fileName) { + return new Header(new KaitaiStream(fileName)); + } + + public Header(KaitaiStream p__io, BlenderBlend p__parent = null, BlenderBlend p__root = null) : base(p__io) { + m_parent = p__parent; + m_root = p__root; + f_psize = false; + _read(); + } + + private void _read() { + _magic = m_io.ReadBytes(7); + if (!((KaitaiStream.ByteArrayCompare(Magic, new byte[] { 66, 76, 69, 78, 68, 69, 82 }) == 0))) { + throw new ValidationNotEqualError(new byte[] { 66, 76, 69, 78, 68, 69, 82 }, Magic, M_Io, + "/types/header/seq/0"); + } + _ptrSizeId = ((BlenderBlend.PtrSize)m_io.ReadU1()); + _endian = ((BlenderBlend.Endian)m_io.ReadU1()); + _version = System.Text.Encoding.GetEncoding("ASCII").GetString(m_io.ReadBytes(3)); + } + + private bool f_psize; + private sbyte _psize; + + /// + /// Number of bytes that a pointer occupies + /// + public sbyte Psize { + get { + if (f_psize) + return _psize; + _psize = (sbyte)((PtrSizeId == BlenderBlend.PtrSize.Bits64 ? 8 : 4)); + f_psize = true; + return _psize; + } + } + + private byte[] _magic; + private PtrSize _ptrSizeId; + private Endian _endian; + private string _version; + private BlenderBlend m_root; + private BlenderBlend m_parent; + + public byte[] Magic { + get { return _magic; } + } + + /// + /// Size of a pointer; all pointers in the file are stored in this format + /// + public PtrSize PtrSizeId { + get { return _ptrSizeId; } + } + + /// + /// Type of byte ordering used + /// + public Endian Endian { + get { return _endian; } + } + + /// + /// Blender version used to save this file + /// + public string Version { + get { return _version; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public BlenderBlend M_Parent { + get { return m_parent; } + } + } + + public partial class DnaField : KaitaiStruct { + public static DnaField FromFile(string fileName) { + return new DnaField(new KaitaiStream(fileName)); + } + + public DnaField(KaitaiStream p__io, BlenderBlend.DnaStruct p__parent = null, BlenderBlend p__root = null) : + base(p__io) { + m_parent = p__parent; + m_root = p__root; + f_type = false; + f_name = false; + _read(); + } + + private void _read() { + _idxType = m_io.ReadU2le(); + _idxName = m_io.ReadU2le(); + } + + private bool f_type; + private string _type; + + public string Type { + get { + if (f_type) + return _type; + _type = (string)(M_Parent.M_Parent.Types[IdxType]); + f_type = true; + return _type; + } + } + + private bool f_name; + private string _name; + + public string Name { + get { + if (f_name) + return _name; + _name = (string)(M_Parent.M_Parent.Names[IdxName]); + f_name = true; + return _name; + } + } + + private ushort _idxType; + private ushort _idxName; + private BlenderBlend m_root; + private BlenderBlend.DnaStruct m_parent; + + public ushort IdxType { + get { return _idxType; } + } + + public ushort IdxName { + get { return _idxName; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public BlenderBlend.DnaStruct M_Parent { + get { return m_parent; } + } + } + + private bool f_sdnaStructs; + private List _sdnaStructs; + + public List SdnaStructs { + get { + if (f_sdnaStructs) + return _sdnaStructs; + _sdnaStructs = (List)(((BlenderBlend.Dna1Body)(Blocks[(Blocks.Count - 2)].Body)).Structs); + f_sdnaStructs = true; + return _sdnaStructs; + } + } + + private Header _hdr; + private List _blocks; + private BlenderBlend m_root; + private KaitaiStruct m_parent; + + public Header Hdr { + get { return _hdr; } + } + + public List Blocks { + get { return _blocks; } + } + + public BlenderBlend M_Root { + get { return m_root; } + } + + public KaitaiStruct M_Parent { + get { return m_parent; } + } + } +} \ No newline at end of file diff --git a/CodeGenerator/CodeGenerator.csproj b/CodeGenerator/CodeGenerator.csproj new file mode 100644 index 0000000..f5f7183 --- /dev/null +++ b/CodeGenerator/CodeGenerator.csproj @@ -0,0 +1,24 @@ + + + + net9.0 + disable + CodeGenerator + Exe + 9 + + + + + + + + + + + + Always + + + + diff --git a/CodeGenerator/Program.cs b/CodeGenerator/Program.cs new file mode 100644 index 0000000..538e703 --- /dev/null +++ b/CodeGenerator/Program.cs @@ -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() + .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; + } + + 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(); + 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 dimensions) { + string dims = string.Concat(dimensions.Select(d => $"[{d}]")); + return new CodeSnippetExpression($"new {type.BaseType}{dims}"); + } + + } +} \ No newline at end of file diff --git a/CodeGenerator/empty.blend b/CodeGenerator/empty.blend new file mode 100644 index 0000000..1f96b82 Binary files /dev/null and b/CodeGenerator/empty.blend differ