From 6d565377a4ca85229ec6778407d8ee007f018f11 Mon Sep 17 00:00:00 2001 From: Samuele Lorefice Date: Thu, 20 Feb 2025 18:41:27 +0100 Subject: [PATCH] Added more documentation and started pointer handling --- BlendFile/Reader.cs | 78 +++++++++++++++++++++++++++---- BlendFileTester/Program.cs | 2 +- BlenderSharp.sln.DotSettings.user | 3 ++ 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/BlendFile/Reader.cs b/BlendFile/Reader.cs index 5cb24e0..3932cac 100644 --- a/BlendFile/Reader.cs +++ b/BlendFile/Reader.cs @@ -1,4 +1,3 @@ -using System.Numerics; using System.Reflection; using System.Text; using Kaitai; @@ -7,20 +6,23 @@ using Kaitai; namespace BlendFile; public class Reader { - readonly string _path; + string _path; private readonly Dictionary dnaTypes = new(); - private List objects = new(); - public List Objects => objects; + private Dictionary<(long, Type), object> objects = new(); + public Dictionary<(long, Type), object> Objects => objects; - private Dictionary memBlocks = new(); + public List GetObjects() => objects.Values.ToList(); + public List GetObjects() => objects.Values.OfType().ToList(); + + private SortedDictionary memBlocks = new(); /// /// Gets the block at the specified memory address /// /// memory address in current system endianness /// A object - public Kaitai.BlendFile.FileBlock? GetBlock(long memAddr) => memBlocks.GetValueOrDefault(memAddr); + public Kaitai.BlendFile.FileBlock? GetBlock(long memAddr) => memBlocks.SkipWhile(x => x.Key < memAddr).FirstOrDefault().Value; /// /// Creates a new instance of the class @@ -43,12 +45,18 @@ public class Reader { } } + public void Read(string path) { + _path = path; + Read(); + } + public void Read() { var file = new KaitaiStream(_path); var blend = new Kaitai.BlendFile(file); bool isLe = blend.Hdr.Endian == Kaitai.BlendFile.Endian.Le; - blend.Blocks.ForEach(block => memBlocks.Add(block.MemAddr.ToMemAddr(isLe), block)); + //TODO: two blocks somehow have the same mem address... this sounds wrong. + blend.Blocks.ForEach(block => memBlocks.TryAdd(block.MemAddr.ToMemAddr(isLe), block)); foreach (var block in blend.Blocks) { @@ -68,21 +76,31 @@ public class Reader { //create an instance of type "t" (the dna type we inferred before) var obj = Activator.CreateInstance(t); if(obj == null) continue; //should never happen - objects.Add(obj); //add the object to the list of objects //fill the object with the data from the block FillObject(block, ref obj, t.GetFields(), blockOffset); + //move the offset to the next object in the block blockOffset += t.GetCustomAttribute()!.Size; } } } - private void FillObject(Kaitai.BlendFile.FileBlock block, ref object obj, FieldInfo[] fieldMetadata, int startOffset = 0) { + /// + /// + /// + /// + /// + /// + /// + /// + private void FillObject(Kaitai.BlendFile.FileBlock block, ref object? obj, FieldInfo[] fieldMetadata, int startOffset = 0) { + if(block.Code == "ENDB") return; foreach (var field in fieldMetadata) { //Get the DNAFieldAttribute of the current field var attrib = field.GetCustomAttribute(); if (attrib == null) continue; //should never happen, but means a field has no metadata + //Calculate the offset from where the data of the field starts. //Because the order of the fields is not guaranteed we need to compute it each time int offset = fieldMetadata.Where(f => f.GetCustomAttribute()!.OriginalIndex < attrib.OriginalIndex) @@ -93,10 +111,50 @@ public class Reader { Array.Copy((byte[])block.Body, offset, data, 0, size); //Convert the data to the correct type object? value = ConvertFieldData(data, attrib.OriginalType); - if(value == null) continue; //should never happen, but means the data could not be converted + + if(value == null){ //if the data could not be converted + //Check if the field is a pointer to another DNA structure + if (dnaTypes.Values.FirstOrDefault(x => x.GetCustomAttribute()!.OriginalName == attrib.OriginalType) != null) { + //Create a new instance of the DNA structure type + object? newObj = Activator.CreateInstance(dnaTypes.Values.First(x => + x.GetCustomAttribute()!.OriginalName == attrib.OriginalType)); + if(newObj == null) continue; //should never happen... type is missing? + + //Get the information of the fields of the new object + var fieldInfo = newObj.GetType().GetFields(); + + //If the field is a pointer, we need to dereference it + if (attrib.IsPointer) { + //Get the memory address of the data + long addr = data.ToMemAddr(); + //Get the closest block that contains the data + var derefBlock = GetBlock(addr); + //Calculate the relative address of the data in the block + long relAddr = addr - derefBlock!.MemAddr.ToMemAddr(); + + if(objects.TryGetValue((addr, newObj.GetType()), out object? o)){ //If the object is already created, we can just assign it + field.SetValue(obj, o); + continue; + } + + //Fill the object with the data from the block (this is recursive) + FillObject(derefBlock, ref newObj, fieldInfo, (int)relAddr); + } else { + long relAddr = block.MemAddr.ToMemAddr() + offset; + if(objects.TryGetValue((relAddr, newObj.GetType()), out object? o)){ //If the object is already created, we can just assign it + field.SetValue(obj, o); + continue; + } + //Fill the object with the data from the block (this is recursive) + FillObject(block, ref newObj, fieldInfo, offset); + } + } + continue; //should never happen, but means the data could not be converted + } //Additionally... some fields might not be nullable so it's better to not assign the value and leave the default one. field.SetValue(obj, value); } + objects.Add((block.MemAddr.ToMemAddr() + startOffset, obj!.GetType()), obj!); } private object? ConvertFieldData(byte[] data, string type) diff --git a/BlendFileTester/Program.cs b/BlendFileTester/Program.cs index 1763f6d..55c8abe 100644 --- a/BlendFileTester/Program.cs +++ b/BlendFileTester/Program.cs @@ -4,7 +4,7 @@ using BlendFile; var reader = new Reader("cube.blend"); reader.Read(); -foreach (var obj in reader.Objects) +foreach (var obj in reader.GetObjects()) { Console.WriteLine(obj.GetType()); Console.WriteLine(obj.ToString()); diff --git a/BlenderSharp.sln.DotSettings.user b/BlenderSharp.sln.DotSettings.user index e6c08e5..8a0f6e6 100644 --- a/BlenderSharp.sln.DotSettings.user +++ b/BlenderSharp.sln.DotSettings.user @@ -1,9 +1,12 @@  ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded \ No newline at end of file