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);
        }
Beispiel #2
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

            var binkFile = new FileInfo(args[0]);

            var resourceContext    = new ResourceSerializationContext(CacheContext, Definition.Resource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <BinkResource>(resourceContext);

            using (var resourceStream = new MemoryStream())
                using (var resourceReader = new BinaryReader(resourceStream))
                    using (var fileStream = binkFile.Create())
                        using (var fileWriter = new BinaryWriter(fileStream))
                        {
                            CacheContext.ExtractResource(Definition.Resource, resourceStream);
                            resourceReader.BaseStream.Position = resourceDefinition.Data.Address.Offset;
                            fileWriter.Write(resourceReader.ReadBytes(resourceDefinition.Data.Size));
                        }

            Console.WriteLine($"Created \"{binkFile.FullName}\" successfully.");

            return(true);
        }
        public void InjectDds(Bitmap bitmap, int imageIndex, Stream ddsStream)
        {
            // Deserialize the old definition
            var resourceContext = new ResourceSerializationContext(bitmap.Resources[imageIndex].Resource);
            var definition      = TagDeserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext);

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

            // Read the DDS header and modify the definition to match
            var dds      = DdsHeader.Read(ddsStream);
            var dataSize = (int)(ddsStream.Length - ddsStream.Position);

            texture.Data            = new ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0));
            texture.Width           = (short)dds.Width;
            texture.Height          = (short)dds.Height;
            texture.Depth           = (sbyte)Math.Max(1, dds.Depth);
            texture.Levels          = (sbyte)Math.Max(1, dds.MipMapCount);
            texture.Type            = BitmapDdsFormatDetection.DetectType(dds);
            texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2"));
            texture.Format          = BitmapDdsFormatDetection.DetectFormat(dds);

            // Set flags based on the format
            switch (definition.Texture.Definition.Format)
            {
            case BitmapFormat.Dxt1:
            case BitmapFormat.Dxt3:
            case BitmapFormat.Dxt5:
            case BitmapFormat.Dxn:
                definition.Texture.Definition.Flags = BitmapFlags.Compressed;
                break;

            default:
                definition.Texture.Definition.Flags = BitmapFlags.None;
                break;
            }

            // Inject the resource data
            _resourceManager.Replace(bitmap.Resources[imageIndex].Resource, ddsStream);

            // Serialize the new resource definition
            TagSerializer.Serialize(resourceContext, definition);

            // Modify the image data in the bitmap tag to match the definition
            var imageData = bitmap.Images[imageIndex];

            imageData.Width       = texture.Width;
            imageData.Height      = texture.Height;
            imageData.Depth       = texture.Depth;
            imageData.Type        = texture.Type;
            imageData.Format      = texture.Format;
            imageData.Flags       = texture.Flags;
            imageData.MipmapCount = (sbyte)(texture.Levels - 1);
            imageData.DataOffset  = texture.Data.Address.Offset;
            imageData.DataSize    = texture.Data.Size;
        }
        public override object Execute(List <string> args)
        {
            if (args.Count != 3)
            {
                return(false);
            }

            var variantName   = args[0];
            var fileType      = args[1].ToLower();
            var modelFileName = args[2];

            switch (fileType)
            {
            case "obj":
                break;

            default:
                throw new NotImplementedException(fileType);
            }

            if (Definition.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(CacheContext, Definition.Geometry.Resource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                //
                // Extract the resource data
                //

                CacheContext.ExtractResource(Definition.Geometry.Resource, resourceStream);

                var modelFile = new FileInfo(modelFileName);

                if (!modelFile.Directory.Exists)
                {
                    modelFile.Directory.Create();
                }

                switch (fileType)
                {
                case "obj":
                    return(ExtractObj(variantName, modelFile, Definition, resourceDefinition, resourceStream));

                default:
                    throw new NotImplementedException(fileType);
                }
            }
        }
Beispiel #5
0
        public void ExtractBitmap(Bitmap bitmap, int imageIndex, Stream outStream)
        {
            var resource        = bitmap.Resources[imageIndex];
            var resourceContext = new ResourceSerializationContext(CacheContext, resource.Resource);
            var definition      = ExtractResourceDefinition(resource, resourceContext);

            var header = new DDSHeader(definition.Texture.Definition);

            header.Write(new EndianWriter(outStream));

            ExtractResourceData(definition, resource, outStream);
        }
        private void SerializeDefinitionData(TagSerializer serializer, RenderGeometryResourceDefinition definition)
        {
            _model.Geometry.Resource = new ResourceReference
            {
                Type             = 5, // FIXME: hax
                DefinitionFixups = new List <ResourceDefinitionFixup>(),
                D3DObjectFixups  = new List <D3DObjectFixup>(),
                Unknown68        = 1,
            };
            var context = new ResourceSerializationContext(_model.Geometry.Resource);

            serializer.Serialize(context, definition);
        }
Beispiel #7
0
        private void SerializeDefinitionData(TagSerializer serializer, RenderGeometryApiResourceDefinition definition)
        {
            _model.Geometry.Resource = new PageableResource
            {
                Page     = new RawPage(),
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.RenderGeometry,
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };
            var context = new ResourceSerializationContext(CacheContext, _model.Geometry.Resource);

            serializer.Serialize(context, definition);
        }
Beispiel #8
0
        public ModelExtractor(HaloOnlineCacheContext cacheContext, RenderModel renderModel)
        {
            Scene = new Scene();

            CacheContext = cacheContext;
            RenderModel  = renderModel;

            MeshMapping = new Dictionary <int, int>();
            BoneNodes   = new List <Node>();

            // Deserialize the render_model resource
            var resourceContext = new ResourceSerializationContext(CacheContext, RenderModel.Geometry.Resource);

            RenderModelResourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);
            RenderModelResourceStream     = new MemoryStream();
            CacheContext.ExtractResource(RenderModel.Geometry.Resource, RenderModelResourceStream);
        }
Beispiel #9
0
        private BitmapTextureInteropResource ExtractResourceDefinition(Bitmap.BitmapResource resource, ResourceSerializationContext context)
        {
            var definition = CacheContext.Deserializer.Deserialize <BitmapTextureInteropResource>(context);

            if (definition.Texture == null || definition.Texture.Definition == null)
            {
                throw new ArgumentException("Invalid bitmap definition");
            }
            return(definition);
        }
        public RenderGeometry Convert(Stream cacheStream, RenderGeometry geometry, Dictionary <ResourceLocation, Stream> resourceStreams, PortTagCommand.PortingFlags portingFlags)
        {
            if (BlamCache.ResourceGestalt == null || BlamCache.ResourceLayoutTable == null)
            {
                BlamCache.LoadResourceTags();
            }

            //
            // Convert byte[] of UnknownBlock
            //

            foreach (var block in geometry.Unknown2)
            {
                var data = block.Unknown3;
                if (data != null || data.Length != 0)
                {
                    var result = new byte[data.Length];

                    using (var inputReader = new EndianReader(new MemoryStream(data), EndianFormat.BigEndian))
                        using (var outputWriter = new EndianWriter(new MemoryStream(result), EndianFormat.LittleEndian))
                        {
                            while (!inputReader.EOF)
                            {
                                outputWriter.Write(inputReader.ReadUInt32());
                            }

                            block.Unknown3 = result;
                        }
                }
            }

            //
            // Convert UnknownSection.Unknown byte[] endian
            //

            for (int i = 0; i < geometry.UnknownSections.Count; i++)
            {
                byte[] dataref = geometry.UnknownSections[i].Unknown;

                if (dataref.Length == 0)
                {
                    continue;
                }

                using (var outStream = new MemoryStream())
                    using (var outReader = new BinaryReader(outStream))
                        using (var outWriter = new EndianWriter(outStream, EndianFormat.LittleEndian))
                            using (var stream = new MemoryStream(dataref))
                                using (var reader = new EndianReader(stream, EndianFormat.BigEndian))
                                {
                                    var dataContext = new DataSerializationContext(reader, outWriter);
                                    var header      = CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext);

                                    var section = new ScenarioLightmapBspDataSection
                                    {
                                        Headers = new List <ScenarioLightmapBspDataSection.Header>
                                        {
                                            header
                                        },
                                        VertexLists = new ScenarioLightmapBspDataSection.VertexList
                                        {
                                            Vertex = new List <ScenarioLightmapBspDataSection.VertexList.Datum>()
                                        }
                                    };

                                    CacheContext.Serializer.Serialize(dataContext, header);

                                    while (reader.BaseStream.Position < dataref.Length) // read the rest of dataref
                                    {
                                        if (section.Headers.Count == 2)                 // remove "wrongfully" added ones
                                        {
                                            section.Headers.RemoveAt(1);
                                        }

                                        section.Headers.Add(CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext));

                                        // if some values match header1, continue
                                        if (section.Headers[0].Position == section.Headers[1].Position)
                                        {
                                            header = section.Headers[1];
                                            CacheContext.Serializer.Serialize(dataContext, header);

                                            while (reader.BaseStream.Position < dataref.Length)
                                            {
                                                section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum {
                                                    Value = reader.ReadByte()
                                                });
                                                outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value);
                                            }
                                        }
                                        else // if read data doesn't match, go back and just read 4 bytes
                                        {
                                            reader.BaseStream.Position = reader.BaseStream.Position - 0x2C; // if read data doesn't match, go back and serialize

                                            section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum {
                                                Value = reader.ReadByte()
                                            });
                                            outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value);

                                            section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum {
                                                Value = reader.ReadByte()
                                            });
                                            outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value);

                                            section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum {
                                                Value = reader.ReadByte()
                                            });
                                            outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value);

                                            section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum {
                                                Value = reader.ReadByte()
                                            });
                                            outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value);
                                        }
                                    }

                                    // Write back to tag
                                    outStream.Position = 0;
                                    geometry.UnknownSections[i].Unknown = outStream.ToArray();
                                }
            }

            //
            // Set up ElDorado resource reference
            //

            geometry.Resource = new PageableResource
            {
                Page = new RawPage
                {
                    Index = -1
                },
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.RenderGeometry,
                    DefinitionData           = new byte[0x30],
                    DefinitionAddress        = new CacheAddress(CacheAddressType.Definition, 0),
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };

            //
            // Port Blam resource definition
            //

            var resourceEntry = BlamCache.ResourceGestalt.TagResources[geometry.ZoneAssetHandle & ushort.MaxValue];

            geometry.Resource.Resource.DefinitionAddress = resourceEntry.DefinitionAddress;
            geometry.Resource.Resource.DefinitionData    = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray();

            RenderGeometryApiResourceDefinition resourceDefinition = null;

            if (geometry.Resource.Resource.DefinitionData.Length < 0x30)
            {
                resourceDefinition = new RenderGeometryApiResourceDefinition
                {
                    VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(),
                    IndexBuffers  = new List <TagStructureReference <IndexBufferDefinition> >()
                };
            }
            else
            {
                using (var definitionStream = new MemoryStream(geometry.Resource.Resource.DefinitionData, true))
                    using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian))
                        using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian))
                        {
                            foreach (var fixup in resourceEntry.ResourceFixups)
                            {
                                definitionStream.Position = fixup.BlockOffset;
                                definitionWriter.Write(fixup.Address.Value);

                                geometry.Resource.Resource.ResourceFixups.Add(fixup);
                            }

                            foreach (var definitionFixup in resourceEntry.ResourceDefinitionFixups)
                            {
                                var newDefinitionFixup = new TagResourceGen3.ResourceDefinitionFixup
                                {
                                    Address = definitionFixup.Address,
                                    ResourceStructureTypeIndex = definitionFixup.ResourceStructureTypeIndex
                                };

                                geometry.Resource.Resource.ResourceDefinitionFixups.Add(newDefinitionFixup);
                            }

                            var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheAddressType.Definition);

                            definitionStream.Position = geometry.Resource.Resource.DefinitionAddress.Offset;
                            resourceDefinition        = BlamCache.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(dataContext);
                        }
            }

            //
            // Load Blam resource data
            //

            var resourceData      = BlamCache.GetRawFromID(geometry.ZoneAssetHandle);
            var generateParticles = false;

            if (resourceData == null)
            {
                if (geometry.Meshes.Count == 1 && geometry.Meshes[0].Type == VertexType.ParticleModel)
                {
                    generateParticles = true;
                    resourceData      = new byte[0];
                }
                else
                {
                    geometry.Resource.Resource.ResourceType = TagResourceTypeGen3.None;
                    return(geometry);
                }
            }

            //
            // Convert Blam data to ElDorado data
            //

            using (var dataStream = new MemoryStream())
                using (var blamResourceStream = new MemoryStream(resourceData))
                {
                    for (int i = 0; i < geometry.Meshes.Count(); i++)
                    {
                        var mesh = geometry.Meshes[i];

                        if (mesh.VertexBufferIndices[6] != 0xFFFF && mesh.VertexBufferIndices[7] != 0xFFFF)
                        {
                            ushort temp = mesh.VertexBufferIndices[6];
                            mesh.VertexBufferIndices[6] = mesh.VertexBufferIndices[7];
                            mesh.VertexBufferIndices[7] = temp;

                            // Get total amount of indices

                            int indexCount = 0;

                            foreach (var subpart in mesh.SubParts)
                            {
                                indexCount += subpart.IndexCount;
                            }

                            WaterConversionData waterData = new WaterConversionData()
                            {
                                IndexBufferLength = indexCount,
                            };

                            for (int j = 0; j < mesh.Parts.Count(); j++)
                            {
                                var part = mesh.Parts[j];
                                waterData.PartData.Add(new Tuple <int, int, bool>(part.FirstIndex, part.IndexCount, part.FlagsNew.HasFlag(Mesh.Part.PartFlagsNew.CanBeRenderedInDrawBundles)));
                            }
                            waterData.Sort();
                            WaterData.Add(waterData);
                        }
                    }

                    if (generateParticles)
                    {
                        var outVertexStream = VertexStreamFactory.Create(CacheContext.Version, dataStream);

                        StreamUtil.Align(dataStream, 4);

                        resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition>
                        {
                            Definition = new VertexBufferDefinition
                            {
                                Format = VertexBufferFormat.ParticleModel,
                                Data   = new TagData
                                {
                                    Size     = 32,
                                    Unused4  = 0,
                                    Unused8  = 0,
                                    Address  = new CacheAddress(CacheAddressType.Resource, (int)dataStream.Position),
                                    Unused10 = 0
                                }
                            }
                        });

                        var vertexBuffer = resourceDefinition.VertexBuffers.Last().Definition;

                        for (var j = 0; j < 3; j++)
                        {
                            outVertexStream.WriteParticleModelVertex(new ParticleModelVertex
                            {
                                Position = new RealVector3d(),
                                Texcoord = new RealVector2d(),
                                Normal   = new RealVector3d()
                            });
                        }

                        geometry.Meshes[0].VertexBufferIndices[0] = (ushort)resourceDefinition.VertexBuffers.IndexOf(resourceDefinition.VertexBuffers.Last());
                        geometry.Meshes[0].IndexBufferIndices[0]  = CreateIndexBuffer(resourceDefinition, dataStream, 3);
                    }
                    else
                    {
                        for (int i = 0, prevVertCount = -1; i < resourceDefinition.VertexBuffers.Count; i++, prevVertCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count)
                        {
                            blamResourceStream.Position = resourceDefinition.VertexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[i].Offset;
                            ConvertVertexBuffer(resourceDefinition, blamResourceStream, dataStream, i, prevVertCount);
                        }

                        for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++)
                        {
                            blamResourceStream.Position = resourceDefinition.IndexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset;
                            ConvertIndexBuffer(resourceDefinition, blamResourceStream, dataStream, i);
                        }

                        foreach (var mesh in geometry.Meshes)
                        {
                            if (!mesh.Flags.HasFlag(MeshFlags.MeshIsUnindexed))
                            {
                                continue;
                            }

                            var indexCount = 0;

                            foreach (var part in mesh.Parts)
                            {
                                indexCount += part.IndexCount;
                            }

                            mesh.IndexBufferIndices[0] = CreateIndexBuffer(resourceDefinition, dataStream, indexCount);
                        }
                    }

                    //
                    // Swap order of water vertex buffers
                    //

                    for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++)
                    {
                        var vertexBuffer = resourceDefinition.VertexBuffers[i];

                        if (vertexBuffer.Definition.Format == VertexBufferFormat.Unknown1B)
                        {
                            TagStructureReference <VertexBufferDefinition> temp = vertexBuffer;
                            resourceDefinition.VertexBuffers[i]     = resourceDefinition.VertexBuffers[i - 1];
                            resourceDefinition.VertexBuffers[i - 1] = temp;
                        }
                    }

                    //
                    // Finalize the new ElDorado geometry resource
                    //

                    var cache = CacheContext.GetResourceCache(ResourceLocation.Resources);

                    if (!resourceStreams.ContainsKey(ResourceLocation.Resources))
                    {
                        resourceStreams[ResourceLocation.Resources] = portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory) ?
                                                                      new MemoryStream() :
                                                                      (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Resources);

                        if (portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory))
                        {
                            using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Resources))
                                resourceStream.CopyTo(resourceStreams[ResourceLocation.Resources]);
                        }
                    }

                    geometry.Resource.ChangeLocation(ResourceLocation.Resources);

                    geometry.Resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Resources], dataStream.ToArray(), out uint compressedSize);
                    geometry.Resource.Page.CompressedBlockSize   = compressedSize;
                    geometry.Resource.Page.UncompressedBlockSize = (uint)dataStream.Length;
                    geometry.Resource.DisableChecksum();

                    var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource);
                    CacheContext.Serializer.Serialize(resourceContext, resourceDefinition);
                }

            return(geometry);
        }
Beispiel #11
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);
        }
Beispiel #12
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 3)
            {
                return(false);
            }

            var variantName   = args[0];
            var fileType      = args[1].ToLower();
            var modelFileName = args[2];

            switch (fileType)
            {
            case "obj":
            case "amf":
            case "dae":
                break;

            default:
                throw new NotImplementedException(fileType);
            }

            //
            // 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 modelVariant = Definition.Variants.FirstOrDefault(v => (CacheContext.GetString(v.Name) ?? v.Name.ToString()) == variantName);

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

            //
            // Deserialize the render model tag
            //

            RenderModel renderModel;

            using (var cacheStream = CacheContext.TagCacheFile.OpenRead())
            {
                renderModel = CacheContext.Deserialize <RenderModel>(cacheStream, Definition.RenderModel);
            }

            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(CacheContext, renderModel.Geometry.Resource);
            var resourceDefinition = CacheContext.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                //
                // Extract the resource data
                //

                CacheContext.ExtractResource(renderModel.Geometry.Resource, resourceStream);

                var modelFile = new FileInfo(modelFileName);

                if (!modelFile.Directory.Exists)
                {
                    modelFile.Directory.Create();
                }

                switch (fileType)
                {
                case "obj":
                    ExtractObj(modelFile, renderModel, modelVariant, resourceDefinition, resourceStream);
                    break;

                case "amf":
                    ExtractAmf(modelFile, renderModel, modelVariant, resourceDefinition, resourceStream);
                    break;

                case "dae":
                    ModelExtractor extractor = new ModelExtractor(CacheContext, renderModel);
                    extractor.ExtractRenderModel();
                    extractor.ExportCollada(modelFile);
                    break;

                default:
                    throw new NotImplementedException(fileType);
                }
            }

            Console.WriteLine("Done!");

            return(true);
        }
        private Sound ConvertSound(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, Sound sound, string blamTag_Name)
        {
            if (BlamSoundGestalt == null)
            {
                BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache);
            }

            if (!File.Exists(@"Tools\ffmpeg.exe") || !File.Exists(@"Tools\towav.exe"))
            {
                Console.WriteLine("Failed to locate sound conversion tools, please install ffmpeg and towav in the Tools folder");
                return(null);
            }

            //
            // Convert Sound Tag Data
            //

            var platformCodec      = BlamSoundGestalt.PlatformCodecs[sound.SoundReference.PlatformCodecIndex];
            var playbackParameters = BlamSoundGestalt.PlaybackParameters[sound.SoundReference.PlaybackParameterIndex];
            var scale          = BlamSoundGestalt.Scales[sound.SoundReference.ScaleIndex];
            var promotion      = sound.SoundReference.PromotionIndex != -1 ? BlamSoundGestalt.Promotions[sound.SoundReference.PromotionIndex] : new Promotion();
            var customPlayBack = sound.SoundReference.CustomPlaybackIndex != -1 ? new List <CustomPlayback> {
                BlamSoundGestalt.CustomPlaybacks[sound.SoundReference.CustomPlaybackIndex]
            } : new List <CustomPlayback>();
            var loop = sound.Flags.HasFlag(Sound.FlagsValue.LoopingSound);

            sound.PlaybackParameters = playbackParameters;
            sound.Scale           = scale;
            sound.PlatformCodec   = platformCodec.DeepClone();
            sound.Promotion       = promotion;
            sound.CustomPlayBacks = customPlayBack;

            //
            // Tag fixes
            //

            sound.SampleRate = platformCodec.SampleRate;
            sound.ImportType = ImportType.SingleLayer;
            // helps looping sound? there is another value, 10 for Unknown2 but I don't know when to activate it.
            if (sound.SoundReference.PitchRangeCount > 1)
            {
                sound.ImportType = ImportType.MultiLayer;
            }

            sound.PlatformCodec.LoadMode = 0;

            //
            // Process all the pitch ranges
            //

            var xmaFileSize = BlamSoundGestalt.GetFileSize(sound.SoundReference.PitchRangeIndex, sound.SoundReference.PitchRangeCount);

            if (xmaFileSize < 0)
            {
                return(null);
            }

            var xmaData = BlamCache.GetSoundRaw(sound.SoundReference.ZoneAssetHandle, xmaFileSize);

            if (xmaData == null)
            {
                return(null);
            }

            sound.PitchRanges = new List <PitchRange>(sound.SoundReference.PitchRangeCount);

            var soundDataAggregate     = new byte[0].AsEnumerable();
            var currentSoundDataOffset = 0;
            var totalSampleCount       = (uint)0;

            for (int pitchRangeIndex = sound.SoundReference.PitchRangeIndex; pitchRangeIndex < sound.SoundReference.PitchRangeIndex + sound.SoundReference.PitchRangeCount; pitchRangeIndex++)
            {
                totalSampleCount += BlamSoundGestalt.GetSamplesPerPitchRange(pitchRangeIndex);

                //
                // Convert Blam pitch range to ElDorado format
                //

                var pitchRange = BlamSoundGestalt.PitchRanges[pitchRangeIndex];
                pitchRange.ImportName           = ConvertStringId(BlamSoundGestalt.ImportNames[pitchRange.ImportNameIndex].Name);
                pitchRange.PitchRangeParameters = BlamSoundGestalt.PitchRangeParameters[pitchRange.PitchRangeParametersIndex];
                pitchRange.Unknown1             = 0;
                pitchRange.Unknown2             = 0;
                pitchRange.Unknown3             = 0;
                pitchRange.Unknown4             = 0;
                pitchRange.Unknown5             = -1;
                pitchRange.Unknown6             = -1;
                //I suspect this unknown7 to be a flag to tell if there is a Unknownblock in extrainfo. (See a sound in udlg for example)
                pitchRange.Unknown7         = 0;
                pitchRange.PermutationCount = (byte)BlamSoundGestalt.GetPermutationCount(pitchRangeIndex);
                pitchRange.Unknown8         = -1;

                // Add pitch range to ED sound
                sound.PitchRanges.Add(pitchRange);
                var newPitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex;
                sound.PitchRanges[newPitchRangeIndex].Permutations = new List <Permutation>();

                //
                // Determine the audio channel count
                //

                var channelCount = Encoding.GetChannelCount(sound.PlatformCodec.Encoding);

                //
                // Set compression format
                //

                sound.PlatformCodec.Compression = Compression.MP3;

                //
                // Convert Blam permutations to ElDorado format
                //

                var useCache = Sounds.UseAudioCacheCommand.AudioCacheDirectory != null;

                var basePermutationCacheName = Path.Combine(
                    useCache ? Sounds.UseAudioCacheCommand.AudioCacheDirectory.FullName : TempDirectory.FullName,
                    GetTagFileFriendlyName(blamTag_Name));

                var permutationCount        = BlamSoundGestalt.GetPermutationCount(pitchRangeIndex);
                var permutationOrder        = BlamSoundGestalt.GetPermutationOrder(pitchRangeIndex);
                var relativePitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex;

                for (int i = 0; i < permutationCount; i++)
                {
                    var permutationIndex  = pitchRange.FirstPermutationIndex + i;
                    var permutationSize   = BlamSoundGestalt.GetPermutationSize(permutationIndex);
                    var permutationOffset = BlamSoundGestalt.GetPermutationOffset(permutationIndex);

                    var permutation = BlamSoundGestalt.GetPermutation(permutationIndex).DeepClone();

                    permutation.ImportName            = ConvertStringId(BlamSoundGestalt.ImportNames[permutation.ImportNameIndex].Name);
                    permutation.SkipFraction          = new Bounds <float>(0.0f, permutation.Gain);
                    permutation.PermutationChunks     = new List <PermutationChunk>();
                    permutation.PermutationNumber     = (uint)permutationOrder[i];
                    permutation.IsNotFirstPermutation = (uint)(permutation.PermutationNumber == 0 ? 0 : 1);

                    string permutationName = $"{basePermutationCacheName}_{relativePitchRangeIndex}_{i}";

                    string extension = "mp3";

                    var cacheFileName = $"{permutationName}.{extension}";

                    bool exists = File.Exists(cacheFileName);

                    byte[] permutationData = null;

                    if (!exists || !useCache)
                    {
                        BlamSound blamSound = SoundConverter.ConvertGen3Sound(BlamCache, BlamSoundGestalt, sound, relativePitchRangeIndex, i, xmaData);
                        permutationData = blamSound.Data;
                        if (useCache)
                        {
                            using (EndianWriter output = new EndianWriter(new FileStream(cacheFileName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian))
                            {
                                output.WriteBlock(blamSound.Data);
                            }
                        }
                    }
                    else
                    {
                        permutationData = File.ReadAllBytes(cacheFileName);
                    }

                    permutation.PermutationChunks.Add(new PermutationChunk(currentSoundDataOffset, permutationData.Length));
                    currentSoundDataOffset += permutationData.Length;
                    pitchRange.Permutations.Add(permutation);

                    soundDataAggregate = soundDataAggregate.Concat(permutationData);
                }
            }

            sound.Promotion.LongestPermutationDuration = (uint)sound.SoundReference.MaximumPlayTime;
            sound.Promotion.TotalSampleSize            = totalSampleCount;

            //
            // Convert Blam extra info to ElDorado format
            //

            var extraInfo = new ExtraInfo()
            {
                LanguagePermutations       = new List <ExtraInfo.LanguagePermutation>(),
                EncodedPermutationSections = new List <ExtraInfo.EncodedPermutationSection>()
            };

            for (int u = 0; u < sound.SoundReference.PitchRangeCount; u++)
            {
                var pitchRange = BlamSoundGestalt.PitchRanges[sound.SoundReference.PitchRangeIndex + u];

                for (int i = 0; i < pitchRange.PermutationCount; i++)
                {
                    var permutation      = BlamSoundGestalt.GetPermutation(pitchRange.FirstPermutationIndex + i);
                    var permutationChunk = BlamSoundGestalt.GetPermutationChunk(permutation.FirstPermutationChunkIndex);

                    extraInfo.LanguagePermutations.Add(new ExtraInfo.LanguagePermutation
                    {
                        RawInfo = new List <ExtraInfo.LanguagePermutation.RawInfoBlock>
                        {
                            new ExtraInfo.LanguagePermutation.RawInfoBlock
                            {
                                SkipFractionName = StringId.Invalid,
                                UnknownList      = new List <ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown>
                                {
                                    new ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown
                                    {
                                        Unknown1 = permutationChunk.UnknownA,
                                        Unknown2 = permutationChunk.UnknownSize,
                                        Unknown3 = 0,
                                        Unknown4 = permutation.SampleSize,
                                        Unknown5 = 0,
                                        Unknown6 = permutationChunk.EncodedSize & 0xFFFF
                                    }
                                },
                                Compression          = 8,
                                ResourceSampleSize   = pitchRange.Permutations[i].SampleSize,
                                ResourceSampleOffset = (uint)pitchRange.Permutations[i].PermutationChunks[0].Offset,
                                SampleCount          = (uint)pitchRange.Permutations[i].PermutationChunks[0].EncodedSize & 0x3FFFFFF,
                                //SampleCount = (uint)Math.Floor(pitchRange.Permutations[i].SampleSize * 128000.0 / (8 * sound.SampleRate.GetSampleRateHz())),
                                Unknown24 = 480
                            }
                        }
                    });
                }
            }

            if (sound.SoundReference.ExtraInfoIndex != -1)
            {
                foreach (var section in BlamSoundGestalt.ExtraInfo[sound.SoundReference.ExtraInfoIndex].EncodedPermutationSections)
                {
                    var newSection = section.DeepClone();

                    foreach (var info in newSection.SoundDialogueInfo)
                    {
                        for (var i = ((info.MouthDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.MouthDataLength; i += 2)
                        {
                            Array.Reverse(newSection.EncodedData, (int)(info.MouthDataOffset + i), 2);
                        }

                        for (var i = ((info.LipsyncDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.LipsyncDataLength; i += 2)
                        {
                            Array.Reverse(newSection.EncodedData, (int)(info.LipsyncDataOffset + i), 2);
                        }
                    }

                    extraInfo.EncodedPermutationSections.Add(newSection);
                }
            }

            sound.ExtraInfo = new List <ExtraInfo> {
                extraInfo
            };

            //
            // Convert Blam languages to ElDorado format
            //

            if (sound.SoundReference.LanguageIndex != -1)
            {
                sound.Languages = new List <LanguageBlock>();

                foreach (var language in BlamSoundGestalt.Languages)
                {
                    sound.Languages.Add(new LanguageBlock
                    {
                        Language             = language.Language,
                        PermutationDurations = new List <LanguageBlock.PermutationDurationBlock>(),
                        PitchRangeDurations  = new List <LanguageBlock.PitchRangeDurationBlock>(),
                    });

                    //Add all the  Pitch Range Duration block (pitch range count dependent)

                    var curLanguage = sound.Languages.Last();

                    for (int i = 0; i < sound.SoundReference.PitchRangeCount; i++)
                    {
                        curLanguage.PitchRangeDurations.Add(language.PitchRangeDurations[sound.SoundReference.LanguageIndex + i]);
                    }

                    //Add all the Permutation Duration Block and adjust offsets

                    for (int i = 0; i < curLanguage.PitchRangeDurations.Count; i++)
                    {
                        var curPRD = curLanguage.PitchRangeDurations[i];

                        //Get current block count for new index
                        short newPermutationIndex = (short)curLanguage.PermutationDurations.Count();

                        for (int j = curPRD.PermutationStartIndex; j < curPRD.PermutationStartIndex + curPRD.PermutationCount; j++)
                        {
                            curLanguage.PermutationDurations.Add(language.PermutationDurations[j]);
                        }

                        //apply new index
                        curPRD.PermutationStartIndex = newPermutationIndex;
                    }
                }
            }

            //
            // Prepare resource
            //

            sound.Unused    = new byte[] { 0, 0, 0, 0 };
            sound.Unknown12 = 0;

            sound.Resource = new PageableResource
            {
                Page = new RawPage
                {
                    Index = -1,
                },
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.Sound,
                    DefinitionData           = new byte[20],
                    DefinitionAddress        = new CacheResourceAddress(CacheResourceAddressType.Definition, 536870912),
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };

            var data = soundDataAggregate.ToArray();

            var resourceContext = new ResourceSerializationContext(CacheContext, sound.Resource);

            CacheContext.Serializer.Serialize(resourceContext,
                                              new SoundResourceDefinition
            {
                Data = new TagData(data.Length, new CacheResourceAddress(CacheResourceAddressType.Resource, 0))
            });

            var definitionFixup = new TagResourceGen3.ResourceFixup()
            {
                BlockOffset = 12,
                Address     = new CacheResourceAddress(CacheResourceAddressType.Resource, 1073741824)
            };

            sound.Resource.Resource.ResourceFixups.Add(definitionFixup);

            sound.Resource.ChangeLocation(ResourceLocation.Audio);
            var resource = sound.Resource;

            if (resource == null)
            {
                throw new ArgumentNullException("resource");
            }

            var cache = CacheContext.GetResourceCache(ResourceLocation.Audio);

            if (!resourceStreams.ContainsKey(ResourceLocation.Audio))
            {
                resourceStreams[ResourceLocation.Audio] = FlagIsSet(PortingFlags.Memory) ?
                                                          new MemoryStream() :
                                                          (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Audio);

                if (FlagIsSet(PortingFlags.Memory))
                {
                    using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Audio))
                        resourceStream.CopyTo(resourceStreams[ResourceLocation.Audio]);
                }
            }

            resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Audio], data, out uint compressedSize);
            resource.Page.CompressedBlockSize   = compressedSize;
            resource.Page.UncompressedBlockSize = (uint)data.Length;
            resource.DisableChecksum();

            for (int i = 0; i < 4; i++)
            {
                sound.Resource.Resource.DefinitionData[i] = (byte)(sound.Resource.Page.UncompressedBlockSize >> (i * 8));
            }

            return(sound);
        }
Beispiel #14
0
        public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream)
        {
            var resource    = bitmap.Resources[imageIndex].Resource;
            var newResource = (resource == null);
            ResourceSerializationContext    resourceContext;
            BitmapTextureResourceDefinition definition;

            if (newResource)
            {
                // Create a new resource reference
                resource = new ResourceReference
                {
                    DefinitionFixups = new List <ResourceDefinitionFixup>(),
                    D3DObjectFixups  = new List <D3DObjectFixup>(),
                    Type             = 1, // TODO: Map out this type enum instead of using numbers
                    Unknown68        = 1
                };
                bitmap.Resources[imageIndex].Resource = resource;
                resourceContext = new ResourceSerializationContext(resource);
                definition      = new BitmapTextureResourceDefinition
                {
                    Texture = new D3DPointer <BitmapTextureResourceDefinition.BitmapDefinition>
                    {
                        Definition = new BitmapTextureResourceDefinition.BitmapDefinition()
                    }
                };
            }
            else
            {
                // Deserialize the old definition
                resourceContext = new ResourceSerializationContext(resource);
                definition      = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext);
            }
            if (definition.Texture == null || definition.Texture.Definition == null)
            {
                throw new ArgumentException("Invalid bitmap definition");
            }
            var texture   = definition.Texture.Definition;
            var imageData = bitmap.Images[imageIndex];

            // Read the DDS header and modify the definition to match
            var dds      = DdsHeader.Read(ddsStream);
            var dataSize = (int)(ddsStream.Length - ddsStream.Position);

            texture.Data            = new ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0));
            texture.Width           = (short)dds.Width;
            texture.Height          = (short)dds.Height;
            texture.Depth           = (sbyte)Math.Max(1, dds.Depth);
            texture.Levels          = (sbyte)Math.Max(1, dds.MipMapCount);
            texture.Type            = BitmapDdsFormatDetection.DetectType(dds);
            texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2"));
            texture.Format          = BitmapDdsFormatDetection.DetectFormat(dds);

            // Set flags based on the format
            switch (texture.Format)
            {
            case BitmapFormat.Dxt1:
            case BitmapFormat.Dxt3:
            case BitmapFormat.Dxt5:
            case BitmapFormat.Dxn:
                texture.Flags = BitmapFlags.Compressed;
                break;

            default:
                texture.Flags = BitmapFlags.None;
                break;
            }
            if ((texture.Width & (texture.Width - 1)) == 0 && (texture.Height & (texture.Height - 1)) == 0)
            {
                texture.Flags |= BitmapFlags.PowerOfTwoDimensions;
            }

            // If creating a new image, then add a new resource, otherwise replace the existing one
            if (newResource)
            {
                _resourceManager.Add(resource, ResourceLocation.Textures, ddsStream);
            }
            else
            {
                _resourceManager.Replace(resource, ddsStream);
            }

            // Serialize the new resource definition
            serializer.Serialize(resourceContext, definition);

            // Modify the image data in the bitmap tag to match the definition
            imageData.Width       = texture.Width;
            imageData.Height      = texture.Height;
            imageData.Depth       = texture.Depth;
            imageData.Type        = texture.Type;
            imageData.Format      = texture.Format;
            imageData.Flags       = texture.Flags;
            imageData.MipmapCount = (sbyte)(texture.Levels - 1);
            imageData.DataOffset  = texture.Data.Address.Offset;
            imageData.DataSize    = texture.Data.Size;
            imageData.Unknown15   = texture.Unknown35;
        }
Beispiel #15
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

            var outDirectory = args[0];

            if (!Directory.Exists(outDirectory))
            {
                Console.Write("Destination directory does not exist. Create it? [y/n] ");
                var answer = Console.ReadLine().ToLower();

                if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n")))
                {
                    return(false);
                }

                if (answer.StartsWith("y"))
                {
                    Directory.CreateDirectory(outDirectory);
                }
                else
                {
                    return(false);
                }
            }


            var resource           = Definition.Resource;
            var resourceContext    = new ResourceSerializationContext(CacheContext, resource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <SoundResourceDefinition>(resourceContext);

            if (resourceDefinition.Data == null)
            {
                Console.WriteLine("Invalid sound definition");
                return(false);
            }

            var dataReference = resourceDefinition.Data;


            byte[] soundData          = new byte[dataReference.Size];
            var    resourceDataStream = new MemoryStream(soundData);

            CacheContext.ExtractResource(resource, resourceDataStream);

            for (int i = 0; i < Definition.PitchRanges.Count; i++)
            {
                var pitchRange = Definition.PitchRanges[i];
                for (int j = 0; j < pitchRange.Permutations.Count; j++)
                {
                    var permutation = pitchRange.Permutations[j];
                    var filename    = Tag.Index.ToString("X8") + "_" + i.ToString() + "_" + j.ToString();

                    byte[] permutationData = new byte[permutation.PermutationChunks[0].EncodedSize & 0x3FFFFFF];
                    Array.Copy(soundData, permutation.PermutationChunks[0].Offset, permutationData, 0, permutationData.Length);

                    switch (Definition.PlatformCodec.Compression)
                    {
                    case Compression.PCM:
                        filename += ".wav";
                        break;

                    case Compression.MP3:
                        filename += ".mp3";
                        break;

                    case Compression.FSB4:
                        filename += ".fsb";
                        break;
                    }

                    var outPath = Path.Combine(outDirectory, filename);

                    using (EndianWriter writer = new EndianWriter(new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian))
                    {
                        var channelCount = Encoding.GetChannelCount(Definition.PlatformCodec.Encoding);
                        var sampleRate   = Definition.SampleRate.GetSampleRateHz();

                        switch (Definition.PlatformCodec.Compression)
                        {
                        case Compression.PCM:
                            WAVFile WAVfile = new WAVFile(permutationData, channelCount, sampleRate);
                            WAVfile.Write(writer);
                            break;

                        case Compression.MP3:
                        case Compression.FSB4:
                            writer.Write(permutationData);
                            break;
                        }
                    }
                }
            }
            Console.WriteLine("Done!");
            return(true);
        }
Beispiel #16
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);
        }
Beispiel #17
0
        private RenderGeometry ConvertGeometry(RenderGeometry geometry, GameCacheContextHaloOnline srcCacheContext, GameCacheContextHaloOnline destCacheContext)
        {
            if (geometry == null || geometry.Resource.HaloOnlinePageableResource == null || geometry.Resource.HaloOnlinePageableResource.Page.Index < 0 || !geometry.Resource.HaloOnlinePageableResource.GetLocation(out var location))
            {
                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  = CacheVersionDetection.Compare(srcCacheContext.Version, CacheVersion.HaloOnline235640);
            var destCompare = CacheVersionDetection.Compare(destCacheContext.Version, CacheVersion.HaloOnline235640);

            if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0))
            {
                geometry.Resource.HaloOnlinePageableResource = ConvertResource(geometry.Resource.HaloOnlinePageableResource, srcCacheContext, destCacheContext);
                return(geometry);
            }

            Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.HaloOnlinePageableResource.Page.Index, location);
            using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream())
            {
                // First extract the model data
                srcCacheContext.ExtractResource(geometry.Resource.HaloOnlinePageableResource, inStream);

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

                // Deserialize the definition data
                var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource.HaloOnlinePageableResource);
                var definition      = srcCacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

                // Convert each vertex buffer
                foreach (var buffer in definition.VertexBuffers)
                {
                    ConvertVertexBuffer(srcCacheContext, destCacheContext, 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 CacheAddress(CacheAddressType.Data, (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
                destCacheContext.Serializer.Serialize(resourceContext, definition);

                // Now inject the new resource data
                var newLocation = FixResourceLocation(location, srcCacheContext.Version, destCacheContext.Version);

                outStream.Position = 0;
                geometry.Resource.HaloOnlinePageableResource.ChangeLocation(newLocation);
                destCacheContext.AddResource(geometry.Resource.HaloOnlinePageableResource, outStream);
            }

            return(geometry);
        }
Beispiel #18
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 0)
            {
                return(false);
            }

            var soundDataAggregate = new byte[0].AsEnumerable();

            int currentFileOffset         = 0;
            int totalSampleCount          = 0;
            int maxPermutationSampleCount = 0;


            int pitchRangeCount = GetPitchRangeCountUser();

            if (pitchRangeCount <= 0)
            {
                return(false);
            }

            //
            // Get basic information on the sounds
            //

            if (pitchRangeCount > 1)
            {
                Definition.ImportType = ImportType.MultiLayer;
            }
            else
            {
                Definition.ImportType = ImportType.SingleLayer;
            }

            Definition.SampleRate.value = GetSoundSampleRateUser();

            Definition.PlatformCodec.Compression = GetSoundCompressionUser();

            Definition.PlatformCodec.Encoding = GetSoundEncodingUser();

            Definition.PitchRanges = new List <PitchRange>();

            //
            // For each pitch range, get all the permutations and append sound data.
            //

            for (int u = 0; u < pitchRangeCount; u++)
            {
                int permutationCount = GetPermutationCountUser();

                if (permutationCount <= 0)
                {
                    return(false);
                }

                var pitchRange = new PitchRange
                {
                    ImportName           = new StringId(5221), //|default|
                    Unknown5             = -1,
                    Unknown6             = -1,
                    Unknown7             = -1,
                    Unknown8             = -1,
                    PermutationCount     = (short)permutationCount,
                    PitchRangeParameters = new PitchRangeParameter()
                };
                pitchRange.PitchRangeParameters.UnknownBounds = new Bounds <short>(-32768, 32767);

                pitchRange.Permutations = new List <Permutation>();

                //
                // Permutation section
                //

                for (int i = 0; i < permutationCount; i++)
                {
                    string soundFile = GetPermutationFileUser(i);
                    if (soundFile == null)
                    {
                        return(false);
                    }

                    int sampleCount = GetPermutationSampleCountUser(i);

                    var perm = new Permutation
                    {
                        ImportName = StringId.Invalid,
                        SampleSize = (uint)sampleCount
                    };

                    if (i != 0)
                    {
                        perm.IsNotFirstPermutation = 1;
                    }

                    perm.PermutationNumber = (uint)i;

                    var permutationData = File.ReadAllBytes(soundFile);

                    perm.PermutationChunks = new List <PermutationChunk>();

                    var chunk = new PermutationChunk(currentFileOffset, permutationData.Length);
                    perm.PermutationChunks.Add(chunk);
                    currentFileOffset += permutationData.Length;
                    totalSampleCount  += sampleCount;

                    if (maxPermutationSampleCount < sampleCount)
                    {
                        maxPermutationSampleCount = sampleCount;
                    }

                    soundDataAggregate = soundDataAggregate.Concat(permutationData);

                    pitchRange.Permutations.Add(perm);
                }

                Definition.PitchRanges.Add(pitchRange);
            }

            Definition.Promotion.LongestPermutationDuration = (uint)(1000 * (double)maxPermutationSampleCount / (Definition.SampleRate.GetSampleRateHz()));
            Definition.Promotion.TotalSampleSize            = (uint)totalSampleCount;


            // remove extra info for now

            Definition.ExtraInfo = new List <ExtraInfo>();

            //
            // Create new resource
            //

            Console.Write("Creating new sound resource...");

            var data = soundDataAggregate.ToArray();

            Definition.Unknown12 = 0;

            using (var dataStream = new MemoryStream(data))
            {
                var fileSize        = (int)dataStream.Length;
                var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Resource);
                CacheContext.Serializer.Serialize(resourceContext,
                                                  new SoundResourceDefinition
                {
                    Data = new TagData(fileSize, new CacheResourceAddress(CacheResourceAddressType.Resource, 0))
                });

                Definition.Resource = new PageableResource
                {
                    Page = new RawPage
                    {
                        Index = -1
                    },
                    Resource = new TagResourceGen3
                    {
                        ResourceType      = TagResourceTypeGen3.Sound,
                        DefinitionData    = new byte[20],
                        DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 536870912),
                        ResourceFixups    = new List <TagResourceGen3.ResourceFixup>
                        {
                            new TagResourceGen3.ResourceFixup
                            {
                                BlockOffset = 12,
                                Address     = new CacheResourceAddress(CacheResourceAddressType.Resource, 1073741824)
                            }
                        },
                        ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                        Unknown2 = 1
                    }
                };

                Definition.Resource.ChangeLocation(ResourceLocation.ResourcesB);
                CacheContext.AddResource(Definition.Resource, dataStream);

                for (int i = 0; i < 4; i++)
                {
                    Definition.Resource.Resource.DefinitionData[i] = (byte)(Definition.Resource.Page.UncompressedBlockSize >> (i * 8));
                }

                Console.WriteLine("done.");
            }
            return(true);
        }
Beispiel #19
0
        public override object Execute(List <string> args)
        {
            string variantName;
            string fileType;
            string modelFileName;

            if (args.Count == 2)
            {
                variantName   = "*";
                fileType      = args[0].ToLower();
                modelFileName = args[1];
            }
            else if (args.Count == 3)
            {
                variantName   = args[0];
                fileType      = args[1].ToLower();
                modelFileName = args[2];
            }
            else
            {
                return(false);
            }

            switch (fileType)
            {
            case "obj":
            case "dae":
                break;

            default:
                throw new NotImplementedException(fileType);
            }

            if (Definition.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(CacheContext, Definition.Geometry.Resource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                //
                // Extract the resource data
                //

                CacheContext.ExtractResource(Definition.Geometry.Resource, resourceStream);

                var modelFile = new FileInfo(modelFileName);

                if (!modelFile.Directory.Exists)
                {
                    modelFile.Directory.Create();
                }

                ModelExtractor extractor = new ModelExtractor(CacheContext, Definition);
                extractor.ExtractRenderModel(variantName);

                switch (fileType)
                {
                case "obj":
                    return(extractor.ExportObject(modelFile));

                case "dae":
                    return(extractor.ExportCollada(modelFile));

                default:
                    throw new NotImplementedException(fileType);
                }
            }
        }
Beispiel #20
0
        public override object Execute(List <string> args)
        {
            if (Definition.PathfindingResource == null)
            {
                Console.WriteLine("ERROR: Pathfinding geometry does not have a resource associated with it.");
                return(true);
            }

            var resourceContext    = new ResourceSerializationContext(CacheContext, Definition.PathfindingResource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <StructureBspCacheFileTagResources>(resourceContext);

            using (var resourceStream = new MemoryStream())
                using (var reader = new EndianReader(resourceStream))
                    using (var writer = new EndianWriter(resourceStream))
                    {
                        CacheContext.ExtractResource(Definition.PathfindingResource, resourceStream);
                        var dataContext = new DataSerializationContext(reader, writer);

                        foreach (var pathfindingDatum in resourceDefinition.PathfindingData)
                        {
                            resourceStream.Position = pathfindingDatum.Sectors.Address.Offset;

                            for (var i = 0; i < pathfindingDatum.Sectors.Count; i++)
                            {
                                pathfindingDatum.Sectors.Add(
                                    CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Sector>(dataContext));
                            }

                            resourceStream.Position = pathfindingDatum.Links.Address.Offset;

                            for (var i = 0; i < pathfindingDatum.Links.Count; i++)
                            {
                                pathfindingDatum.Links.Add(
                                    CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Link>(dataContext));
                            }

                            resourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset;

                            for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++)
                            {
                                pathfindingDatum.PathfindingHints.Add(
                                    CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>(dataContext));
                            }

                            resourceStream.Position = pathfindingDatum.Vertices.Address.Offset;

                            for (var i = 0; i < pathfindingDatum.Vertices.Count; i++)
                            {
                                pathfindingDatum.Vertices.Add(
                                    CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Vertex>(dataContext));
                            }

                            for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++)
                            {
                                var hint = pathfindingDatum.PathfindingHints[i];

                                if (hint.HintType != JumpLink && hint.HintType != WallJumpLink)
                                {
                                    continue;
                                }

                                var hintverts = new List <short>();
                                var success   = false;

                                hintverts.Add((short)(hint.Data[1] & ushort.MaxValue));
                                hintverts.Add((short)((hint.Data[1] >> 16) & ushort.MaxValue));

                                if (hintverts[0] == -1 || hintverts[1] == -1)
                                {
                                    continue;
                                }

                                float hint_x = (pathfindingDatum.Vertices[hintverts[0]].Position.X + pathfindingDatum.Vertices[hintverts[1]].Position.X) / 2.0f;
                                float hint_y = (pathfindingDatum.Vertices[hintverts[0]].Position.Y + pathfindingDatum.Vertices[hintverts[1]].Position.Y) / 2.0f;
                                float hint_z = (pathfindingDatum.Vertices[hintverts[0]].Position.Z + pathfindingDatum.Vertices[hintverts[1]].Position.Z) / 2.0f;

                                var sectorlist       = new List <int>();
                                var backupsectorlist = new List <int>();

                                var zavelist       = new List <float>();
                                var backupzavelist = new List <float>();

                                for (var s = 0; s < pathfindingDatum.Sectors.Count; s++)
                                {
                                    var sector   = pathfindingDatum.Sectors[s];
                                    var vertices = new HashSet <short>();
                                    if (sector.FirstLink == -1)
                                    {
                                        continue;
                                    }
                                    var link = pathfindingDatum.Links[sector.FirstLink];

                                    while (true)
                                    {
                                        if (link.LeftSector == s)
                                        {
                                            vertices.Add(link.Vertex1);
                                            vertices.Add(link.Vertex2);
                                            if (link.ForwardLink == sector.FirstLink)
                                            {
                                                break;
                                            }
                                            else
                                            {
                                                link = pathfindingDatum.Links[link.ForwardLink];
                                            }
                                        }
                                        else if (link.RightSector == s)
                                        {
                                            vertices.Add(link.Vertex1);
                                            vertices.Add(link.Vertex2);
                                            if (link.ReverseLink == sector.FirstLink)
                                            {
                                                break;
                                            }
                                            else
                                            {
                                                link = pathfindingDatum.Links[link.ReverseLink];
                                            }
                                        }
                                    }

                                    var points = new List <RealPoint3d>();
                                    var xlist  = new List <float>();
                                    var ylist  = new List <float>();
                                    var zlist  = new List <float>();

                                    foreach (var vert in vertices)
                                    {
                                        points.Add(pathfindingDatum.Vertices[vert].Position);
                                        xlist.Add(pathfindingDatum.Vertices[vert].Position.X);
                                        ylist.Add(pathfindingDatum.Vertices[vert].Position.Y);
                                        zlist.Add(pathfindingDatum.Vertices[vert].Position.Z);
                                    }

                                    float xmin = xlist.Min();
                                    float xmax = xlist.Max();
                                    float ymin = ylist.Min();
                                    float ymax = ylist.Max();
                                    float zmin = zlist.Min();
                                    float zmax = zlist.Max();
                                    float zave = zlist.Average();

                                    bool pnpoly(List <RealPoint3d> polygon, RealPoint3d testPoint)
                                    {
                                        var result = false;

                                        for (int p = 0, j = polygon.Count - 1; p < polygon.Count(); j = p++)
                                        {
                                            if (polygon[p].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[p].Y >= testPoint.Y)
                                            {
                                                if (polygon[p].X + (testPoint.Y - polygon[p].Y) / (polygon[j].Y - polygon[p].Y) * (polygon[j].X - polygon[p].X) < testPoint.X)
                                                {
                                                    result = !result;
                                                }
                                            }
                                        }

                                        // TODO: maybe check Z?

                                        return(result);
                                    }

                                    if (pnpoly(points, new RealPoint3d(hint_x, hint_y, hint_z)))
                                    {
                                        sectorlist.Add(s);
                                        zavelist.Add(Math.Abs(hint_z - zave));
                                    }
                                    else if (xmin < hint_x && xmax > hint_x && ymin < hint_y && ymax > hint_y)
                                    {
                                        backupsectorlist.Add(s);
                                        backupzavelist.Add(Math.Abs(hint_z - zave));
                                    }
                                }

                                if (sectorlist.Count > 0)
                                {
                                    var s      = sectorlist[zavelist.IndexOf(zavelist.Min())];
                                    var hiword = (short)(hint.Data[3] >> 16);
                                    hint.Data[3] = hiword << 16 | s;
                                    success      = true;
                                }
                                else if (backupsectorlist.Count > 0)
                                {
                                    var s      = backupsectorlist[backupzavelist.IndexOf(backupzavelist.Min())];
                                    var hiword = (short)(hint.Data[3] >> 16);
                                    hint.Data[3] = hiword << 16 | s;
                                    success      = true;
                                }

                                if (!success)
                                {
                                    Console.WriteLine($"Pathfinding Jump Hint {i} sector not found!");
                                }
                            }

                            resourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset;

                            for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++)
                            {
                                CacheContext.Serializer.Serialize(dataContext, pathfindingDatum.PathfindingHints[i]);
                            }

                            resourceStream.Position = 0;
                            CacheContext.ReplaceResource(Definition.PathfindingResource, resourceStream);
                        }
                    }

            return(true);
        }
Beispiel #21
0
        public override object Execute(List <string> args)
        {
            // Deserialize the definition data
            var resourceContext = new ResourceSerializationContext(CacheContext, BSP.CollisionBspResource);
            var definition      = CacheContext.Deserializer.Deserialize <StructureBspTagResources>(resourceContext);

            // Extract the resource data
            var resourceDataStream = new MemoryStream();

            CacheContext.ExtractResource(BSP.CollisionBspResource, resourceDataStream);

            using (var reader = new EndianReader(resourceDataStream))
            {
                #region collision bsps

                foreach (var cbsp in definition.CollisionBsps)
                {
                    reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                        cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                    }

                    reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                    for (var i = 0; i < cbsp.Planes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                        cbsp.Planes.Add((CollisionGeometry.Plane)element);
                    }

                    reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                    for (var i = 0; i < cbsp.Leaves.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                        cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                    }

                    reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                        cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                    }

                    reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                        cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                    }

                    reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                    for (var i = 0; i < cbsp.Surfaces.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                        cbsp.Surfaces.Add((CollisionGeometry.Surface)element);
                    }

                    reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                    for (var i = 0; i < cbsp.Edges.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                        cbsp.Edges.Add((CollisionGeometry.Edge)element);
                    }

                    reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                    for (var i = 0; i < cbsp.Vertices.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                        cbsp.Vertices.Add((CollisionGeometry.Vertex)element);
                    }
                }

                #endregion

                #region large collision bsps

                foreach (var cbsp in definition.LargeCollisionBsps)
                {
                    reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode));
                        cbsp.Bsp3dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)element);
                    }

                    reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                    for (var i = 0; i < cbsp.Planes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                        cbsp.Planes.Add((CollisionGeometry.Plane)element);
                    }

                    reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                    for (var i = 0; i < cbsp.Leaves.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                        cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                    }

                    reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference));
                        cbsp.Bsp2dReferences.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)element);
                    }

                    reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                    for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode));
                        cbsp.Bsp2dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)element);
                    }

                    reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                    for (var i = 0; i < cbsp.Surfaces.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Surface));
                        cbsp.Surfaces.Add((StructureBspTagResources.LargeCollisionBspBlock.Surface)element);
                    }

                    reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                    for (var i = 0; i < cbsp.Edges.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Edge));
                        cbsp.Edges.Add((StructureBspTagResources.LargeCollisionBspBlock.Edge)element);
                    }

                    reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                    for (var i = 0; i < cbsp.Vertices.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Vertex));
                        cbsp.Vertices.Add((StructureBspTagResources.LargeCollisionBspBlock.Vertex)element);
                    }
                }

                #endregion

                #region compressions

                foreach (var instance in definition.InstancedGeometry)
                {
                    #region compression's resource data

                    reader.BaseStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                        instance.CollisionInfo.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Planes.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                        instance.CollisionInfo.Planes.Add((CollisionGeometry.Plane)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Leaves.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                        instance.CollisionInfo.Leaves.Add((CollisionGeometry.Leaf)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                        instance.CollisionInfo.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                        instance.CollisionInfo.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Surfaces.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                        instance.CollisionInfo.Surfaces.Add((CollisionGeometry.Surface)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Edges.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                        instance.CollisionInfo.Edges.Add((CollisionGeometry.Edge)element);
                    }

                    reader.BaseStream.Position = instance.CollisionInfo.Vertices.Address.Offset;
                    for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                        instance.CollisionInfo.Vertices.Add((CollisionGeometry.Vertex)element);
                    }

                    #endregion

                    #region compression's other resource data

                    foreach (var cbsp in instance.CollisionGeometries)
                    {
                        reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                            cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                        for (var i = 0; i < cbsp.Planes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                            cbsp.Planes.Add((CollisionGeometry.Plane)element);
                        }

                        reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                        for (var i = 0; i < cbsp.Leaves.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                            cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                            cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                            cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                        for (var i = 0; i < cbsp.Surfaces.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                            cbsp.Surfaces.Add((CollisionGeometry.Surface)element);
                        }

                        reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                        for (var i = 0; i < cbsp.Edges.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                            cbsp.Edges.Add((CollisionGeometry.Edge)element);
                        }

                        reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                        for (var i = 0; i < cbsp.Vertices.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                            cbsp.Vertices.Add((CollisionGeometry.Vertex)element);
                        }
                    }

                    #endregion

                    #region Unknown Data

                    for (var i = 0; i < instance.Unknown1.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown1Block));
                        instance.Unknown1.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)element);
                    }

                    for (var i = 0; i < instance.Unknown2.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown2Block));
                        instance.Unknown2.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)element);
                    }

                    for (var i = 0; i < instance.Unknown3.Count; i++)
                    {
                        var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown3Block));
                        instance.Unknown3.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)element);
                    }

                    #endregion

                    #region compression's havok collision data

                    foreach (var collision in instance.BspPhysics)
                    {
                        for (var i = 0; i < collision.Data.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(byte));
                            collision.Data.Add(new StructureBspTagResources.CollisionBspPhysicsBlock.Datum {
                                Value = (byte)element
                            });
                        }
                    }

                    #endregion
                }

                #endregion
            }

            return(true);
        }
        public override object Execute(List <string> args)
        {
            if (args.Count != 2)
            {
                return(false);
            }

            var fileType = args[0];
            var fileName = args[1];

            if (fileType != "obj")
            {
                throw new NotSupportedException(fileType);
            }

            if (Definition.Geometry2.Resource == null)
            {
                Console.WriteLine("ERROR: Render geometry does not have a resource associated with it.");
                return(true);
            }

            //
            // Deserialize the resource definition
            //

            var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Geometry2.Resource);
            var definition      = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                //
                // Extract the resource data
                //

                CacheContext.ExtractResource(Definition.Geometry2.Resource, resourceStream);

                var file = new FileInfo(fileName);

                if (!file.Directory.Exists)
                {
                    file.Directory.Create();
                }

                using (var objFile = new StreamWriter(file.Create()))
                {
                    var objExtractor = new ObjExtractor(objFile);

                    foreach (var cluster in Definition.Clusters)
                    {
                        var meshReader = new MeshReader(CacheContext.Version, Definition.Geometry2.Meshes[cluster.MeshIndex], definition);
                        objExtractor.ExtractMesh(meshReader, null, resourceStream);
                    }

                    foreach (var instance in Definition.InstancedGeometryInstances)
                    {
                        var vertexCompressor = new VertexCompressor(Definition.Geometry2.Compression[0]);
                        var meshReader       = new MeshReader(CacheContext.Version, Definition.Geometry2.Meshes[instance.InstanceDefinition], definition);
                        objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream);
                    }

                    objExtractor.Finish();
                }
            }

            Console.WriteLine("Done!");

            return(true);
        }
Beispiel #23
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

            var resourceFile = new FileInfo(args[0]);
            var fileSize     = 0;

            if (!resourceFile.Exists)
            {
                Console.WriteLine($"ERROR: File not found: \"{resourceFile.FullName}\"");
                return(true);
            }

            //
            // Create new resource
            //

            Console.Write("Creating new sound resource...");

            Definition.Unknown12 = 0;

            using (var dataStream = resourceFile.OpenRead())
            {
                fileSize = (int)dataStream.Length;
                var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Resource);
                CacheContext.Serializer.Serialize(resourceContext,
                                                  new SoundResourceDefinition
                {
                    Data = new TagData(fileSize, new CacheAddress(CacheAddressType.Resource, 0))
                });

                Definition.Resource = new PageableResource
                {
                    Page = new RawPage
                    {
                        Index = -1
                    },
                    Resource = new TagResourceGen3
                    {
                        ResourceType      = TagResourceTypeGen3.Sound,
                        DefinitionData    = new byte[20],
                        DefinitionAddress = new CacheAddress(CacheAddressType.Definition, 536870912),
                        ResourceFixups    = new List <TagResourceGen3.ResourceFixup>
                        {
                            new TagResourceGen3.ResourceFixup
                            {
                                BlockOffset = 12,
                                Address     = new CacheAddress(CacheAddressType.Resource, 1073741824)
                            }
                        },
                        ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                        Unknown2 = 1
                    }
                };

                Definition.Resource.ChangeLocation(ResourceLocation.ResourcesB);
                CacheContext.AddResource(Definition.Resource, dataStream);

                for (int i = 0; i < 4; i++)
                {
                    Definition.Resource.Resource.DefinitionData[i] = (byte)(Definition.Resource.Page.UncompressedBlockSize >> (i * 8));
                }

                Console.WriteLine("done.");
            }

            //
            // Adjust tag definition to use correctly the sound file.
            //

            var chunkSize = (ushort)fileSize;

            var permutationChunk = new PermutationChunk(0, chunkSize);

            var permutation = Definition.PitchRanges[0].Permutations[0];

            permutation.PermutationChunks = new List <PermutationChunk>
            {
                permutationChunk
            };

            permutation.PermutationNumber     = 0;
            permutation.SampleSize            = 0;
            permutation.IsNotFirstPermutation = 0;

            Definition.PitchRanges[0].Permutations = new List <Permutation>
            {
                permutation
            };

            Definition.PlatformCodec.Compression = Compression.MP3;

            return(true);
        }
Beispiel #24
0
        public override object Execute(List <string> args)
        {
            if (args.Count != 1)
            {
                return(false);
            }

            if (Definition.CollisionBspResource == null)
            {
                Console.WriteLine("ERROR: Collision geometry does not have a resource associated with it.");
                return(true);
            }

            var resourceContext    = new ResourceSerializationContext(CacheContext, Definition.CollisionBspResource);
            var resourceDefinition = CacheContext.Deserializer.Deserialize <StructureBspTagResources>(resourceContext);

            using (var resourceStream = new MemoryStream())
            {
                CacheContext.ExtractResource(Definition.CollisionBspResource, resourceStream);

                using (var reader = new EndianReader(resourceStream))
                {
                    foreach (var cbsp in resourceDefinition.CollisionBsps)
                    {
                        reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                            cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                        for (var i = 0; i < cbsp.Planes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                            cbsp.Planes.Add((CollisionGeometry.Plane)element);
                        }

                        reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                        for (var i = 0; i < cbsp.Leaves.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                            cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                            cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                            cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                        for (var i = 0; i < cbsp.Surfaces.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                            cbsp.Surfaces.Add((CollisionGeometry.Surface)element);
                        }

                        reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                        for (var i = 0; i < cbsp.Edges.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                            cbsp.Edges.Add((CollisionGeometry.Edge)element);
                        }

                        reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                        for (var i = 0; i < cbsp.Vertices.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                            cbsp.Vertices.Add((CollisionGeometry.Vertex)element);
                        }
                    }

                    foreach (var cbsp in resourceDefinition.LargeCollisionBsps)
                    {
                        reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode));
                            cbsp.Bsp3dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                        for (var i = 0; i < cbsp.Planes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                            cbsp.Planes.Add((CollisionGeometry.Plane)element);
                        }

                        reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                        for (var i = 0; i < cbsp.Leaves.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                            cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference));
                            cbsp.Bsp2dReferences.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)element);
                        }

                        reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                        for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode));
                            cbsp.Bsp2dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)element);
                        }

                        reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                        for (var i = 0; i < cbsp.Surfaces.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Surface));
                            cbsp.Surfaces.Add((StructureBspTagResources.LargeCollisionBspBlock.Surface)element);
                        }

                        reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                        for (var i = 0; i < cbsp.Edges.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Edge));
                            cbsp.Edges.Add((StructureBspTagResources.LargeCollisionBspBlock.Edge)element);
                        }

                        reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                        for (var i = 0; i < cbsp.Vertices.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Vertex));
                            cbsp.Vertices.Add((StructureBspTagResources.LargeCollisionBspBlock.Vertex)element);
                        }
                    }

                    foreach (var instance in resourceDefinition.InstancedGeometry)
                    {
                        reader.BaseStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                            instance.CollisionInfo.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Planes.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                            instance.CollisionInfo.Planes.Add((CollisionGeometry.Plane)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Leaves.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                            instance.CollisionInfo.Leaves.Add((CollisionGeometry.Leaf)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                            instance.CollisionInfo.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                            instance.CollisionInfo.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Surfaces.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                            instance.CollisionInfo.Surfaces.Add((CollisionGeometry.Surface)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Edges.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                            instance.CollisionInfo.Edges.Add((CollisionGeometry.Edge)element);
                        }

                        reader.BaseStream.Position = instance.CollisionInfo.Vertices.Address.Offset;
                        for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                            instance.CollisionInfo.Vertices.Add((CollisionGeometry.Vertex)element);
                        }

                        foreach (var cbsp in instance.CollisionGeometries)
                        {
                            reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset;
                            for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode));
                                cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element);
                            }

                            reader.BaseStream.Position = cbsp.Planes.Address.Offset;
                            for (var i = 0; i < cbsp.Planes.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane));
                                cbsp.Planes.Add((CollisionGeometry.Plane)element);
                            }

                            reader.BaseStream.Position = cbsp.Leaves.Address.Offset;
                            for (var i = 0; i < cbsp.Leaves.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf));
                                cbsp.Leaves.Add((CollisionGeometry.Leaf)element);
                            }

                            reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset;
                            for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference));
                                cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element);
                            }

                            reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset;
                            for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode));
                                cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element);
                            }

                            reader.BaseStream.Position = cbsp.Surfaces.Address.Offset;
                            for (var i = 0; i < cbsp.Surfaces.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface));
                                cbsp.Surfaces.Add((CollisionGeometry.Surface)element);
                            }

                            reader.BaseStream.Position = cbsp.Edges.Address.Offset;
                            for (var i = 0; i < cbsp.Edges.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge));
                                cbsp.Edges.Add((CollisionGeometry.Edge)element);
                            }

                            reader.BaseStream.Position = cbsp.Vertices.Address.Offset;
                            for (var i = 0; i < cbsp.Vertices.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex));
                                cbsp.Vertices.Add((CollisionGeometry.Vertex)element);
                            }
                        }

                        for (var i = 0; i < instance.Unknown1.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown1Block));
                            instance.Unknown1.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)element);
                        }

                        for (var i = 0; i < instance.Unknown2.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown2Block));
                            instance.Unknown2.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)element);
                        }

                        for (var i = 0; i < instance.Unknown3.Count; i++)
                        {
                            var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown3Block));
                            instance.Unknown3.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)element);
                        }

                        foreach (var collision in instance.BspPhysics)
                        {
                            for (var i = 0; i < collision.Data.Count; i++)
                            {
                                var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(byte));
                                collision.Data.Add(new StructureBspTagResources.CollisionBspPhysicsBlock.Datum {
                                    Value = (byte)element
                                });
                            }
                        }
                    }
                }

                var file = new FileInfo(args[0]);

                if (!file.Directory.Exists)
                {
                    file.Directory.Create();
                }

                using (var writer = new StreamWriter(file.Create()))
                {
                    var baseVertex = 0;

                    foreach (var bsp in resourceDefinition.CollisionBsps)
                    {
                        for (var i = 0; i < bsp.Vertices.Count; i++)
                        {
                            var vertex = bsp.Vertices[i];
                            writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}");
                        }

                        writer.WriteLine($"g bsp_surfaces_{resourceDefinition.CollisionBsps.IndexOf(bsp)}");

                        for (var i = 0; i < bsp.Surfaces.Count; i++)
                        {
                            var surface  = bsp.Surfaces[i];
                            var vertices = new HashSet <short>();
                            var edge     = bsp.Edges[surface.FirstEdge];

                            writer.Write("f");

                            while (true)
                            {
                                if (edge.LeftSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.StartVertex + 1}");

                                    if (edge.ForwardEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = bsp.Edges[edge.ForwardEdge];
                                    }
                                }
                                else if (edge.RightSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.EndVertex + 1}");

                                    if (edge.ReverseEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = bsp.Edges[edge.ReverseEdge];
                                    }
                                }
                            }

                            writer.WriteLine();
                        }

                        baseVertex += bsp.Vertices.Count;
                    }

                    foreach (var largeBsp in resourceDefinition.LargeCollisionBsps)
                    {
                        for (var i = 0; i < largeBsp.Vertices.Count; i++)
                        {
                            var vertex = largeBsp.Vertices[i];
                            writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}");
                        }

                        writer.WriteLine($"g large_bsp_surfaces_{resourceDefinition.LargeCollisionBsps.IndexOf(largeBsp)}");

                        for (var i = 0; i < largeBsp.Surfaces.Count; i++)
                        {
                            var surface  = largeBsp.Surfaces[i];
                            var vertices = new HashSet <short>();
                            var edge     = largeBsp.Edges[surface.FirstEdge];

                            writer.Write("f");

                            while (true)
                            {
                                if (edge.LeftSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.StartVertex + 1}");

                                    if (edge.ForwardEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = largeBsp.Edges[edge.ForwardEdge];
                                    }
                                }
                                else if (edge.RightSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.EndVertex + 1}");

                                    if (edge.ReverseEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = largeBsp.Edges[edge.ReverseEdge];
                                    }
                                }
                            }

                            writer.WriteLine();
                        }

                        baseVertex += largeBsp.Vertices.Count;
                    }

                    foreach (var instanceDef in Definition.InstancedGeometryInstances)
                    {
                        if (instanceDef.InstanceDefinition == -1)
                        {
                            continue;
                        }

                        var instance = resourceDefinition.InstancedGeometry[instanceDef.InstanceDefinition];

                        var instanceName = instanceDef.Name != StringId.Invalid ?
                                           CacheContext.GetString(instanceDef.Name) :
                                           $"instance_{instanceDef.InstanceDefinition}";

                        for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++)
                        {
                            var vertex = instance.CollisionInfo.Vertices[i];
                            var point  = Vector3.Transform(
                                new Vector3(vertex.Point.X, vertex.Point.Y, vertex.Point.Z),
                                new Matrix4x4(
                                    instanceDef.Matrix.m11, instanceDef.Matrix.m12, instanceDef.Matrix.m13, 0.0f,
                                    instanceDef.Matrix.m21, instanceDef.Matrix.m22, instanceDef.Matrix.m23, 0.0f,
                                    instanceDef.Matrix.m31, instanceDef.Matrix.m32, instanceDef.Matrix.m33, 0.0f,
                                    instanceDef.Matrix.m41, instanceDef.Matrix.m42, instanceDef.Matrix.m43, 0.0f));

                            writer.WriteLine($"v {point.X} {point.Z} {point.Y}");
                        }

                        writer.WriteLine($"g {instanceName}_main_surfaces");

                        for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++)
                        {
                            var surface  = instance.CollisionInfo.Surfaces[i];
                            var vertices = new HashSet <short>();
                            var edge     = instance.CollisionInfo.Edges[surface.FirstEdge];

                            writer.Write("f");

                            while (true)
                            {
                                if (edge.LeftSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.StartVertex + 1}");

                                    if (edge.ForwardEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = instance.CollisionInfo.Edges[edge.ForwardEdge];
                                    }
                                }
                                else if (edge.RightSurface == i)
                                {
                                    writer.Write($" {baseVertex + edge.EndVertex + 1}");

                                    if (edge.ReverseEdge == surface.FirstEdge)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        edge = instance.CollisionInfo.Edges[edge.ReverseEdge];
                                    }
                                }
                            }

                            writer.WriteLine();
                        }

                        baseVertex += instance.CollisionInfo.Vertices.Count;

                        foreach (var bsp in instance.CollisionGeometries)
                        {
                            for (var i = 0; i < bsp.Vertices.Count; i++)
                            {
                                var vertex = bsp.Vertices[i];
                                writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}");
                            }

                            writer.WriteLine($"g {instanceName}_bsp_surfaces_{resourceDefinition.CollisionBsps.IndexOf(bsp)}");

                            for (var i = 0; i < bsp.Surfaces.Count; i++)
                            {
                                var surface  = bsp.Surfaces[i];
                                var vertices = new HashSet <short>();
                                var edge     = bsp.Edges[surface.FirstEdge];

                                writer.Write("f");

                                while (true)
                                {
                                    if (edge.LeftSurface == i)
                                    {
                                        writer.Write($" {baseVertex + edge.StartVertex + 1}");

                                        if (edge.ForwardEdge == surface.FirstEdge)
                                        {
                                            break;
                                        }
                                        else
                                        {
                                            edge = bsp.Edges[edge.ForwardEdge];
                                        }
                                    }
                                    else if (edge.RightSurface == i)
                                    {
                                        writer.Write($" {baseVertex + edge.EndVertex + 1}");

                                        if (edge.ReverseEdge == surface.FirstEdge)
                                        {
                                            break;
                                        }
                                        else
                                        {
                                            edge = bsp.Edges[edge.ReverseEdge];
                                        }
                                    }
                                }

                                writer.WriteLine();
                            }

                            baseVertex += bsp.Vertices.Count;
                        }
                    }
                }
            }

            return(true);
        }
Beispiel #25
0
        public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream, ResourceLocation location = ResourceLocation.Textures)
        {
            var resource    = bitmap.Resources[imageIndex].Resource;
            var newResource = (resource == null);
            ResourceSerializationContext resourceContext;
            BitmapTextureInteropResource definition;

            if (newResource)
            {
                // Create a new resource reference
                resource = new PageableResource
                {
                    Page     = new RawPage(),
                    Resource = new TagResourceGen3
                    {
                        ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                        ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                        ResourceType             = TagResourceTypeGen3.Bitmap,
                        Unknown2 = 1
                    }
                };

                bitmap.Resources[imageIndex].Resource = resource;
                resourceContext = new ResourceSerializationContext(CacheContext, resource);
                definition      = new BitmapTextureInteropResource
                {
                    Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition>
                    {
                        Definition = new BitmapTextureInteropResource.BitmapDefinition
                        {
                            Data        = new TagData(),
                            UnknownData = new TagData(),
                        }
                    }
                };
            }
            else
            {
                // Deserialize the old definition
                resourceContext = new ResourceSerializationContext(CacheContext, resource);
                definition      = deserializer.Deserialize <BitmapTextureInteropResource>(resourceContext);
            }
            if (definition.Texture == null || definition.Texture.Definition == null)
            {
                throw new ArgumentException("Invalid bitmap definition");
            }
            var texture   = definition.Texture.Definition;
            var imageData = bitmap.Images[imageIndex];

            // Read the DDS header and modify the definition to match
            var dds      = DdsHeader.Read(ddsStream);
            var dataSize = (int)(ddsStream.Length - ddsStream.Position);

            texture.Data        = new TagData(dataSize, new CacheResourceAddress(CacheResourceAddressType.Resource, 0));
            texture.Width       = (short)dds.Width;
            texture.Height      = (short)dds.Height;
            texture.Depth       = (sbyte)Math.Max(1, dds.Depth);
            texture.MipmapCount = (sbyte)Math.Max(1, dds.MipMapCount);
            texture.Type        = BitmapDdsFormatDetection.DetectType(dds);
            texture.D3DFormat   = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2"));
            texture.Format      = BitmapDdsFormatDetection.DetectFormat(dds);

            // Set flags based on the format
            switch (texture.Format)
            {
            case BitmapFormat.Dxt1:
            case BitmapFormat.Dxt3:
            case BitmapFormat.Dxt5:
            case BitmapFormat.Dxn:
                texture.Flags = BitmapFlags.Compressed;
                break;

            default:
                texture.Flags = BitmapFlags.None;
                break;
            }
            if ((texture.Width & (texture.Width - 1)) == 0 && (texture.Height & (texture.Height - 1)) == 0)
            {
                texture.Flags |= BitmapFlags.PowerOfTwoDimensions;
            }

            // If creating a new image, then add a new resource, otherwise replace the existing one
            if (newResource)
            {
                resource.ChangeLocation(location);
                CacheContext.AddResource(resource, ddsStream);
            }
            else
            {
                CacheContext.ReplaceResource(resource, ddsStream);
            }

            // Serialize the new resource definition
            serializer.Serialize(resourceContext, definition);

            // Modify the image data in the bitmap tag to match the definition
            imageData.Width       = texture.Width;
            imageData.Height      = texture.Height;
            imageData.Depth       = texture.Depth;
            imageData.Type        = texture.Type;
            imageData.Format      = texture.Format;
            imageData.Flags       = texture.Flags;
            imageData.MipmapCount = (sbyte)(texture.MipmapCount - 1);
            imageData.DataOffset  = texture.Data.Address.Offset;
            imageData.DataSize    = texture.Data.Size;
            imageData.Curve       = (BitmapImageCurve)texture.Curve;
        }
        public override object Execute(List <string> args)
        {
            if (args.Count < 2)
            {
                return(false);
            }

            //
            // Verify Blam tag instance
            //

            var unitName = args[0].ToLower();

            if (unitName != "spartan" && unitName != "elite")
            {
                Console.WriteLine("ERROR: Only 'spartan' and 'elite' armor variants are allowed.");
                return(false);
            }

            args.RemoveAt(0);

            var blamTagName = unitName == "spartan" ?
                              @"objects\characters\masterchief\mp_masterchief\mp_masterchief" :
                              @"objects\characters\elite\mp_elite\mp_elite";

            Console.Write($"Verifying {blamTagName}.render_model...");

            CacheFile.IndexItem blamTag = null;

            foreach (var tag in BlamCache.IndexItems)
            {
                if ((tag.GroupTag == "mode") && (tag.Name == blamTagName))
                {
                    blamTag = tag;
                    break;
                }
            }

            if (blamTag == null)
            {
                Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.render_model");
                return(true);
            }

            Console.WriteLine("done.");

            //
            // Load the Blam tag definition
            //

            var variantName = args[0];

            args.RemoveAt(0);

            CachedTagInstance edModeTag = null;
            var isScenery   = false;
            var regionNames = new List <string>();

            while (args.Count != 0)
            {
                switch (args[0].ToLower())
                {
                case "scenery":
                    isScenery = true;
                    args.RemoveAt(0);
                    break;

                case "replace:":
                    edModeTag = CacheContext.GetTag(args[1]);
                    args.RemoveAt(1);
                    args.RemoveAt(0);
                    break;

                case "regions:":
                    regionNames.AddRange(args.Skip(1));
                    args.Clear();
                    break;

                default:
                    throw new InvalidDataException($"{args}");
                }
            }

            var blamContext      = new CacheSerializationContext(ref BlamCache, blamTag);
            var edModeDefinition = BlamCache.Deserializer.Deserialize <RenderModel>(blamContext);

            var materials = edModeDefinition.Materials.Select(i => new RenderMaterial
            {
                BreakableSurfaceIndex = i.BreakableSurfaceIndex,
                Properties            = i.Properties,
                RenderMethod          = i.RenderMethod,
                Skins    = i.Skins,
                Unknown  = i.Unknown,
                Unknown2 = i.Unknown2,
                Unknown3 = i.Unknown3,
                Unknown4 = i.Unknown4
            }).ToList();

            edModeDefinition = (RenderModel)ConvertData(null, edModeDefinition, false);

            var variantRegions = new List <RenderModel.Region>();

            foreach (var region in edModeDefinition.Regions)
            {
                if (regionNames.Count != 0 && !regionNames.Contains(CacheContext.GetString(region.Name)))
                {
                    continue;
                }

                var variantRegion = new RenderModel.Region
                {
                    Name         = region.Name,
                    Permutations = new List <RenderModel.Region.Permutation>()
                };

                foreach (var permutation in region.Permutations)
                {
                    if (variantName == CacheContext.GetString(permutation.Name))
                    {
                        variantRegion.Permutations.Add(permutation);
                    }
                }

                variantRegions.Add(variantRegion);
            }

            var variantMeshes        = new List <int>();
            var variantMaterials     = new List <int>();
            var variantVertexBuffers = new List <int>();
            var variantIndexBuffers  = new List <int>();

            foreach (var region in variantRegions)
            {
                foreach (var permutation in region.Permutations)
                {
                    for (var i = permutation.MeshIndex; i < (short)(permutation.MeshIndex + permutation.MeshCount); i++)
                    {
                        var mesh = edModeDefinition.Geometry.Meshes[i];

                        foreach (var part in mesh.Parts)
                        {
                            if (part.MaterialIndex != -1 && !variantMaterials.Contains(part.MaterialIndex))
                            {
                                variantMaterials.Add(part.MaterialIndex);
                            }
                        }

                        foreach (var vertexBuffer in mesh.VertexBufferIndices)
                        {
                            if (vertexBuffer != ushort.MaxValue && !variantVertexBuffers.Contains(vertexBuffer))
                            {
                                variantVertexBuffers.Add(vertexBuffer);
                            }
                        }

                        foreach (var indexBuffer in mesh.IndexBufferIndices)
                        {
                            if (indexBuffer != ushort.MaxValue && !variantIndexBuffers.Contains(indexBuffer))
                            {
                                variantIndexBuffers.Add(indexBuffer);
                            }
                        }

                        if (!variantMeshes.Contains(i))
                        {
                            variantMeshes.Add(i);
                        }
                    }
                }
            }

            variantMeshes.Sort();
            variantMaterials.Sort();
            variantVertexBuffers.Sort();
            variantIndexBuffers.Sort();

            foreach (var meshIndex in variantMeshes)
            {
                var mesh = edModeDefinition.Geometry.Meshes[meshIndex];

                foreach (var part in mesh.Parts)
                {
                    if (part.MaterialIndex != -1)
                    {
                        part.MaterialIndex = (short)variantMaterials.IndexOf(part.MaterialIndex);
                    }
                }
            }

            foreach (var region in variantRegions)
            {
                foreach (var permutation in region.Permutations)
                {
                    if (permutation.MeshIndex != -1)
                    {
                        permutation.MeshIndex = (short)variantMeshes.IndexOf(permutation.MeshIndex);
                    }
                }
            }

            foreach (var meshIndex in variantMeshes)
            {
                var mesh = edModeDefinition.Geometry.Meshes[meshIndex];

                for (var i = 0; i < mesh.VertexBufferIndices.Length; i++)
                {
                    if (!variantVertexBuffers.Contains(mesh.VertexBufferIndices[i]))
                    {
                        mesh.VertexBufferIndices[i] = ushort.MaxValue;
                    }
                    else
                    {
                        mesh.VertexBufferIndices[i] = (ushort)variantVertexBuffers.IndexOf(mesh.VertexBufferIndices[i]);
                    }
                }

                for (var i = 0; i < mesh.IndexBufferIndices.Length; i++)
                {
                    if (!variantIndexBuffers.Contains(mesh.IndexBufferIndices[i]))
                    {
                        mesh.IndexBufferIndices[i] = ushort.MaxValue;
                    }
                    else
                    {
                        mesh.IndexBufferIndices[i] = (ushort)variantIndexBuffers.IndexOf(mesh.IndexBufferIndices[i]);
                    }
                }
            }

            edModeDefinition.Regions         = variantRegions;
            edModeDefinition.Geometry.Meshes = edModeDefinition.Geometry.Meshes.Where(i => variantMeshes.Contains(edModeDefinition.Geometry.Meshes.IndexOf(i))).ToList();

            //
            // Port Blam render_model materials
            //

            materials = materials.Where(i => variantMaterials.Contains(materials.IndexOf(i))).ToList();

            using (var stream = CacheContext.OpenTagCacheReadWrite())
            {
                for (var i = 0; i < materials.Count; i++)
                {
                    var material = materials[i];

                    if (material.RenderMethod.Index == -1)
                    {
                        continue;
                    }

                    var blamRenderMethod    = materials[i].RenderMethod;
                    var blamRenderMethodTag = BlamCache.IndexItems.GetItemByID(blamRenderMethod.Index);

                    var renderMethodExists = false;

                    foreach (var instance in CacheContext.TagCache.Index.FindAllInGroup("rm  "))
                    {
                        if (instance?.Name == blamRenderMethodTag.Name)
                        {
                            renderMethodExists    = true;
                            material.RenderMethod = instance;
                            break;
                        }
                    }

                    if (!renderMethodExists)
                    {
                        material.RenderMethod = CacheContext.GetTag <Shader>(@"shaders\invalid");
                    }
                }
            }

            edModeDefinition.Materials = materials;

            //
            // Load Blam resource data
            //

            var resourceData = BlamCache.GetRawFromID(edModeDefinition.Geometry.ZoneAssetHandle);

            if (resourceData == null)
            {
                Console.WriteLine("Blam render_geometry resource contains no data.");
                return(true);
            }

            //
            // Load Blam resource definition
            //

            Console.Write("Loading Blam render_geometry resource definition...");

            var definitionEntry = BlamCache.ResourceGestalt.TagResources[edModeDefinition.Geometry.ZoneAssetHandle.Index];

            var resourceDefinition = new RenderGeometryApiResourceDefinition
            {
                VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(),
                IndexBuffers  = new List <TagStructureReference <IndexBufferDefinition> >()
            };

            using (var definitionStream = new MemoryStream(BlamCache.ResourceGestalt.FixupInformation))
                using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian))
                {
                    var dataContext = new DataSerializationContext(definitionReader, null, CacheResourceAddressType.Definition);

                    definitionReader.SeekTo(definitionEntry.FixupInformationOffset + (definitionEntry.FixupInformationLength - 24));

                    var vertexBufferCount = definitionReader.ReadInt32();
                    definitionReader.Skip(8);
                    var indexBufferCount = definitionReader.ReadInt32();

                    definitionReader.SeekTo(definitionEntry.FixupInformationOffset);

                    for (var i = 0; i < vertexBufferCount; i++)
                    {
                        resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition>
                        {
                            Definition = new VertexBufferDefinition
                            {
                                Count      = definitionReader.ReadInt32(),
                                Format     = (VertexBufferFormat)definitionReader.ReadInt16(),
                                VertexSize = definitionReader.ReadInt16(),
                                Data       = new TagData
                                {
                                    Size     = definitionReader.ReadInt32(),
                                    Unused4  = definitionReader.ReadInt32(),
                                    Unused8  = definitionReader.ReadInt32(),
                                    Address  = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()),
                                    Unused10 = definitionReader.ReadInt32()
                                }
                            }
                        });
                    }

                    definitionReader.Skip(vertexBufferCount * 12);

                    for (var i = 0; i < indexBufferCount; i++)
                    {
                        resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition>
                        {
                            Definition = new IndexBufferDefinition
                            {
                                Format = (IndexBufferFormat)definitionReader.ReadInt32(),
                                Data   = new TagData
                                {
                                    Size     = definitionReader.ReadInt32(),
                                    Unused4  = definitionReader.ReadInt32(),
                                    Unused8  = definitionReader.ReadInt32(),
                                    Address  = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()),
                                    Unused10 = definitionReader.ReadInt32()
                                }
                            }
                        });
                    }
                }

            Console.WriteLine("done.");

            //
            // Convert Blam resource data
            //

            using (var edResourceStream = new MemoryStream())
            {
                //
                // Convert Blam render_geometry_api_resource_definition
                //

                using (var blamResourceStream = new MemoryStream(resourceData))
                {
                    //
                    // Convert Blam vertex buffers
                    //

                    Console.Write("Converting vertex buffers...");

                    var previousVertexBufferCount = -1;

                    for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++)
                    {
                        if (!variantVertexBuffers.Contains(i))
                        {
                            continue;
                        }

                        blamResourceStream.Position = definitionEntry.ResourceFixups[i].Offset;
                        if (i > 0)
                        {
                            previousVertexBufferCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count;
                        }
                        GeometryConverter.ConvertVertexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i, previousVertexBufferCount);
                    }

                    Console.WriteLine("done.");

                    //
                    // Convert Blam index buffers
                    //

                    Console.Write("Converting index buffers...");

                    for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++)
                    {
                        if (!variantIndexBuffers.Contains(i))
                        {
                            continue;
                        }

                        blamResourceStream.Position = definitionEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset;
                        GeometryConverter.ConvertIndexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i);
                    }

                    Console.WriteLine("done.");
                }

                resourceDefinition.VertexBuffers = resourceDefinition.VertexBuffers.Where(i => variantVertexBuffers.Contains(resourceDefinition.VertexBuffers.IndexOf(i))).ToList();
                resourceDefinition.IndexBuffers  = resourceDefinition.IndexBuffers.Where(i => variantIndexBuffers.Contains(resourceDefinition.IndexBuffers.IndexOf(i))).ToList();

                //
                // Finalize the new ElDorado geometry resource
                //

                Console.Write("Writing resource data...");

                edModeDefinition.Geometry.Resource = new PageableResource
                {
                    Page     = new RawPage(),
                    Resource = new TagResourceGen3
                    {
                        ResourceType             = TagResourceTypeGen3.RenderGeometry,
                        ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                        ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                        Unknown2 = 1
                    }
                };

                edResourceStream.Position = 0;

                var resourceContext = new ResourceSerializationContext(CacheContext, edModeDefinition.Geometry.Resource);
                CacheContext.Serializer.Serialize(resourceContext, resourceDefinition);
                edModeDefinition.Geometry.Resource.ChangeLocation(ResourceLocation.ResourcesB);
                CacheContext.AddResource(edModeDefinition.Geometry.Resource, edResourceStream);

                Console.WriteLine("done.");
            }

            edModeDefinition.Name = CacheContext.GetStringId(variantName);

            if (edModeTag == null)
            {
                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edModeTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("mode")]);
                        break;
                    }
                }

                if (edModeTag == null)
                {
                    edModeTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("mode")]);
                }
            }

            //
            // Create a new armor model tag
            //

            Model             edHlmtDefinition = null;
            CachedTagInstance edHlmtTag        = null;

            if (isScenery)
            {
                Console.Write($"Verifying {blamTagName}.model...");

                CacheFile.IndexItem blamHlmtTag = null;

                foreach (var tag in BlamCache.IndexItems)
                {
                    if ((tag.GroupTag == "hlmt") && (tag.Name == blamTagName))
                    {
                        blamHlmtTag = tag;
                        break;
                    }
                }

                if (blamHlmtTag == null)
                {
                    Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.model");
                    return(true);
                }

                Console.WriteLine("done.");

                blamContext      = new CacheSerializationContext(ref BlamCache, blamHlmtTag);
                edHlmtDefinition = (Model)ConvertData(null, BlamCache.Deserializer.Deserialize <Model>(blamContext), false);

                edHlmtDefinition.RenderModel        = edModeTag;
                edHlmtDefinition.ReduceToL1SuperLow = 36.38004f;
                edHlmtDefinition.ReduceToL2Low      = 27.28503f;
                edHlmtDefinition.Variants           = new List <Model.Variant>();
                edHlmtDefinition.Materials          = new List <Model.Material>();
                edHlmtDefinition.NewDamageInfo      = new List <Model.GlobalDamageInfoBlock>();
                edHlmtDefinition.Targets            = new List <Model.Target>();

                var collisionRegions = new List <Model.CollisionRegion>();

                foreach (var collisionRegion in edHlmtDefinition.CollisionRegions)
                {
                    var found = false;

                    foreach (var variantRegion in variantRegions)
                    {
                        if (collisionRegion.Name == variantRegion.Name)
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        continue;
                    }

                    found = false;

                    foreach (var permutation in collisionRegion.Permutations)
                    {
                        if (permutation.Name == CacheContext.GetStringId(variantName))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (found)
                    {
                        collisionRegions.Add(collisionRegion);
                    }
                }

                foreach (var collisionRegion in collisionRegions)
                {
                    Model.CollisionRegion.Permutation permutation = null;

                    foreach (var collisionPermutation in collisionRegion.Permutations)
                    {
                        if (collisionPermutation.Name == CacheContext.GetStringId(variantName))
                        {
                            permutation = collisionPermutation;
                            break;
                        }
                    }

                    if (permutation == null)
                    {
                        throw new KeyNotFoundException();
                    }

                    collisionRegion.Permutations = new List <Model.CollisionRegion.Permutation> {
                        permutation
                    };
                }

                edHlmtDefinition.CollisionRegions = collisionRegions;

                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edHlmtTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("hlmt")]);
                        break;
                    }
                }

                if (edHlmtTag == null)
                {
                    edHlmtTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("hlmt")]);
                }
            }

            //
            // Create a new armor scenery tag
            //

            Scenery           edScenDefinition = null;
            CachedTagInstance edScenTag        = null;

            if (isScenery)
            {
                edScenDefinition = new Scenery
                {
                    ObjectType = new GameObjectType
                    {
                        Halo2       = GameObjectTypeHalo2.Scenery,
                        Halo3Retail = GameObjectTypeHalo3Retail.Scenery,
                        Halo3ODST   = GameObjectTypeHalo3ODST.Scenery,
                        HaloOnline  = GameObjectTypeHaloOnline.Scenery
                    },
                    BoundingRadius    = 0.44f,
                    BoundingOffset    = new RealPoint3d(-0.02f, 0.0f, 0.0f),
                    AccelerationScale = 1.2f,
                    SweetenerSize     = GameObject.SweetenerSizeValue.Medium,
                    Model             = edHlmtTag,
                    ChangeColors      = new List <GameObject.ChangeColor>
                    {
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor(),
                        new GameObject.ChangeColor()
                    },
                    NodeMaps = new List <GameObject.NodeMap>()
                };

                for (sbyte i = 0; i < 51; i++)
                {
                    edScenDefinition.NodeMaps.Add(new GameObject.NodeMap {
                        TargetNode = i
                    });
                }

                for (var i = 0; i < CacheContext.TagCache.Index.Count; i++)
                {
                    if (CacheContext.TagCache.Index[i] == null)
                    {
                        CacheContext.TagCache.Index[i] = edScenTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("scen")]);
                        break;
                    }
                }

                if (edScenTag == null)
                {
                    edScenTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("scen")]);
                }
            }

            //
            // Serialize new ElDorado tag definitions
            //

            using (var cacheStream = CacheContext.OpenTagCacheReadWrite())
            {
                CacheContext.Serialize(cacheStream, edModeTag, edModeDefinition);
                edModeTag.Name = isScenery ?
                                 (unitName == "spartan" ?
                                  $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" :
                                  $@"objects\characters\elite\mp_elite\armor\{variantName}") :
                                 (unitName == "spartan" ?
                                  @"objects\characters\masterchief\mp_masterchief\mp_masterchief" :
                                  @"objects\characters\elite\mp_elite\mp_elite");

                if (isScenery)
                {
                    CacheContext.Serialize(cacheStream, edHlmtTag, edHlmtDefinition);
                    CacheContext.Serialize(cacheStream, edScenTag, edScenDefinition);
                    edScenTag.Name = unitName == "spartan" ?
                                     $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" :
                                     $@"objects\characters\elite\mp_elite\armor\{variantName}";
                }
            }

            return(true);
        }
        public override object Execute(List <string> args)
        {
            if (args.Count < 1 || args.Count > 2)
            {
                return(false);
            }

            if (args.Count == 2 && (args[0].ToLower() != "raw"))
            {
                return(false);
            }

            var file = "";

            if (args.Count == 1)
            {
                file = args[0];
            }
            else
            {
                file = args[1];
            }

            var resourceContext = new ResourceSerializationContext(CacheContext, Geometry.Resource);
            var definition      = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

            if (args.Count == 2)
            {
                using (var edResourceStream = new MemoryStream())
                    using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian))
                    {
                        var directory = args[1];
                        if (!Directory.Exists(directory))
                        {
                            Directory.CreateDirectory(directory);
                        }

                        var dataOutDir = directory;

                        for (var i = 0; i < definition.VertexBuffers.Count; i++)
                        {
                            edResourceStream.Position = definition.VertexBuffers[i].Definition.Data.Address.Offset;

                            var vertexBuffer = definition.VertexBuffers[i].Definition;

                            dataOutDir = Path.Combine(directory, $"{i}_{vertexBuffer.Format}_{vertexBuffer.Count}");

                            using (EndianWriter output = new EndianWriter(File.OpenWrite(dataOutDir), EndianFormat.LittleEndian))
                            {
                                byte[] data = edResourceReader.ReadBytes((int)vertexBuffer.Data.Size);
                                output.WriteBlock(data);
                            }
                        }
                    }
            }
            else
            {
                //
                // Convert Blam data to ElDorado data
                //

                using (var fileStream = File.Create(file))
                    using (var fileWriter = new StreamWriter(fileStream))
                    {
                        using (var edResourceStream = new MemoryStream())
                            using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian))
                            {
                                //
                                // Convert Blam vertex buffers
                                //

                                Console.Write("Converting vertex buffers...");
                                CacheContext.ExtractResource(Geometry.Resource, edResourceStream);

                                for (var i = 0; i < definition.VertexBuffers.Count; i++)
                                {
                                    edResourceStream.Position = definition.VertexBuffers[i].Definition.Data.Address.Offset;

                                    var vertexBuffer = definition.VertexBuffers[i].Definition;

                                    //fileWriter.WriteLine($"Offset = {vertexBuffer.Data.Address.Offset.ToString("X8")} Count = {vertexBuffer.Count} Size = {vertexBuffer.VertexSize}, Format = {vertexBuffer.Format.ToString()}");
                                    //fileWriter.WriteLine(Environment.NewLine);

                                    //fileWriter.WriteLine($"Vertex buffer index: {i}");

                                    switch (vertexBuffer.Format)
                                    {
                                    case VertexBufferFormat.TinyPosition:
                                        for (var j = 0; j < vertexBuffer.Count; j++)
                                        {
                                            fileWriter.WriteLine($"Position = ({edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")})");
                                            fileWriter.WriteLine($"Normal   = ({edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")})");
                                            fileWriter.WriteLine($"Color    = {edResourceReader.ReadUInt32().ToString("X8")}");
                                        }
                                        break;

                                    case VertexBufferFormat.StaticPerVertex:
                                        for (var j = 0; j < vertexBuffer.Count; j++)
                                        {
                                            fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:");
                                            fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8"));
                                            fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8"));
                                            fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8"));
                                            fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8"));
                                            fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8"));
                                            fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}");
                                            fileWriter.WriteLine(Environment.NewLine);
                                        }
                                        break;

                                    case VertexBufferFormat.Skinned:
                                        for (var j = 0; j < vertexBuffer.Count; j++)
                                        {
                                            fileWriter.WriteLine($"{j}:");
                                            fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                            fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle());
                                            fileWriter.WriteLine($"Normal   X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                            fileWriter.WriteLine($"Tangent  X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                            fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                            edResourceReader.ReadUInt32();
                                            edResourceReader.ReadUInt32();
                                        }
                                        break;

                                        /*
                                         * case VertexBufferFormat.Unknown1B:
                                         *  var goodCount = 0;
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      //fileWriter.WriteLine($"Index: {j}:");
                                         *      string values = $"({ edResourceReader.ReadSingle()},{ edResourceReader.ReadSingle()}," +
                                         *          $"{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()}" +
                                         *          $",{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()})";
                                         *      if(values != "(0,0,0,0,0,0,0,0,0)")
                                         *      {
                                         *          goodCount++;
                                         *          //fileWriter.WriteLine($"(1,2,3,4,5,6,7,8,9) = "+values);
                                         *      }
                                         *
                                         *
                                         *      /*
                                         *      fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 2 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 3 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 4 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 5 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 6 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 7 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 8 = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Unknown 9 = " + edResourceReader.ReadSingle());
                                         *
                                         *  }
                                         *  //fileWriter.WriteLine($"Valid Unknown1B count = {goodCount}");
                                         *  break;
                                         *
                                         * case VertexBufferFormat.Unknown1A:
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      //fileWriter.WriteLine($"Index: {j}:");
                                         *      //fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadUInt32().ToString("X8"));
                                         *      fileWriter.WriteLine($"(1,2,3) = ({edResourceReader.ReadUInt32().ToString("X8")},{edResourceReader.ReadUInt32().ToString("X8")},{edResourceReader.ReadUInt32().ToString("X8")})");
                                         *  }
                                         *  break;
                                         *
                                         * case VertexBufferFormat.Rigid:
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      fileWriter.WriteLine($"{j}:");
                                         *      fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Normal   X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Tangent  X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *  }
                                         *  break;
                                         *
                                         * case VertexBufferFormat.StaticPerPixel:
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      fileWriter.WriteLine($"{j} U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle());
                                         *      //fileWriter.WriteLine(Environment.NewLine);
                                         *  }
                                         *  break;
                                         *
                                         * case VertexBufferFormat.AmbientPrt:
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      fileWriter.WriteLine($"{j} Blend weight = " + edResourceReader.ReadSingle());
                                         *      //fileWriter.WriteLine(Environment.NewLine);
                                         *  }
                                         *  break;
                                         *
                                         *
                                         * case VertexBufferFormat.World:
                                         *  for (var j = 0; j < vertexBuffer.Count; j++)
                                         *  {
                                         *      fileWriter.WriteLine($"{j}:");
                                         *      fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Normal   X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Tangent  X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *      fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *  }
                                         *  break;
                                         *
                                         *
                                         *
                                         */
                                        /*
                                         *  case VertexBufferFormat.QuadraticPrt:
                                         *      for (var j = 0; j < vertexBuffer.Count; j++)
                                         *      {
                                         *          fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:");
                                         *          fileWriter.WriteLine($"A X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"B X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"C X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}");
                                         *          fileWriter.WriteLine(Environment.NewLine);
                                         *      }
                                         *      break;
                                         *
                                         *
                                         *
                                         *  case VertexBufferFormat.Unknown1B:
                                         *      for (var j = 0; j < vertexBuffer.Count; j++)
                                         *      {
                                         *          fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:");
                                         *          fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 2 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 3 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 4 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 5 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 6 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 7 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 8 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Unknown 9 = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}");
                                         *          fileWriter.WriteLine(Environment.NewLine);
                                         *      }
                                         *      break;
                                         *
                                         *  case VertexBufferFormat.Decorator:
                                         *      for (var j = 0; j < vertexBuffer.Count; j++)
                                         *      {
                                         *          fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:");
                                         *          fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"Normal   X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle());
                                         *          fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}");
                                         *          fileWriter.WriteLine(Environment.NewLine);
                                         *      }
                                         *      break;
                                         *
                                         *  case VertexBufferFormat.LinearPrt:
                                         *      for (var j = 0; j < vertexBuffer.Count; j++)
                                         *      {
                                         *          fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:");
                                         *          fileWriter.WriteLine($"Hex : " + edResourceReader.ReadUInt32().ToString("X8"));
                                         *          fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}");
                                         *          fileWriter.WriteLine(Environment.NewLine);
                                         *      }
                                         *      break;
                                         */
                                    }

                                    //fileWriter.WriteLine($"End of Vertex Buffer index: {i}");

                                    //fileWriter.WriteLine(Environment.NewLine);
                                }
                            }
                    }
            }



            return(true);
        }
        private PageableResource ConvertBitmap(Bitmap bitmap, Dictionary <ResourceLocation, Stream> resourceStreams, int imageIndex, string tagName)
        {
            var        image      = bitmap.Images[imageIndex];
            BaseBitmap baseBitmap = BitmapConverter.ConvertGen3Bitmap(BlamCache, bitmap, imageIndex, BlamCache.Version);

            if (baseBitmap == null)
            {
                return(null);
            }

            // fix type enum
            if (baseBitmap.Type == BitmapType.Array)
            {
                baseBitmap.Type = BitmapType.Texture3D;
            }

            SetTagData(baseBitmap, image);
            var dataSize = baseBitmap.Data.Length;

            var resource = new PageableResource
            {
                Page     = new RawPage(),
                Resource = new TagResourceGen3
                {
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    ResourceType             = TagResourceTypeGen3.Bitmap,
                    Unknown2 = 1
                }
            };

            using (var dataStream = new MemoryStream(baseBitmap.Data))
            {
                var bitmapResource = new Bitmap.BitmapResource
                {
                    Resource = resource,
                    Unknown4 = 0
                };
                var resourceContext = new ResourceSerializationContext(CacheContext, resource);

                // Create new definition
                var resourceDefinition = new BitmapTextureInteropResource
                {
                    Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition>
                    {
                        Definition = new BitmapTextureInteropResource.BitmapDefinition
                        {
                            Data        = new TagData(),
                            UnknownData = new TagData(),
                        }
                    }
                };

                SetResourceDefinitionData(baseBitmap, image, resourceDefinition.Texture.Definition);

                //
                // Serialize the new resource definition
                //

                var location = bitmap.Usage == 2 ?
                               ResourceLocation.TexturesB : // bump maps
                               ResourceLocation.Textures;   // everything else

                resource.ChangeLocation(location);

                if (resource == null)
                {
                    throw new ArgumentNullException("resource");
                }

                if (!dataStream.CanRead)
                {
                    throw new ArgumentException("The input stream is not open for reading", "dataStream");
                }

                var cache = CacheContext.GetResourceCache(location);

                if (!resourceStreams.ContainsKey(location))
                {
                    resourceStreams[location] = FlagIsSet(PortingFlags.Memory) ?
                                                new MemoryStream() :
                                                (Stream)CacheContext.OpenResourceCacheReadWrite(location);

                    if (FlagIsSet(PortingFlags.Memory))
                    {
                        using (var resourceStream = CacheContext.OpenResourceCacheRead(location))
                            resourceStream.CopyTo(resourceStreams[location]);
                    }
                }

                dataSize = (int)(dataStream.Length - dataStream.Position);
                var data = new byte[dataSize];
                dataStream.Read(data, 0, dataSize);

                resource.Page.Index = cache.Add(resourceStreams[location], data, out uint compressedSize);
                resource.Page.CompressedBlockSize   = compressedSize;
                resource.Page.UncompressedBlockSize = (uint)dataSize;
                resource.DisableChecksum();

                CacheContext.Serializer.Serialize(resourceContext, resourceDefinition);
            }

            return(resource);
        }
        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);
        }
Beispiel #30
0
        private ScenarioStructureBsp ConvertScenarioStructureBsp(ScenarioStructureBsp sbsp, CachedTagInstance instance, Dictionary <ResourceLocation, Stream> resourceStreams)
        {
            sbsp.CollisionBspResource = ConvertStructureBspTagResources(sbsp, resourceStreams);
            sbsp.PathfindingResource  = ConvertStructureBspCacheFileTagResources(sbsp, resourceStreams);

            sbsp.Unknown86 = 1;

            //
            // Set compatibility flag for H3 mopps
            //

            if (BlamCache.Version == CacheVersion.Halo3Retail)
            {
                sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.UseMoppIndexPatch;
            }
            else
            {
                sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.None;
            }

            //
            // Fix cluster tag ref and decorator grids
            //

            var resource = sbsp.Geometry.Resource;

            if (resource != null && resource.Page.Index >= 0 && resource.GetLocation(out var location))
            {
                var resourceContext = new ResourceSerializationContext(CacheContext, sbsp.Geometry.Resource);
                var definition      = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext);

                using (var edResourceStream = new MemoryStream())
                    using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian))
                    {
                        var pageable = sbsp.Geometry.Resource;

                        if (pageable == null)
                        {
                            throw new ArgumentNullException("sbsp.Geometry.Resource");
                        }

                        if (!edResourceStream.CanWrite)
                        {
                            throw new ArgumentException("The output stream is not open for writing", "outStream");
                        }

                        pageable.GetLocation(out var resourceLocation);

                        var cache = CacheContext.GetResourceCache(resourceLocation);

                        if (!resourceStreams.ContainsKey(resourceLocation))
                        {
                            resourceStreams[resourceLocation] = FlagIsSet(PortingFlags.Memory) ?
                                                                new MemoryStream() :
                                                                (Stream)CacheContext.OpenResourceCacheReadWrite(resourceLocation);

                            if (FlagIsSet(PortingFlags.Memory))
                            {
                                using (var resourceStream = CacheContext.OpenResourceCacheRead(resourceLocation))
                                    resourceStream.CopyTo(resourceStreams[resourceLocation]);
                            }
                        }

                        cache.Decompress(resourceStreams[resourceLocation], pageable.Page.Index, pageable.Page.CompressedBlockSize, edResourceStream);

                        var inVertexStream = VertexStreamFactory.Create(CacheVersion.HaloOnline106708, edResourceStream);

                        foreach (var cluster in sbsp.Clusters)
                        {
                            List <ScenarioStructureBsp.Cluster.DecoratorGrid> newDecoratorGrids = new List <ScenarioStructureBsp.Cluster.DecoratorGrid>();

                            foreach (var grid in cluster.DecoratorGrids)
                            {
                                grid.DecoratorGeometryIndex_HO = grid.DecoratorGeometryIndex_H3;
                                grid.DecoratorIndex_HO         = grid.DecoratorIndex_H3;

                                if (grid.Amount == 0)
                                {
                                    newDecoratorGrids.Add(grid);
                                }
                                else
                                {
                                    List <TinyPositionVertex> vertices = new List <TinyPositionVertex>();

                                    // Get the buffer the right grid
                                    var vertexBuffer = definition.VertexBuffers[grid.DecoratorGeometryIndex_HO].Definition;
                                    // Get the offset from the grid
                                    edResourceStream.Position = vertexBuffer.Data.Address.Offset + grid.DecoratorGeometryOffset;
                                    // Read all vertices and add to the list
                                    for (int i = 0; i < grid.Amount; i++)
                                    {
                                        vertices.Add(inVertexStream.ReadTinyPositionVertex());
                                    }

                                    // Get the new grids
                                    List <ScenarioStructureBsp.Cluster.DecoratorGrid> newGrids = ConvertDecoratorGrid(vertices, grid);

                                    // Add all to list
                                    foreach (var newGrid in newGrids)
                                    {
                                        newDecoratorGrids.Add(newGrid);
                                    }
                                }
                            }

                            cluster.DecoratorGrids = newDecoratorGrids;
                        }
                    }
            }

            //
            // Temporary Fixes:
            //

            // Without this 005_intro crash on cortana sbsp
            sbsp.Geometry2.UnknownSections = new List <RenderGeometry.UnknownSection>();

            return(sbsp);
        }