public void Load(BinaryReader r, BlenderFile file) { IsParsable = ( Count > 0 && Size > 0 && Size == Count * file.SDNA.Structures[SDNAIndex].Size && !Code.Equals("DNA1", StringComparison.Ordinal) && !Code.Equals("ENDB", StringComparison.Ordinal) ); OriginalData = r.ReadBytes(Size); // original data is needed to resolve multidimensional pointers later if (!IsParsable) { GameDebugger.Log(LogLevel.VerboseDebug, "LOADING BYTES ONLY {0} size={1} addr=0x{2:X16} SDNA={3} count={4}", Code, Size, OldMemoryAddress, SDNAIndex, Count); return; } r.BaseStream.Position = DataPosition; ulong addr = OldMemoryAddress; Data = new BlenderFileObject[Count]; GameDebugger.Log(LogLevel.VerboseDebug, "LOADING STRUCT {0} size={1} addr=0x{2:X16} SDNA={3} count={4}", Code, Size, OldMemoryAddress, SDNAIndex, Count); for (int i = 0; i < Count; i++) { Data[i] = new BlenderFileObject(r, file, addr, SDNAIndex); addr += (ulong)file.SDNA.Structures[SDNAIndex].Size; } }
/// <summary> /// /// </summary> /// <param name="field"></param> /// <param name="file"></param> public void ResolvePointerField(BlenderFileObjectField field, BlenderFile file) { // TODO: add support for array of pointers BlenderPointer ptr = (BlenderPointer)field.Value; if (ptr.State == BlenderPointerState.Resolved) { field.Value = ptr.Value; return; } if (ptr.State == BlenderPointerState.Resolving || ptr.State == BlenderPointerState.Failed) { field.Value = null; return; } // This is needed to avoid infinite loops ptr.State = BlenderPointerState.Resolving; field.Value = ptr; dynamic resolvedValue = null; // TODO: resolve the pointer field.Value = resolvedValue; }
public BlenderFileBlock(BinaryReader r, BlenderFile file) { Code = r.ReadFixedSizeString(4, Encoding.ASCII); Size = r.ReadInt32(file.Header.Endianness); OldMemoryAddress = (file.Header.PointerSize == BlenderPointerSize.Ptr32) ? (ulong)r.ReadUInt32(file.Header.Endianness) : r.ReadUInt64(file.Header.Endianness); SDNAIndex = r.ReadInt32(file.Header.Endianness); Count = r.ReadInt32(file.Header.Endianness); DataPosition = r.BaseStream.Position; }
/// <summary> /// Processes pointers in loaded data /// </summary> /// <param name="r"></param> /// <param name="file"></param> public void Process(BlenderFile file) { if (!IsParsable) { return; } foreach (BlenderFileObject data in Data) { data.ResolvePointers(file); } }
public void ResolvePointers(BlenderFile file) { WalkFields(true, (name, field) => { if (!field.IsPointer) { return(true); } // ResolvePointerField(field, file); return(true); }); }
protected dynamic ReadFieldValue(BinaryReader r, BlenderFile file, BlenderInternalType loadType, string typeName, ulong address, int stringLength, int pointerDimensions, out int readSize) { if (pointerDimensions > 0) { readSize = (file.Header.PointerSize == BlenderPointerSize.Ptr64) ? 8 : 4; return(new BlenderPointer((file.Header.PointerSize == BlenderPointerSize.Ptr64) ? r.ReadUInt64() : (ulong)r.ReadUInt32(), pointerDimensions)); } switch (loadType) { // case InternalType.Char: readSize = 1; return Encoding.ASCII.GetChars(r.ReadBytes(1))[0]; case BlenderInternalType.Char: readSize = 1; return(r.ReadSByte()); case BlenderInternalType.Byte: readSize = 1; return(r.ReadByte()); case BlenderInternalType.Int16: readSize = 2; return(r.ReadInt16(file.Header.Endianness)); case BlenderInternalType.UInt16: readSize = 2; return(r.ReadUInt16(file.Header.Endianness)); case BlenderInternalType.Int32: readSize = 4; return(r.ReadInt32(file.Header.Endianness)); case BlenderInternalType.UInt32: readSize = 4; return(r.ReadUInt32(file.Header.Endianness)); case BlenderInternalType.Int64: readSize = 8; return(r.ReadInt64(file.Header.Endianness)); case BlenderInternalType.UInt64: readSize = 8; return(r.ReadUInt64(file.Header.Endianness)); case BlenderInternalType.Single: readSize = 4; return(r.ReadSingle(file.Header.Endianness)); case BlenderInternalType.Double: readSize = 8; return(r.ReadDouble(file.Header.Endianness)); case BlenderInternalType.String: readSize = stringLength; return(r.ReadFixedSizeString(stringLength, Encoding.ASCII)); case BlenderInternalType.Method: readSize = (file.Header.PointerSize == BlenderPointerSize.Ptr64) ? 8 : 4; return((file.Header.PointerSize == BlenderPointerSize.Ptr64) ? r.ReadUInt64() : (ulong)r.ReadUInt32()); case BlenderInternalType.Object: readSize = file.SDNA.StructureIndex[typeName].Size; return(new BlenderFileObject(r, file, address, typeName)); } throw new Exception("Unexpected value type"); }
public BlenderFileObject(BinaryReader r, BlenderFile file, ulong address, string typeName) { ulong fieldAddress = address, valAddress; BlenderFileStructure structInfo = file.SDNA.StructureIndex[typeName]; string fieldName; BlenderInternalType loadType; int dimStartIndex, stringLength, readSize, totalReadSize, i; string[] dimensionSizesText; int[] dimensionSizes; object[] dimensionSizesForReflection; dynamic val, v; int pointerDimensions; m_Address = address; m_Size = structInfo.Size; m_TypeName = structInfo.Name; foreach (BlenderFileField fieldInfo in structInfo.Fields) { fieldName = fieldInfo.Name; dimensionSizes = null; stringLength = 0; pointerDimensions = 0; if (fieldName.EndsWith("]", StringComparison.InvariantCulture)) { dimStartIndex = fieldName.IndexOf('['); if (dimStartIndex <= 0) { throw new Exception(String.Format("Invalid field name format: {0}", fieldName)); } dimensionSizesText = fieldName.Substring(dimStartIndex + 1, fieldName.Length - dimStartIndex - 2).Split(new String[] { "][" }, StringSplitOptions.None); dimensionSizes = new int[dimensionSizesText.Length]; for (i = dimensionSizesText.Length - 1; i >= 0; i--) { dimensionSizes[i] = int.Parse(dimensionSizesText[i]); } fieldName = fieldName.Substring(0, dimStartIndex); } while (pointerDimensions < fieldName.Length && fieldName[pointerDimensions] == '*') { pointerDimensions++; } if (pointerDimensions > 0) { fieldName = fieldName.Substring(pointerDimensions); } switch (fieldInfo.Type) { case "char": if (dimensionSizes != null) { stringLength = dimensionSizes[dimensionSizes.Length - 1]; int[] newDimSizes = (dimensionSizes.Length > 1) ? new int[dimensionSizes.Length - 1] : null; if (newDimSizes != null) { for (i = dimensionSizes.Length - 2; i >= 0; i--) { newDimSizes[i] = dimensionSizes[i]; } } dimensionSizes = newDimSizes; loadType = BlenderInternalType.String; } else { loadType = BlenderInternalType.Char; } break; case "uchar": loadType = BlenderInternalType.Byte; break; case "short": loadType = BlenderInternalType.Int16; break; case "ushort": loadType = BlenderInternalType.UInt16; break; case "int": loadType = BlenderInternalType.Int32; break; case "uint": loadType = BlenderInternalType.UInt32; break; case "int64_t": goto case "long"; case "long": loadType = BlenderInternalType.Int64; break; case "uint64_t": goto case "ulong"; case "ulong": loadType = BlenderInternalType.UInt64; break; case "float": loadType = BlenderInternalType.Single; break; case "double": loadType = BlenderInternalType.Double; break; default: if (fieldName.StartsWith("(*", StringComparison.InvariantCulture) && fieldName.EndsWith(")()", StringComparison.InvariantCulture)) { fieldName = fieldName.Substring(2, fieldInfo.Name.Length - 5); loadType = BlenderInternalType.Method; } else if (pointerDimensions > 0 || !fieldInfo.Type.Equals("void", StringComparison.Ordinal)) { loadType = BlenderInternalType.Object; } else { throw new Exception("Unexpected non-pointer void"); } break; } if (dimensionSizes == null) { val = ReadFieldValue(r, file, loadType, fieldInfo.Type, fieldAddress, stringLength, pointerDimensions, out readSize); if (loadType == BlenderInternalType.Method) { AddField(fieldName, loadType, fieldInfo.Type, fieldAddress, readSize, pointerDimensions > 0, null); } else { AddField(fieldName, loadType, fieldInfo.Type, fieldAddress, readSize, pointerDimensions > 0, val); } fieldAddress += (ulong)readSize; } else { val = null; dimensionSizesForReflection = new object[dimensionSizes.Length]; for (i = dimensionSizes.Length - 1; i >= 0; i--) { dimensionSizesForReflection[i] = dimensionSizes[i]; } val = Activator.CreateInstance(typeof(object).MakeArrayType(dimensionSizes.Length), dimensionSizesForReflection); int[] idx = new int[dimensionSizes.Length]; valAddress = fieldAddress; totalReadSize = 0; do { v = ReadFieldValue(r, file, loadType, fieldInfo.Type, valAddress, stringLength, pointerDimensions, out readSize); totalReadSize += readSize; valAddress += (ulong)readSize; val.SetValue(v, idx); // go to next possible index in array of current indexes for (i = idx.Length - 1; i >= 0 && ++idx[i] >= dimensionSizes[i]; i--) { idx[i] = 0; } } while(i >= 0); AddField(fieldName, loadType, fieldInfo.Type, fieldAddress, totalReadSize, pointerDimensions > 0, val); fieldAddress = valAddress; } } }
public BlenderFileObject(BinaryReader r, BlenderFile file, ulong address, int sdnaIndex) : this(r, file, address, file.SDNA.Structures[sdnaIndex].Name) { }
public BlenderSDNAFileBlock(BinaryReader r, BlenderFile file) { int i, j, idx, nameCount, typeCount, structCount, fieldCount; string id; long basePos, pos; int[] typeSizes; string[] names, typeNames; BlenderFileStructure st; BlenderFileField fld; Encoding ascii = Encoding.ASCII; basePos = r.BaseStream.Position; id = r.ReadFixedSizeString(4, ascii); if (!id.Equals("SDNA", StringComparison.Ordinal)) { throw new Exception("SDNA block identifier expected"); } id = r.ReadFixedSizeString(4, Encoding.ASCII); if (!id.Equals("NAME", StringComparison.Ordinal)) { throw new Exception("NAME block identifier expected"); } nameCount = r.ReadInt32(file.Header.Endianness); names = new string[nameCount]; for (i = 0; i < nameCount; i++) { names[i] = r.ReadZeroTerminatedString(ascii); GameDebugger.Log(LogLevel.VerboseDebug, "names[{0}]={1}", i, names[i]); } // align to 4 bytes pos = r.BaseStream.Position; if (((pos - basePos) & 3) != 0) { r.BaseStream.Position = basePos + ((pos - basePos) / 4 + 1) * 4; } id = r.ReadFixedSizeString(4, Encoding.ASCII); if (!id.Equals("TYPE", StringComparison.Ordinal)) { throw new Exception("TYPE block identifier expected"); } typeCount = r.ReadInt32(file.Header.Endianness); typeNames = new string[typeCount]; typeSizes = new int[typeCount]; for (i = 0; i < typeCount; i++) { typeNames[i] = r.ReadZeroTerminatedString(ascii); GameDebugger.Log(LogLevel.VerboseDebug, "typeNames[{0}]={1}", i, typeNames[i]); } // align to 4 bytes pos = r.BaseStream.Position; if (((pos - basePos) & 3) != 0) { r.BaseStream.Position = basePos + ((pos - basePos) / 4 + 1) * 4; } id = r.ReadFixedSizeString(4, Encoding.ASCII); if (!id.Equals("TLEN", StringComparison.Ordinal)) { throw new Exception("TLEN block identifier expected"); } for (i = 0; i < typeCount; i++) { typeSizes[i] = r.ReadInt16(file.Header.Endianness); GameDebugger.Log(LogLevel.VerboseDebug, "typeSizes[{0}]={1}", i, typeSizes[i]); } // align to 4 bytes pos = r.BaseStream.Position; if (((pos - basePos) & 3) != 0) { r.BaseStream.Position = basePos + ((pos - basePos) / 4 + 1) * 4; } id = r.ReadFixedSizeString(4, Encoding.ASCII); if (!id.Equals("STRC", StringComparison.Ordinal)) { throw new Exception("STRC block identifier expected"); } structCount = r.ReadInt32(file.Header.Endianness); Structures = new BlenderFileStructure[structCount]; StructureIndex = new Dictionary <string, BlenderFileStructure>(); for (i = 0; i < structCount; i++) { st = new BlenderFileStructure(); idx = r.ReadInt16(file.Header.Endianness); st.Index = i; st.Name = typeNames[idx]; st.Size = typeSizes[idx]; fieldCount = r.ReadInt16(file.Header.Endianness); st.Fields = new BlenderFileField[fieldCount]; GameDebugger.Log(LogLevel.VerboseDebug, "struct #{0} {1} (size={2}, fields={3})", st.Index, st.Name, st.Size, fieldCount); for (j = 0; j < fieldCount; j++) { fld = new BlenderFileField(); idx = r.ReadInt16(file.Header.Endianness); fld.Type = typeNames[idx]; fld.Size = typeSizes[idx]; fld.Name = names[r.ReadInt16(file.Header.Endianness)]; st.Fields[j] = fld; GameDebugger.Log(LogLevel.VerboseDebug, " {0} {1} (size={2})", fld.Type, fld.Name, fld.Size); } Structures[i] = st; StructureIndex.Add(st.Name, st); } }