示例#1
0
        public void ExtractDds(TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream outStream)
        {
            // TODO: Make sure 3D textures and cube maps work

            // Deserialize the resource definition and verify it
            var resource        = bitmap.Resources[imageIndex];
            var resourceContext = new ResourceSerializationContext(resource.Resource);
            var definition      = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext);

            if (definition.Texture == null || definition.Texture.Definition == null)
            {
                throw new ArgumentException("Invalid bitmap definition");
            }
            var dataReference = definition.Texture.Definition.Data;

            if (dataReference.Address.Type != ResourceAddressType.Resource)
            {
                throw new InvalidOperationException("Invalid resource data address");
            }

            var header             = CreateDdsHeader(definition);
            var resourceDataStream = new MemoryStream();

            _resourceManager.Extract(resource.Resource, resourceDataStream);
            header.WriteTo(outStream);
            resourceDataStream.Position = dataReference.Address.Offset;
            StreamUtil.Copy(resourceDataStream, outStream, dataReference.Size);
        }
示例#2
0
        public static GeometryReference ConvertGeometry(GeometryReference geometry, OpenTagCache srcInfo, ResourceDataManager srcResources, OpenTagCache destInfo, ResourceDataManager destResources)
        {
            if (geometry == null || geometry.Resource == null || geometry.Resource.Index < 0)
            {
                return(geometry);
            }

            // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally
            var srcCompare  = Definition.Compare(srcInfo.Version, DefinitionSet.HaloOnline235640);
            var destCompare = Definition.Compare(destInfo.Version, DefinitionSet.HaloOnline235640);

            if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0))
            {
                geometry.Resource = ConvertResource(geometry.Resource, srcInfo, srcResources, destInfo, destResources);
                return(geometry);
            }

            Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.Index, geometry.Resource.GetLocation());
            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcResources.Extract(geometry.Resource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(srcInfo.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(geometry.Resource);
                var definition      = srcInfo.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                // Update the definition data
                destInfo.Serializer.Serialize(resourceContext, definition);

                // Now inject the new resource data
                var newLocation = FixResourceLocation(geometry.Resource.GetLocation(), srcInfo.Version, destInfo.Version);
                outStream.Position = 0;
                destResources.Add(geometry.Resource, newLocation, outStream);
            }
            return(geometry);
        }
        public override bool Execute(List <string> args)
        {
            if (args.Count != 3)
            {
                return(false);
            }
            var variantName = args[0];
            var fileType    = args[1];
            var fileName    = args[2];

            if (fileType != "obj")
            {
                return(false);
            }

            // Find the variant to extract
            if (Definition.RenderModel == null)
            {
                Console.WriteLine("The model does not have a render model associated with it.");
                return(true);
            }
            var variant = Definition.Variants.FirstOrDefault(v => (Info.StringIds.GetString(v.Name) ?? v.Name.ToString()) == variantName);

            if (variant == null && Definition.Variants.Count > 0)
            {
                Console.WriteLine("Unable to find variant \"{0}\"", variantName);
                Console.WriteLine("Use \"listvariants\" to list available variants.");
                return(true);
            }

            // Load resource caches
            Console.WriteLine("Loading resource caches...");
            var resourceManager = new ResourceDataManager();

            try
            {
                resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName);
            }
            catch
            {
                Console.WriteLine("Unable to load the resource .dat files.");
                Console.WriteLine("Make sure that they all exist and are valid.");
                return(true);
            }

            // Deserialize the render model tag
            Console.WriteLine("Reading model data...");
            RenderModel renderModel;

            using (var cacheStream = Info.CacheFile.OpenRead())
            {
                var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIds, Definition.RenderModel);
                renderModel = Info.Deserializer.Deserialize <RenderModel>(renderModelContext);
            }
            if (renderModel.Geometry.Resource == null)
            {
                Console.WriteLine("Render model does not have a resource associated with it");
                return(true);
            }

            // Deserialize the resource definition
            var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource);
            var definition      = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                // Extract the resource data
                resourceManager.Extract(renderModel.Geometry.Resource, resourceStream);
                using (var objFile = new StreamWriter(File.Open(fileName, FileMode.Create, FileAccess.Write)))
                {
                    var objExtractor     = new ObjExtractor(objFile);
                    var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]); // Create a (de)compressor from the first compression block
                    if (variant != null)
                    {
                        // Extract each region in the variant
                        foreach (var region in variant.Regions)
                        {
                            // Get the corresonding region in the render model tag
                            if (region.RenderModelRegionIndex >= renderModel.Regions.Count)
                            {
                                continue;
                            }
                            var renderModelRegion = renderModel.Regions[region.RenderModelRegionIndex];

                            // Get the corresponding permutation in the render model tag
                            // (Just extract the first permutation for now)
                            if (region.Permutations.Count == 0)
                            {
                                continue;
                            }
                            var permutation = region.Permutations[0];
                            if (permutation.RenderModelPermutationIndex < 0 ||
                                permutation.RenderModelPermutationIndex >= renderModelRegion.Permutations.Count)
                            {
                                continue;
                            }
                            var renderModelPermutation = renderModelRegion.Permutations[permutation.RenderModelPermutationIndex];

                            // Extract each mesh in the permutation
                            var meshIndex       = renderModelPermutation.MeshIndex;
                            var meshCount       = renderModelPermutation.MeshCount;
                            var regionName      = Info.StringIds.GetString(region.Name) ?? region.Name.ToString();
                            var permutationName = Info.StringIds.GetString(permutation.Name) ?? permutation.Name.ToString();
                            Console.WriteLine("Extracting {0} mesh(es) for {1}:{2}...", meshCount, regionName, permutationName);
                            for (var i = 0; i < meshCount; i++)
                            {
                                // Create a MeshReader for the mesh and pass it to the obj extractor
                                var meshReader = new MeshReader(Info.Version, renderModel.Geometry.Meshes[meshIndex + i], definition);
                                objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream);
                            }
                        }
                    }
                    else
                    {
                        // No variant - just extract every mesh
                        Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Geometry.Meshes.Count);
                        foreach (var mesh in renderModel.Geometry.Meshes)
                        {
                            // Create a MeshReader for the mesh and pass it to the obj extractor
                            var meshReader = new MeshReader(Info.Version, mesh, definition);
                            objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream);
                        }
                    }
                    objExtractor.Finish();
                }
            }
            Console.WriteLine("Done!");
            return(true);
        }
示例#4
0
        public override bool Execute(List <string> args)
        {
            if (args.Count != 2)
            {
                return(false);
            }

            var destDir = new DirectoryInfo(args[1]);

            if (!destDir.Exists)
            {
                WriteLine($"Destination cache directory does not exist: {destDir.FullName}");
                return(false);
            }

            var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat"));

            if (!destTagsFile.Exists)
            {
                WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}");
                return(false);
            }

            var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat"));

            if (!destStringIDsFile.Exists)
            {
                WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}");
                return(false);
            }

            var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat"));

            if (!destResourcesFile.Exists)
            {
                WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}");
                return(false);
            }

            var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat"));

            if (!destTexturesFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}");
                return(false);
            }

            var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat"));

            if (!destTexturesBFile.Exists)
            {
                WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}");
                return(false);
            }

            var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat"));

            if (!destAudioFile.Exists)
            {
                WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}");
                return(false);
            }

            TagCache destTagCache;

            using (var stream = destTagsFile.OpenRead())
                destTagCache = new TagCache(stream);

            DefinitionSet guessedVersion;
            var           destVersion = Detect(destTagCache, out guessedVersion);

            if (destVersion == Unknown)
            {
                WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})");
                return(true);
            }

            WriteLine($"Destination cache version: {GetVersionString(destVersion)}");

            StringIDCache destStringIDCache;

            using (var stream = destStringIDsFile.OpenRead())
                destStringIDCache = new StringIDCache(stream, Create(destVersion));

            var destResources = new ResourceDataManager();

            destResources.LoadCachesFromDirectory(destDir.FullName);

            var srcResources = new ResourceDataManager();

            srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName);

            var destSerializer   = new TagSerializer(destVersion);
            var destDeserializer = new TagDeserializer(destVersion);

            var destInfo = new OpenTagCache
            {
                Cache         = destTagCache,
                CacheFile     = destTagsFile,
                StringIDs     = destStringIDCache,
                StringIDsFile = destStringIDsFile,
                Version       = destVersion,
                Serializer    = destSerializer,
                Deserializer  = destDeserializer
            };

            var destTag = ParseTagIndex(destInfo, args[0]);

            if (destTag == null || !destTag.IsInGroup(new Tag("mode")))
            {
                WriteLine("Destination tag must be of group 'mode'.");
                return(false);
            }

            RenderModel destDefinition;

            using (var destStream = destInfo.OpenCacheRead())
            {
                var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag);
                destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context);
            }

            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcResources.Extract(Definition.Geometry.Resource, inStream);

                // Now open source and destination vertex streams
                inStream.Position = 0;
                var inVertexStream  = VertexStreamFactory.Create(Info.Version, inStream);
                var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream);

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource);
                var definition      = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream);
                }

                // Copy each index buffer over
                foreach (var buffer in definition.IndexBuffers)
                {
                    if (buffer.Definition.Data.Size == 0)
                    {
                        continue;
                    }
                    inStream.Position = buffer.Definition.Data.Address.Offset;
                    buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position);
                    var bufferData = new byte[buffer.Definition.Data.Size];
                    inStream.Read(bufferData, 0, bufferData.Length);
                    outStream.Write(bufferData, 0, bufferData.Length);
                    StreamUtil.Align(outStream, 4);
                }

                destInfo.Serializer.Serialize(resourceContext, definition);

                outStream.Position = 0;
                destResources.Replace(destDefinition.Geometry.Resource, outStream);
            }

            return(true);
        }
示例#5
0
        /// <summary>
        /// Creates a CollisionModel bsp from a Scenario StructureBsp.
        /// This does not work for sbsps with > 65536 planes, which use a
        ///  larger encoding for their bsp related structs differing from the
        ///  struct used in common with collision model bsps.
        /// </summary>
        /// <returns></returns>
        public static BSP fromSbsp(ScenarioStructureBsp sbsp, OpenTagCache info)
        {
            // Need to work out how to do that class attribute enumeration thing
            // so all of this folds down to < 10 lines
            BSP bsp = fromSbspInit(sbsp);
            var resource = sbsp.Resource3;
            var resourceManager = new ResourceDataManager();
            try
            {
                resourceManager.LoadCachesFromDirectory(info.CacheFile.DirectoryName);
            }
            catch
            {
                Console.WriteLine("Unable to load the resource .dat files.");
                Console.WriteLine("Make sure that they all exist and are valid.");
            }

            //Create a binary reader for the resource
            Stream stream = new MemoryStream();
            resourceManager.Extract(sbsp.Resource3, stream);
            BinaryReader reader = new BinaryReader(stream);
            reader.BaseStream.Position = 0;
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Bsp3dNodes.Count; ++i)
            {
                BSP.Bsp3dNode node = new BSP.Bsp3dNode();
                node.Plane = reader.ReadInt16();
                node.BackChildLower = reader.ReadByte();
                node.BackChildMid = reader.ReadByte();
                node.BackChildUpper = reader.ReadByte();
                node.FrontChildLower = reader.ReadByte();
                node.FrontChildMid = reader.ReadByte();
                node.FrontChildUpper = reader.ReadByte();
                bsp.Bsp3dNodes[i] = node;
            }

            //Align to the next multiple of 16
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Planes.Count; ++i)
            {
                BSP.Plane plane = new BSP.Plane();
                plane.PlaneI = reader.ReadSingle();
                plane.PlaneJ = reader.ReadSingle();
                plane.PlaneK = reader.ReadSingle();
                plane.PlaneD = reader.ReadSingle();
                bsp.Planes[i] = plane;
            }

            //Put here for consistency
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Leaves.Count; ++i)
            {
                BSP.Leaf leaf = new BSP.Leaf();
                leaf.Flags = reader.ReadInt16();
                leaf.Bsp2dReferenceCount = reader.ReadInt16();
                leaf.Unknown = reader.ReadInt16();
                leaf.FirstBsp2dReference = reader.ReadInt16();
                bsp.Leaves[i] = leaf;
            }

            //Align to the next multiple of 16
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Bsp2dReferences.Count; ++i)
            {
                BSP.Bsp2dReference bsp2dref = new BSP.Bsp2dReference();
                bsp2dref.Plane = reader.ReadInt16();
                bsp2dref.Bsp2dNode = reader.ReadInt16();
                bsp.Bsp2dReferences[i] = bsp2dref;
            }

            //Align to the next multiple of 16
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Bsp2dNodes.Count; ++i)
            {
                BSP.Bsp2dNode node = new BSP.Bsp2dNode();
                node.PlaneI = reader.ReadSingle();
                node.PlaneJ = reader.ReadSingle();
                node.PlaneD = reader.ReadSingle();
                node.LeftChild = reader.ReadInt16();
                node.RightChild = reader.ReadInt16();
                bsp.Bsp2dNodes[i] = node;
            }

            //Put here for consistency
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Surfaces.Count; ++i)
            {
                BSP.Surface surface = new BSP.Surface();
                surface.Plane = reader.ReadUInt16();
                surface.FirstEdge = reader.ReadUInt16();
                surface.Material = reader.ReadInt16();
                surface.Unknown = reader.ReadInt16();
                surface.BreakableSurface = reader.ReadInt16();
                surface.Unknown2 = reader.ReadInt16();
                bsp.Surfaces[i] = surface;
            }

            //Align to the next multiple of 16
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Edges.Count; ++i)
            {
                BSP.Edge edge = new BSP.Edge();
                edge.StartVertex = reader.ReadUInt16();
                edge.EndVertex = reader.ReadUInt16();
                edge.ForwardEdge = reader.ReadUInt16();
                edge.ReverseEdge = reader.ReadUInt16();
                edge.LeftSurface = reader.ReadUInt16();
                edge.RightSurface = reader.ReadUInt16();
                bsp.Edges[i] = edge;
            }

            //Align to the next multiple of 16
            reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf);
            Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position);
            for (int i = 0; i < bsp.Vertices.Count; ++i)
            {
                BSP.Vertex vert = new BSP.Vertex();
                vert.PointX = reader.ReadSingle();
                vert.PointY = reader.ReadSingle();
                vert.PointZ = reader.ReadSingle();
                vert.FirstEdge = reader.ReadInt16();
                vert.Unknown = reader.ReadInt16();
                bsp.Vertices[i] = vert;
            }

            return bsp;
        }
示例#6
0
        private bool ExtractAMF(Model.Variant variant, string fileName)
        {
            // Load resource caches
            Console.WriteLine("Loading resource caches...");
            var resourceManager = new ResourceDataManager();

            try
            {
                resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName);
            }
            catch
            {
                Console.WriteLine("Unable to load the resource .dat files.");
                Console.WriteLine("Make sure that they all exist and are valid.");
                return(true);
            }

            // Deserialize the render model tag
            Console.WriteLine("Reading model data...");
            RenderModel renderModel;

            using (var cacheStream = Info.CacheFile.OpenRead())
            {
                var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Definition.RenderModel);
                renderModel = Info.Deserializer.Deserialize <RenderModel>(renderModelContext);
            }

            if (renderModel.Geometry.Resource == null)
            {
                Console.WriteLine("Render model does not have a resource associated with it");
                return(true);
            }

            // Deserialize the resource definition
            var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource);
            var definition      = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                // Extract the resource data
                resourceManager.Extract(renderModel.Geometry.Resource, resourceStream);

                var regionMeshes = new Dictionary <string, Mesh>();

                foreach (var region in variant.Regions)
                {
                    regionMeshes[Info.StringIDs.GetString(region.Name)] = renderModel.Geometry.Meshes[region.RenderModelRegionIndex];
                }

                var headerAddressList = new List <int>();
                var headerValueList   = new List <int>();
                var markerAddressList = new List <int>();
                var markerValueList   = new List <int>();
                var permAddressList   = new List <int>();
                var permValueList     = new List <int>();
                var vertAddressList   = new List <int>();
                var indxAddressList   = new List <int>();
                var meshAddressList   = new List <int>();

                using (var bw = new BinaryWriter(File.Create(fileName)))
                {
                    #region Header
                    bw.Write("AMF!".ToCharArray());
                    bw.Write(2.0f); //format version
                    bw.Write((Info.StringIDs.GetString(renderModel.Name) + "\0").ToCharArray());

                    bw.Write(renderModel.Nodes.Count);
                    headerAddressList.Add((int)bw.BaseStream.Position);
                    bw.Write(0);

                    bw.Write(renderModel.MarkerGroups.Count);
                    headerAddressList.Add((int)bw.BaseStream.Position);
                    bw.Write(0);

                    bw.Write(regionMeshes.Count);
                    headerAddressList.Add((int)bw.BaseStream.Position);
                    bw.Write(0);

                    bw.Write(renderModel.Materials.Count);
                    headerAddressList.Add((int)bw.BaseStream.Position);
                    bw.Write(0);
                    #endregion
                    #region Nodes
                    headerValueList.Add((int)bw.BaseStream.Position);
                    foreach (var node in renderModel.Nodes)
                    {
                        bw.Write((Info.StringIDs.GetString(node.Name) + "\0").ToCharArray());
                        bw.Write((short)node.ParentNode);
                        bw.Write((short)node.FirstChildNode);
                        bw.Write((short)node.NextSiblingNode);
                        bw.Write(node.DefaultTranslation.X * 100);
                        bw.Write(node.DefaultTranslation.Y * 100);
                        bw.Write(node.DefaultTranslation.Z * 100);
                        bw.Write(node.DefaultRotation.X);
                        bw.Write(node.DefaultRotation.Y);
                        bw.Write(node.DefaultRotation.Z);
                        bw.Write(node.DefaultRotation.W);
                    }
                    #endregion
                    #region Marker Groups
                    headerValueList.Add((int)bw.BaseStream.Position);
                    foreach (var group in renderModel.MarkerGroups)
                    {
                        bw.Write((Info.StringIDs.GetString(group.Name) + "\0").ToCharArray());
                        bw.Write(group.Markers.Count);
                        markerAddressList.Add((int)bw.BaseStream.Position);
                        bw.Write(0);
                    }
                    #endregion
                    #region Markers
                    foreach (var group in renderModel.MarkerGroups)
                    {
                        markerValueList.Add((int)bw.BaseStream.Position);
                        foreach (var marker in group.Markers)
                        {
                            bw.Write((byte)marker.RegionIndex);
                            bw.Write((byte)marker.PermutationIndex);
                            bw.Write((short)marker.NodeIndex);
                            bw.Write(marker.Translation.X * 100);
                            bw.Write(marker.Translation.Y * 100);
                            bw.Write(marker.Translation.Z * 100);
                            bw.Write(marker.Rotation.X);
                            bw.Write(marker.Rotation.Y);
                            bw.Write(marker.Rotation.Z);
                            bw.Write(marker.Rotation.W);
                        }
                    }
                    #endregion
                    #region Regions
                    headerValueList.Add((int)bw.BaseStream.Position);
                    foreach (var region in renderModel.Regions)
                    {
                        bw.Write((Info.StringIDs.GetString(region.Name) + "\0").ToCharArray());
                        bw.Write(regionMeshes.Count);
                        permAddressList.Add((int)bw.BaseStream.Position);
                        bw.Write(0);
                    }
                    #endregion
                    #region Permutations
                    foreach (var part in regionMeshes)
                    {
                        permValueList.Add((int)bw.BaseStream.Position);
                        bw.Write((Info.StringIDs.GetString(variant.Name) + "\0").ToCharArray());

                        if (part.Value.Type == VertexType.Rigid)
                        {
                            bw.Write((byte)1);
                        }
                        else if (part.Value.Type == VertexType.Skinned)
                        {
                            bw.Write((byte)2);
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }

                        bw.Write((byte)part.Value.RigidNodeIndex);

                        bw.Write(definition.VertexBuffers[part.Value.VertexBuffers[0]].Definition.Count);
                        vertAddressList.Add((int)bw.BaseStream.Position);
                        bw.Write(0);

                        int count = 0;
                        foreach (var submesh in part.Value.SubParts)
                        {
                            count += submesh.IndexCount;
                        }

                        bw.Write(count);
                        indxAddressList.Add((int)bw.BaseStream.Position);
                        bw.Write(0);

                        bw.Write(part.Value.SubParts.Count);
                        meshAddressList.Add((int)bw.BaseStream.Position);
                        bw.Write(0);

                        bw.Write(float.NaN); //no transforms (render_models are pre-transformed)
                    }
                    #endregion
                }
            }

            return(true);
        }