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 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); } }