/// <summary> /// Parses a RenderWare TXD texture dictionary from a byte array. /// </summary> /// <param name="data">The TXD data to read in binary form.</param> /// <returns>A new ExtendedSection object containing the parsed TXD data.</returns> public ExtendedSection Parse(byte[] data) { parserData = data; parserOffset = 0; ExtendedSection root = CreateTree(); return(root); }
private void ReadExtenedSection(ExtendedSection id) { if (id == ExtendedSection.Markers) { var count = reader.readVarInt(); for (int i = 0; i < count; i++) { var marker = new ProfileMarker(); marker.Frame = _currentframe; marker.Kind = (MarkerKind)reader.readVarInt(); marker.ThreadId = reader.ReadByte(); marker.UserValue = (int)reader.readVarInt(); var labelId = reader.readVarInt(); marker.Label = labelId != 0 ? ppMap[(int)labelId] : ""; marker.Timestamp = reader.readVarInt64(); _markers.Add(marker); } } }
private ExtendedSection CreateTree() { ExtendedSection root = null; ExtendedSection parent = null; while (true) { // 1. Check whether it's possible to create // other sections inside the parent Section if (parent != null) { var bytesReadSinceParentCreation = (parserOffset - parent.CreationOffset); if (bytesReadSinceParentCreation == parent.Size) { // 1.1 Attempt to navigate the tree up, by setting // the parent variable to its own Parent. If this // is not possible, then break out of the loop. if (parent.Parent != null) { parent = parent.Parent; continue; } //else // Debug.Write(parent.GetTreeString()); break; } else if (bytesReadSinceParentCreation > parent.Size) { // 1.2 File structure error. Throw an exception. throw new TxdParsingException(); } } // 2. Read the header information SectionType sectionType = (SectionType)NextUInt32(); UInt32 sectionSize = NextUInt32(); UInt32 sectionVersion = NextUInt32(); int startOffset = parserOffset; // 3. Determine the type of section switch (sectionType) { // 3.1 In case of a complex section, we create a new // ExtendedSection object, and we set the parent // variable to the newly created instance case SectionType.RwExtension: case SectionType.RwTextureNative: case SectionType.RwTextureDictionary: var extendedSection = new ExtendedSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, CreationOffset = parserOffset }; if (root == null) { root = extendedSection; } if (parent != null) { parent.AddChild(extendedSection); } parent = extendedSection; continue; // 3.2 In case of a data section, we need to know the // parent Section type in order to instantiate the // appropriate class and fill it with data in a // meaningful way case SectionType.RwData: switch (parent.Type) // parent cannot be null { case SectionType.RwTextureNative: var textureNativeDataSection = new TextureNativeDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // TextureVersion = NextUInt32(), FilterFlags = NextUInt32(), TextureName = NextString(32), AlphaName = NextString(32), AlphaFlags = NextUInt32(), D3DTextureFormat = (TextureFormats)NextUInt32(), Width = NextUInt16(), Height = NextUInt16(), BitDepth = NextByte(), MipMapCount = NextByte(), TexCodeType = NextByte(), Compression = NextByte() // TODO: to be implemented }; if (textureNativeDataSection.BitDepth == 8) { for (int i = 0; i < (256 * 4); i++) { textureNativeDataSection.Palette.Add(NextByte()); } } textureNativeDataSection.DataSize = NextUInt32(); for (int i = 0; i < textureNativeDataSection.DataSize; i++) { textureNativeDataSection.Data.Add(NextByte()); } if (textureNativeDataSection.MipMapCount > 1) { for (int i = 0; i < (textureNativeDataSection.MipMapCount - 1); i++) { TextureNativeDataSection.MipMap mipMap = new TextureNativeDataSection.MipMap() { MipMapDataSize = NextUInt32(), MipMapData = new List <byte>() }; for (int j = 0; j < mipMap.MipMapDataSize; j++) { mipMap.MipMapData.Add(NextByte()); } textureNativeDataSection.MipMaps.Add(mipMap); } } if (parserOffset != (startOffset + (int)textureNativeDataSection.Size)) { parserOffset = (startOffset + (int)textureNativeDataSection.Size); textureNativeDataSection.IsDamaged = true; } parent.AddChild(textureNativeDataSection); continue; case SectionType.RwTextureDictionary: var textureDictionaryDataSection = new TextureDictionaryDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // TextureCount = NextUInt16(), Unknown1 = NextUInt16() }; if (parserOffset != (startOffset + (int)textureDictionaryDataSection.Size)) { parserOffset = (startOffset + (int)textureDictionaryDataSection.Size); textureDictionaryDataSection.IsDamaged = true; } parent.AddChild(textureDictionaryDataSection); continue; default: // Unreadable: skip the number of bytes defined in the section header NextString((int)sectionSize); continue; } // In all other cases we simply skip the number of bytes // declared in the section header, without creating a Section // object, nor adding it to the tree default: NextString((int)sectionSize); continue; } } return(root); }
private ExtendedSection CreateTree() { ExtendedSection root = null; ExtendedSection parent = null; while (true) { // 1. Check whether it's possible to create // other sections inside the parent Section if (parent != null) { var bytesReadSinceParentCreation = (parserOffset - parent.CreationOffset); if (bytesReadSinceParentCreation == parent.Size) { // 1.1 Attempt to navigate the tree up, by setting // the parent variable to its own Parent. If this // is not possible, then break out of the loop. if (parent.Parent != null) { parent = parent.Parent; continue; } //else // Debug.Write(parent.GetTreeString()); break; } else if (bytesReadSinceParentCreation > parent.Size) { // 1.2 File structure error. Throw an exception. throw new DffParsingException(); } } // 2. Read the header information SectionType sectionType = (SectionType)NextUInt32(); uint sectionSize = NextUInt32(); uint sectionVersion = NextUInt32(); int startOffset = parserOffset; // 3. Determine the type of section switch (sectionType) { // 3.1 In case of a complex section, we create a new // ExtendedSection object, and we call this function // recursively with 'parent' set to the new instance case SectionType.RwClump: case SectionType.RwFrameList: case SectionType.RwExtension: case SectionType.RwGeometryList: case SectionType.RwGeometry: case SectionType.RwMaterialList: case SectionType.RwMaterial: case SectionType.RwTexture: case SectionType.RwAtomic: case SectionType.RwLight: var extendedSection = new ExtendedSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, CreationOffset = parserOffset }; if (root == null) { root = extendedSection; } if (parent != null) { parent.AddChild(extendedSection); } parent = extendedSection; continue; // 3.2 In case of a data section, we need to know the // parent Section type in order to instantiate the // appropriate class and fill it with data in a // meaningful way case SectionType.RwData: switch (parent.Type) // parent cannot be null { case SectionType.RwClump: var clumpDataSection = new ClumpDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // ObjectCount = NextUInt32(), Unknown1 = NextUInt32(), Unknown2 = NextUInt32() }; if (parserOffset != (startOffset + (int)clumpDataSection.Size)) { parserOffset = (startOffset + (int)clumpDataSection.Size); clumpDataSection.IsDamaged = true; } parent.AddChild(clumpDataSection); continue; case SectionType.RwFrameList: var frameListDataSection = new FrameListDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // FrameCount = NextUInt32() }; for (int i = 0; i < frameListDataSection.FrameCount; i++) { frameListDataSection.FrameInformation.Add(new FrameListDataSection.FrameStruct() { RotationalMatrix = new float[, ] { { NextFloat(), NextFloat(), NextFloat() }, { NextFloat(), NextFloat(), NextFloat() }, { NextFloat(), NextFloat(), NextFloat() } }, CoordinatesOffset = new float[] { NextFloat(), NextFloat(), NextFloat() }, Parent = NextUInt32(), Unknown1 = NextUInt32() }); } if (parserOffset != (startOffset + (int)frameListDataSection.Size)) { parserOffset = (startOffset + (int)frameListDataSection.Size); frameListDataSection.IsDamaged = true; } parent.AddChild(frameListDataSection); continue; case SectionType.RwGeometryList: var geometryListDataSection = new GeometryListDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // GeometryCount = NextUInt32() }; if (parserOffset != (startOffset + (int)geometryListDataSection.Size)) { parserOffset = (startOffset + (int)geometryListDataSection.Size); geometryListDataSection.IsDamaged = true; } parent.AddChild(geometryListDataSection); continue; case SectionType.RwGeometry: var geometryDataSection = new GeometryDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // Flags = (GeometryDataFlags)NextUInt16(), UVMapCount = NextByte(), HasNativeGeometry = NextByte(), TriangleCount = NextUInt32(), VertexCount = NextUInt32(), MorphCount = NextUInt32() }; if (geometryDataSection.Version < 0x34000) { NextString(12); } if (geometryDataSection.HasNativeGeometry == 0) { if ((geometryDataSection.Flags & GeometryDataFlags.RwObjectVertexColor) != GeometryDataFlags.None) { for (int i = 0; i < geometryDataSection.VertexCount; i++) { geometryDataSection.VertexColors.Add(new GeometryDataSection.VertexColor() { R = NextByte(), G = NextByte(), B = NextByte(), A = NextByte(), }); } } if ((geometryDataSection.Flags & GeometryDataFlags.RwObjectVertexUv) != GeometryDataFlags.None) { for (int i = 0; i < geometryDataSection.VertexCount; i++) { geometryDataSection.VertexUVs.Add(new GeometryDataSection.VertexUV() { U = NextFloat(), V = NextFloat() }); } geometryDataSection.UVMapCount = 1; } if ((geometryDataSection.Flags & GeometryDataFlags.RwObjectVertexTextured) != GeometryDataFlags.None) { for (int i = 0; i < geometryDataSection.UVMapCount; i++) { geometryDataSection.VertexUVs.Add(new GeometryDataSection.VertexUV() { U = NextFloat(), V = NextFloat() }); } } for (int i = 0; i < geometryDataSection.TriangleCount; i++) { geometryDataSection.Triangles.Add(new GeometryDataSection.Triangle() { Vertex2 = NextUInt16(), Vertex1 = NextUInt16(), Flags = NextUInt16(), Vertex3 = NextUInt16() }); } } geometryDataSection.BoundingSphereX = NextFloat(); geometryDataSection.BoundingSphereY = NextFloat(); geometryDataSection.BoundingSphereZ = NextFloat(); geometryDataSection.BoundingSphereRadius = NextFloat(); geometryDataSection.HasPosition = NextUInt32(); geometryDataSection.HasNormals = NextUInt32(); if (geometryDataSection.HasNativeGeometry == 0) { for (int i = 0; i < geometryDataSection.VertexCount; i++) { geometryDataSection.Vertices.Add(new GeometryDataSection.Vertex() { X = NextFloat(), Y = NextFloat(), Z = NextFloat() }); } if ((geometryDataSection.Flags & GeometryDataFlags.RwObjectVertexNormal) != GeometryDataFlags.None) { for (int i = 0; i < geometryDataSection.VertexCount; i++) { geometryDataSection.Normals.Add(new GeometryDataSection.Normal() { X = NextFloat(), Y = NextFloat(), Z = NextFloat() }); } } } if (parserOffset != (startOffset + (int)geometryDataSection.Size)) { parserOffset = (startOffset + (int)geometryDataSection.Size); geometryDataSection.IsDamaged = true; } parent.AddChild(geometryDataSection); continue; case SectionType.RwMaterialList: var materialListDataSection = new MaterialListDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // MaterialCount = NextUInt32(), Unknown1 = NextUInt32() }; if (parserOffset != (startOffset + (int)materialListDataSection.Size)) { parserOffset = (startOffset + (int)materialListDataSection.Size); materialListDataSection.IsDamaged = true; } parent.AddChild(materialListDataSection); continue; case SectionType.RwMaterial: var materialDataSection = new MaterialDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // Unknown1 = NextUInt32(), Color = new MaterialDataSection.MaterialColor() { R = NextByte(), G = NextByte(), B = NextByte(), A = NextByte() }, Unknown2 = NextUInt32(), TextureCount = NextUInt32(), Unknown3 = new float[] { NextFloat(), NextFloat(), NextFloat() } }; if (parserOffset != (startOffset + (int)materialDataSection.Size)) { parserOffset = (startOffset + (int)materialDataSection.Size); materialDataSection.IsDamaged = true; } parent.AddChild(materialDataSection); continue; case SectionType.RwTexture: var textureDataSection = new TextureDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // TextureFilterModeFlags = NextUInt16(), Unknown1 = NextUInt16() }; if (parserOffset != (startOffset + (int)textureDataSection.Size)) { parserOffset = (startOffset + (int)textureDataSection.Size); textureDataSection.IsDamaged = true; } parent.AddChild(textureDataSection); continue; case SectionType.RwAtomic: var atomicDataSection = new AtomicDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // FrameNumber = NextUInt32(), GeometryNumber = NextUInt32(), Unknown1 = NextUInt32(), Unknown2 = NextUInt32() }; if (parserOffset != (startOffset + (int)atomicDataSection.Size)) { parserOffset = (startOffset + (int)atomicDataSection.Size); atomicDataSection.IsDamaged = true; } parent.AddChild(atomicDataSection); continue; default: // Unreadable: skip the number of bytes defined in the section header NextString((int)sectionSize); continue; } // 3.3 In case of special data formats, we parse their content // accordingly case SectionType.RwFrame: var frameSection = new FrameDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, }; frameSection.FrameName = NextString((int)frameSection.Size); if (parserOffset != (startOffset + (int)frameSection.Size)) { parserOffset = (startOffset + (int)frameSection.Size); frameSection.IsDamaged = true; } parent.AddChild(frameSection); continue; case SectionType.RwString: var stringDataSection = new StringDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, }; stringDataSection.String = NextString((int)stringDataSection.Size); if (parserOffset != (startOffset + (int)stringDataSection.Size)) { parserOffset = (startOffset + (int)stringDataSection.Size); stringDataSection.IsDamaged = true; } parent.AddChild(stringDataSection); continue; case SectionType.RwMaterialSplit: var materialSplitDataSection = new MaterialSplitDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // FaceType = NextUInt32(), SplitCount = NextUInt32(), FaceCount = NextUInt32() }; for (int i = 0; i < materialSplitDataSection.SplitCount; i++) { var split = new MaterialSplitDataSection.Split() { IndexCount = NextUInt32(), Material = NextUInt32(), Indices = new List <UInt32>() }; materialSplitDataSection.Splits.Add(split); for (int j = 0; j < split.IndexCount; j++) { split.Indices.Add(NextUInt32()); } } if (parserOffset != (startOffset + (int)materialSplitDataSection.Size)) { parserOffset = (startOffset + (int)materialSplitDataSection.Size); materialSplitDataSection.IsDamaged = true; } parent.AddChild(materialSplitDataSection); continue; case SectionType.RwAnimPlugin: var animPluginDataSection = new AnimPluginDataSection() { Type = sectionType, Size = sectionSize, Version = sectionVersion, // Unknown1 = NextUInt32(), BoneId = NextUInt32(), BoneCount = NextUInt32(), Unknown2 = NextUInt32(), Unknown3 = NextUInt32() }; for (int i = 0; i < animPluginDataSection.BoneCount; i++) { animPluginDataSection.Bones.Add(new AnimPluginDataSection.BoneInformation() { Id = NextUInt32(), Index = NextUInt32(), Type = NextUInt32() }); } if (parserOffset != (startOffset + (int)animPluginDataSection.Size)) { parserOffset = (startOffset + (int)animPluginDataSection.Size); animPluginDataSection.IsDamaged = true; } parent.AddChild(animPluginDataSection); continue; // In all other cases we simply skip the number of bytes // declared in the section header, without creating a Section // object, nor adding it to the tree default: NextString((int)sectionSize); continue; } } return(root); }