Ejemplo n.º 1
0
        private void WriteTagsSection(EndianWriter writer, DataSerializationContext context, TagSerializer serializer)
        {
            uint sectionOffset = (uint)writer.BaseStream.Position;
            GenericSectionEntry tagCachesEntry = new GenericSectionEntry(TagCaches.Count, 0x8);

            tagCachesEntry.Write(writer);
            // make room for table

            writer.Write(new byte[0x28 * TagCaches.Count]);

            for (int i = 0; i < TagCaches.Count; i++)
            {
                uint offset = (uint)writer.BaseStream.Position;

                TagCachesStreams[i].Position = 0;
                StreamUtil.Copy(TagCachesStreams[i], writer.BaseStream, (int)TagCachesStreams[i].Length);
                StreamUtil.Align(writer.BaseStream, 4);

                uint size = (uint)(writer.BaseStream.Position - offset);

                writer.BaseStream.Seek(tagCachesEntry.TableOffset + 0x28 * i + sectionOffset, SeekOrigin.Begin);
                var tableEntry = new CacheTableEntry(size, offset - sectionOffset, CacheNames[i]);
                serializer.Serialize(context, tableEntry);
                writer.BaseStream.Seek(0, SeekOrigin.End);
            }
        }
        public void ConvertIndexBuffer(RenderGeometryApiResourceDefinition resourceDefinition, Stream inputStream, Stream outputStream, int indexBufferIndex)
        {
            var indexBuffer = resourceDefinition.IndexBuffers[indexBufferIndex].Definition;

            var indexCount = indexBuffer.Data.Size / 2;

            var inIndexStream = new IndexBufferStream(
                inputStream,
                EndianFormat.BigEndian);

            var outIndexStream = new IndexBufferStream(
                outputStream,
                EndianFormat.LittleEndian);

            StreamUtil.Align(outputStream, 4);
            indexBuffer.Data.Address = new CacheAddress(CacheAddressType.Resource, (int)outputStream.Position);

            for (var j = 0; j < indexCount; j++)
            {
                outIndexStream.WriteIndex(inIndexStream.ReadIndex());
            }

            resourceDefinition.IndexBuffers[indexBufferIndex].DefinitionAddress = 0;
            resourceDefinition.IndexBuffers[indexBufferIndex].RuntimeAddress    = 0;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Serializes a data reference composed of raw bytes.
        /// </summary>
        /// <param name="tagStream">The stream to write completed blocks of tag data to.</param>
        /// <param name="block">The temporary block to write incomplete tag data to.</param>
        /// <param name="data">The data.</param>
        /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param>
        private void SerializeDataReference(MemoryStream tagStream, IDataBlock block, byte[] data, TagFieldAttribute valueInfo)
        {
            var  writer = block.Writer;
            uint offset = 0;
            uint size   = 0;

            if (data != null && data.Length > 0)
            {
                // Ensure the block is aligned correctly
                var align = Math.Max(DefaultBlockAlign, (valueInfo != null) ? valueInfo.Align : 0);
                StreamUtil.Align(tagStream, (int)align);

                // Write its data
                offset = (uint)tagStream.Position;
                size   = (uint)data.Length;
                tagStream.Write(data, 0, data.Length);
                StreamUtil.Align(tagStream, DefaultBlockAlign);
            }

            // Write the reference data
            writer.Write(size);
            writer.Write(0);
            writer.Write(0);
            if (size > 0)
            {
                block.WritePointer(offset, typeof(byte[]));
            }
            else
            {
                writer.Write(0);
            }
            writer.Write(0);
        }
Ejemplo n.º 4
0
        private void WriteFileEntries(EndianWriter writer, ISerializationContext context, TagSerializer serializer)
        {
            const int kFileTableEntrySize = 0x108;

            uint sectionOffset        = (uint)writer.BaseStream.Position;
            GenericSectionEntry table = new GenericSectionEntry(Files.Count, 0x8);

            table.Write(writer);

            // make room for table
            writer.BaseStream.Position = sectionOffset + table.TableOffset + Files.Count * kFileTableEntrySize;
            var index = 0;

            foreach (var fileEntry in Files)
            {
                StreamUtil.Align(writer.BaseStream, 0x10);
                uint offset = (uint)(writer.BaseStream.Position - sectionOffset);
                // write the contents
                fileEntry.Value.CopyTo(writer.BaseStream);

                // seek to the file table entry
                writer.BaseStream.Position = sectionOffset + table.TableOffset + index * kFileTableEntrySize;
                index++;

                // write the table entry
                var tableEntry = new FileTableEntry();
                tableEntry.Path   = fileEntry.Key;
                tableEntry.Size   = (uint)fileEntry.Value.Length;
                tableEntry.Offset = offset;
                serializer.Serialize(context, tableEntry);

                // move back to where we were
                writer.Seek(0, SeekOrigin.End);
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Serializes a data reference composed of raw bytes.
        /// </summary>
        /// <param name="tagStream">The stream to write completed blocks of tag data to.</param>
        /// <param name="block">The temporary block to write incomplete tag data to.</param>
        /// <param name="data">The data.</param>
        private static void SerializeDataReference(MemoryStream tagStream, IDataBlock block, byte[] data)
        {
            var  writer = block.Writer;
            uint offset = 0;
            uint size   = 0;

            if (data != null && data.Length > 0)
            {
                // The block has data - write it out to the tag
                StreamUtil.Align(tagStream, DefaultBlockAlign);
                offset = (uint)tagStream.Position;
                size   = (uint)data.Length;
                tagStream.Write(data, 0, data.Length);
                StreamUtil.Align(tagStream, DefaultBlockAlign);
            }

            // Write the reference data
            writer.Write(size);
            writer.Write(0);
            writer.Write(0);
            if (size > 0)
            {
                block.WritePointer(offset, typeof(byte[]));
            }
            else
            {
                writer.Write(0);
            }
            writer.Write(0);
        }
Ejemplo n.º 6
0
        private void WriteMapsSection(EndianWriter writer)
        {
            uint sectionOffset           = (uint)writer.BaseStream.Position;
            GenericSectionEntry mapEntry = new GenericSectionEntry(MapFileStreams.Count, 0x8);

            mapEntry.Write(writer);
            // make room for table

            writer.Write(new byte[0x10 * mapEntry.Count]);

            for (int i = 0; i < MapFileStreams.Count; i++)
            {
                var  mapFileStream = MapFileStreams[i];
                uint offset        = (uint)writer.BaseStream.Position;
                int  size          = (int)mapFileStream.Length;

                mapFileStream.Position = 0;
                StreamUtil.Copy(mapFileStream, writer.BaseStream, (int)mapFileStream.Length);
                StreamUtil.Align(writer.BaseStream, 4);

                // seek to the table and update size and offset
                writer.BaseStream.Seek(mapEntry.TableOffset + 0x10 * i + sectionOffset, SeekOrigin.Begin);
                var tableEntry = new CacheMapTableEntry(size, offset - sectionOffset, MapToCacheMapping[i], MapIds[i]);
                tableEntry.Write(writer);
                writer.BaseStream.Seek(0, SeekOrigin.End);
            }

            writer.BaseStream.Seek(0, SeekOrigin.End);
        }
Ejemplo n.º 7
0
        private void WriteStringIdsSection(EndianWriter writer)
        {
            var stringIdStream = new MemoryStream();

            StringTable.Save(stringIdStream);
            stringIdStream.Position = 0;
            StreamUtil.Copy(stringIdStream, writer.BaseStream, (int)stringIdStream.Length);
            StreamUtil.Align(writer.BaseStream, 4);
        }
Ejemplo n.º 8
0
        private static void SerializeIndexBuffer(MeshData mesh, Stream outStream)
        {
            var indexStream = new IndexBufferStream(outStream, IndexBufferFormat.UInt16);

            foreach (var index in mesh.Indexes)
            {
                indexStream.WriteIndex(index);
            }
            StreamUtil.Align(outStream, 4);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Serializes a tag block.
        /// </summary>
        /// <param name="version"></param>
        /// <param name="context">The serialization context to use.</param>
        /// <param name="tagStream">The stream to write completed blocks of tag data to.</param>
        /// <param name="block">The temporary block to write incomplete tag data to.</param>
        /// <param name="list">The list of values in the tag block.</param>
        /// <param name="listType">Type of the list.</param>
        /// <param name="valueInfo">Information about the value. Can be <c>null</c>.</param>
        private void SerializeTagBlock(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object list, Type listType, TagFieldAttribute valueInfo)
        {
            var writer = block.Writer;
            var count  = 0;

            if (list != null)
            {
                // Use reflection to get the number of elements in the list
                var countProperty = listType.GetProperty("Count");
                count = (int)countProperty.GetValue(list);
            }
            if (count == 0)
            {
                writer.Write(0);
                writer.Write(0);
                writer.Write(0);
                return;
            }

            var elementType = listType.GenericTypeArguments[0];
            TagStructureAttribute structure;

            try
            {
                structure = TagStructure.GetTagStructureInfo(elementType, Version).Structure;
            }
            catch
            {
                structure = null;
            }

            // Serialize each value in the list to a data block
            var tagBlock       = context.CreateBlock();
            var enumerableList = (System.Collections.IEnumerable)list;

            foreach (var val in enumerableList)
            {
                SerializeValue(version, context, tagStream, tagBlock, val, null, elementType);
            }

            // Ensure the block is aligned correctly
            var align = Math.Max(DefaultBlockAlign, (valueInfo != null) ? valueInfo.Align : 0);

            StreamUtil.Align(tagStream, (int)align);

            // Finalize the block and write the tag block reference
            writer.Write(count);
            block.WritePointer(tagBlock.Finalize(tagStream), listType);
            writer.Write(0);
        }
            public uint Finalize(Stream outStream)
            {
                // Write the data out, aligning the offset and size
                StreamUtil.Align(outStream, DefaultBlockAlign);
                var dataOffset = (uint)outStream.Position;

                outStream.Write(Stream.GetBuffer(), 0, (int)Stream.Length);
                StreamUtil.Align(outStream, DefaultBlockAlign);

                // Adjust fixups and add them to the resource
                _context._fixups.AddRange(_fixups.Select(f => FinalizeDefinitionFixup(f, dataOffset)));
                _context._d3dFixups.AddRange(_d3dFixups.Select(f => FinalizeD3DFixup(f, dataOffset)));

                // Free the block data
                Writer.Close();
                Stream = null;
                Writer = null;
                return(dataOffset);
            }
Ejemplo n.º 11
0
        public static byte[] ConvertHkpMoppData(CacheVersion sourceVersion, CacheVersion destVersion, byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                return(data);
            }

            byte[] result;
            using (var inputReader = new EndianReader(new MemoryStream(data), CacheVersionDetection.IsLittleEndian(sourceVersion) ? EndianFormat.LittleEndian : EndianFormat.BigEndian))
                using (var outputStream = new MemoryStream())
                    using (var outputWriter = new EndianWriter(outputStream, CacheVersionDetection.IsLittleEndian(destVersion) ? EndianFormat.LittleEndian : EndianFormat.BigEndian))
                    {
                        var dataContext  = new DataSerializationContext(inputReader, outputWriter);
                        var deserializer = new TagDeserializer(sourceVersion);
                        var serializer   = new TagSerializer(destVersion);
                        while (!inputReader.EOF)
                        {
                            var         header      = deserializer.Deserialize <HkpMoppCode>(dataContext);
                            var         dataSize    = header.ArrayBase.GetCapacity();
                            var         alignedSize = dataSize + 0xf & ~0xf;
                            var         nextOffset  = inputReader.Position + alignedSize;
                            List <byte> moppCodes   = new List <byte>();
                            for (int j = 0; j < header.ArrayBase.GetCapacity(); j++)
                            {
                                moppCodes.Add(inputReader.ReadByte());
                            }
                            inputReader.SeekTo(nextOffset);

                            moppCodes = ConvertMoppCodes(sourceVersion, destVersion, moppCodes);

                            serializer.Serialize(dataContext, header);
                            for (int j = 0; j < moppCodes.Count; j++)
                            {
                                outputWriter.Write(moppCodes[j]);
                            }

                            StreamUtil.Align(outputStream, 0x10);
                        }
                        result = outputStream.ToArray();
                    }
            return(result);
        }
Ejemplo n.º 12
0
            public uint Finalize(Stream outStream)
            {
                // Write the data out, aligning the offset and size
                StreamUtil.Align(outStream, (int)_align);
                var dataOffset = (uint)outStream.Position;

                outStream.Write(Stream.GetBuffer(), 0, (int)Stream.Length);
                StreamUtil.Align(outStream, DefaultBlockAlign);

                // Adjust fixups and add them to the tag
                _context.Data.PointerFixups.AddRange(_fixups.Select(f => FinalizeFixup(f, dataOffset)));
                _context.Data.ResourcePointerOffsets.AddRange(_resourceOffsets.Select(o => o + dataOffset));
                _context.Data.TagReferenceOffsets.AddRange(_tagReferenceOffsets.Select(o => o + dataOffset));

                // Free the block data
                Writer.Close();
                Stream = null;
                Writer = null;
                return(dataOffset);
            }
Ejemplo n.º 13
0
        public ushort CreateIndexBuffer(RenderGeometryApiResourceDefinition resourceDefinition, Stream outputStream, List <ushort> buffer)
        {
            resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition>
            {
                Definition = new IndexBufferDefinition
                {
                    Format = IndexBufferFormat.TriangleStrip,
                    Data   = new TagData
                    {
                        Size     = buffer.Count() * 2,
                        Unused4  = 0,
                        Unused8  = 0,
                        Address  = new CacheAddress(),
                        Unused10 = 0
                    }
                }
            });

            var indexBuffer = resourceDefinition.IndexBuffers.Last().Definition;

            var indexCount = indexBuffer.Data.Size / 2;

            var outIndexStream = new EndianWriter(
                outputStream,
                EndianFormat.LittleEndian);

            StreamUtil.Align(outputStream, 4);
            indexBuffer.Data.Address = new CacheAddress(CacheAddressType.Resource, (int)outputStream.Position);

            for (var j = 0; j < indexCount; j++)
            {
                outIndexStream.Write((short)buffer[j]);
            }

            return((ushort)resourceDefinition.IndexBuffers.IndexOf(resourceDefinition.IndexBuffers.Last()));
        }
Ejemplo n.º 14
0
        public override void SerializeTagData(ISerializationContext context, MemoryStream tagStream, IDataBlock block, TagData tagData, TagFieldAttribute valueInfo)
        {
            if (context.GetType() != typeof(ResourceDefinitionSerializationContext))
            {
                throw new Exception($"Invalid context type given resource deserialization");
            }

            if (block.GetType() != typeof(ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock))
            {
                throw new Exception($"Invalid block type given resource deserialization");
            }

            var resourceBlock   = block as ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock;
            var resourceContext = context as ResourceDefinitionSerializationContext;

            var writer = block.Writer;

            if (tagData == null || tagData.Data == null || tagData.Data.Length == 0)
            {
                writer.Write(0);
                writer.Write(0);
                writer.Write(0);
                writer.Write(0);
                writer.Write(0);
                return;
            }

            CacheAddressType addressType = tagData.AddressType;

            int  dataOffset  = 0;                                      // offset in the data stream to the start of the data
            uint blockOffset = (uint)writer.BaseStream.Position + 0xC; // offset to the address pointing to the above relative to the current block.
            uint size        = 0;

            // stream where the byte[] in the data should be written to:
            var dataStream = (MemoryStream)resourceContext.GetWriter(addressType).BaseStream;

            var data = tagData.Data;

            if (data != null && data.Length > 0)
            {
                // Ensure the block is aligned correctly
                var align = Math.Max(DefaultResourceAlign, (valueInfo != null) ? valueInfo.Align : 0);
                StreamUtil.Align(dataStream, (int)align);

                // Write its data
                dataOffset = (int)dataStream.Position;
                size       = (uint)data.Length;
                dataStream.Write(data, 0, data.Length);
                StreamUtil.Align(dataStream, DefaultResourceAlign);
            }

            var dataAddress = new CacheAddress(addressType, dataOffset);

            var dataFixup = new ResourceFixupLocation
            {
                BlockOffset = blockOffset,
                Address     = dataAddress
            };

            resourceBlock.FixupLocations.Add(dataFixup);

            // this fixup will need to be adjusted when we move the block

            // Write the reference data
            writer.Write(size);
            writer.Write(0);
            writer.Write(0);
            writer.Write(dataAddress.Value);
            writer.Write(0);
        }
Ejemplo n.º 15
0
        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);
        }
        private PageableResource ConvertStructureBspCacheFileTagResources(ScenarioStructureBsp bsp, Dictionary <ResourceLocation, Stream> resourceStreams)
        {
            //
            // Set up ElDorado resource reference
            //

            bsp.PathfindingResource = new PageableResource
            {
                Page = new RawPage
                {
                    Index = -1
                },
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.Pathfinding,
                    DefinitionData           = new byte[0x30],
                    DefinitionAddress        = new CacheResourceAddress(CacheResourceAddressType.Definition, 0),
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };

            //
            // Load Blam resource data
            //

            var resourceData = BlamCache.Version > CacheVersion.Halo3Retail ?
                               BlamCache.GetRawFromID(bsp.ZoneAssetIndex4) :
                               null;

            if (resourceData == null)
            {
                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                {
                    return(bsp.PathfindingResource);
                }

                resourceData = new byte[0x30];
            }

            //
            // Port Blam resource definition
            //

            StructureBspCacheFileTagResources resourceDefinition = null;

            if (BlamCache.Version >= CacheVersion.Halo3ODST)
            {
                var resourceEntry = BlamCache.ResourceGestalt.TagResources[bsp.ZoneAssetIndex4.Index];

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

                using (var definitionStream = new MemoryStream(bsp.PathfindingResource.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)
                            {
                                var newFixup = new TagResourceGen3.ResourceFixup
                                {
                                    BlockOffset = (uint)fixup.BlockOffset,
                                    Address     = new CacheResourceAddress(
                                        fixup.Type == 4 ?
                                        CacheResourceAddressType.Resource :
                                        CacheResourceAddressType.Definition,
                                        fixup.Offset)
                                };

                                definitionStream.Position = newFixup.BlockOffset;
                                definitionWriter.Write(newFixup.Address.Value);

                                bsp.PathfindingResource.Resource.ResourceFixups.Add(newFixup);
                            }

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

                            definitionStream.Position = bsp.PathfindingResource.Resource.DefinitionAddress.Offset;
                            resourceDefinition        = BlamCache.Deserializer.Deserialize <StructureBspCacheFileTagResources>(dataContext);
                        }
            }
            else
            {
                resourceDefinition = new StructureBspCacheFileTagResources()
                {
                    SurfacePlanes   = new TagBlock <ScenarioStructureBsp.SurfacesPlanes>(bsp.SurfacePlanes.Count, new CacheResourceAddress()),
                    Planes          = new TagBlock <ScenarioStructureBsp.Plane>(bsp.Planes.Count, new CacheResourceAddress()),
                    EdgeToSeams     = new TagBlock <ScenarioStructureBsp.EdgeToSeamMapping>(bsp.EdgeToSeams.Count, new CacheResourceAddress()),
                    PathfindingData = new List <StructureBspCacheFileTagResources.PathfindingDatum>() // TODO: copy from bsp.PathfindingData...
                };
            }

            //
            // Port Blam resource to ElDorado resource cache
            //

            using (var blamResourceStream = new MemoryStream(resourceData))
                using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian))
                    using (var dataStream = new MemoryStream())
                        using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian))
                        {
                            var dataContext = new DataSerializationContext(resourceReader, resourceWriter);

                            //
                            // Surfaces Planes
                            //

                            StreamUtil.Align(dataStream, 0x4);

                            if (BlamCache.Version >= CacheVersion.Halo3ODST)
                            {
                                blamResourceStream.Position = resourceDefinition.SurfacePlanes.Address.Offset;
                            }

                            resourceDefinition.SurfacePlanes = new TagBlock <ScenarioStructureBsp.SurfacesPlanes>(
                                (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.SurfacePlanes.Count : resourceDefinition.SurfacePlanes.Count),
                                new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));

                            for (var i = 0; i < resourceDefinition.SurfacePlanes.Count; i++)
                            {
                                var element = BlamCache.Version < CacheVersion.Halo3ODST ?
                                              bsp.SurfacePlanes[i] :
                                              BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.SurfacesPlanes>(dataContext);

                                if (BlamCache.Version < CacheVersion.Halo3ODST)
                                {
                                    element.PlaneIndexNew = element.PlaneIndexOld;
                                    element.PlaneCountNew = element.PlaneCountOld;
                                }

                                CacheContext.Serializer.Serialize(dataContext, element);
                            }

                            //
                            // UnknownRaw1sts
                            //

                            StreamUtil.Align(dataStream, 0x4);

                            if (BlamCache.Version >= CacheVersion.Halo3ODST)
                            {
                                blamResourceStream.Position = resourceDefinition.Planes.Address.Offset;
                            }

                            resourceDefinition.Planes = new TagBlock <ScenarioStructureBsp.Plane>(
                                (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.Planes.Count : resourceDefinition.Planes.Count),
                                new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));

                            for (var i = 0; i < resourceDefinition.Planes.Count; i++)
                            {
                                var element = BlamCache.Version < CacheVersion.Halo3ODST ?
                                              bsp.Planes[i] :
                                              BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.Plane>(dataContext);

                                CacheContext.Serializer.Serialize(dataContext, element);
                            }

                            //
                            // UnknownRaw7ths
                            //

                            StreamUtil.Align(dataStream, 0x4);

                            if (BlamCache.Version >= CacheVersion.Halo3ODST)
                            {
                                blamResourceStream.Position = resourceDefinition.EdgeToSeams.Address.Offset;
                            }

                            resourceDefinition.EdgeToSeams = new TagBlock <ScenarioStructureBsp.EdgeToSeamMapping>(
                                (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.EdgeToSeams.Count : resourceDefinition.EdgeToSeams.Count),
                                new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));

                            for (var i = 0; i < resourceDefinition.EdgeToSeams.Count; i++)
                            {
                                var element = BlamCache.Version < CacheVersion.Halo3ODST ?
                                              bsp.EdgeToSeams[i] :
                                              BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.EdgeToSeamMapping>(dataContext);

                                CacheContext.Serializer.Serialize(dataContext, element);
                            }

                            if (BlamCache.Version < CacheVersion.Halo3ODST && bsp.PathfindingData.Count != 0)
                            {
                                var pathfinding = new StructureBspCacheFileTagResources.PathfindingDatum()
                                {
                                    StructureChecksum = bsp.PathfindingData[0].StructureChecksum,
                                    ObjectReferences  = new List <StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference>(),
                                    Seams             = new List <StructureBspCacheFileTagResources.PathfindingDatum.Seam>(),
                                    JumpSeams         = new List <StructureBspCacheFileTagResources.PathfindingDatum.JumpSeam>()
                                };

                                foreach (var oldObjectReference in bsp.PathfindingData[0].ObjectReferences)
                                {
                                    var objectReference = new StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference
                                    {
                                        Flags          = oldObjectReference.Flags,
                                        Bsps           = new List <StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference.BspReference>(),
                                        ObjectUniqueID = oldObjectReference.ObjectUniqueID,
                                        OriginBspIndex = oldObjectReference.OriginBspIndex,
                                        ObjectType     = oldObjectReference.ObjectType.DeepClone(),
                                        Source         = oldObjectReference.Source
                                    };

                                    foreach (var bspRef in oldObjectReference.Bsps)
                                    {
                                        objectReference.Bsps.Add(new StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference.BspReference
                                        {
                                            BspIndex     = bspRef.BspIndex,
                                            NodeIndex    = bspRef.NodeIndex,
                                            Bsp2dRefs    = new TagBlock <ScenarioStructureBsp.PathfindingDatum.ObjectReference.BspReference.Bsp2dRef>(bspRef.Bsp2dRefs.Count, new CacheResourceAddress()),
                                            VertexOffset = bspRef.VertexOffset
                                        });
                                    }

                                    pathfinding.ObjectReferences.Add(objectReference);
                                }

                                foreach (var oldSeam in bsp.PathfindingData[0].Seams)
                                {
                                    pathfinding.Seams.Add(new StructureBspCacheFileTagResources.PathfindingDatum.Seam
                                    {
                                        LinkIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>(
                                            oldSeam.LinkIndices.Count, new CacheResourceAddress())
                                    });
                                }

                                foreach (var oldJumpSeam in bsp.PathfindingData[0].JumpSeams)
                                {
                                    pathfinding.JumpSeams.Add(new StructureBspCacheFileTagResources.PathfindingDatum.JumpSeam
                                    {
                                        UserJumpIndex = oldJumpSeam.UserJumpIndex,
                                        DestOnly      = oldJumpSeam.DestOnly,
                                        Length        = oldJumpSeam.Length,
                                        JumpIndices   = new TagBlock <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>(
                                            oldJumpSeam.JumpIndices.Count, new CacheResourceAddress())
                                    });
                                }

                                resourceDefinition.PathfindingData.Add(pathfinding);
                            }

                            foreach (var pathfindingDatum in resourceDefinition.PathfindingData)
                            {
                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.Sectors.Address.Offset;
                                }
                                pathfindingDatum.Sectors = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Sector>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Sectors.Count : pathfindingDatum.Sectors.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.Sectors.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].Sectors[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Sector>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.Links.Address.Offset;
                                }
                                pathfindingDatum.Links = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Link>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Links.Count : pathfindingDatum.Links.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.Links.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].Links[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Link>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.References.Address.Offset;
                                }
                                pathfindingDatum.References = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Reference>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].References.Count : pathfindingDatum.References.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.References.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].References[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Reference>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.Bsp2dNodes.Address.Offset;
                                }
                                pathfindingDatum.Bsp2dNodes = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Bsp2dNode>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Bsp2dNodes.Count : pathfindingDatum.Bsp2dNodes.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.Bsp2dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].Bsp2dNodes[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Bsp2dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.Vertices.Address.Offset;
                                }
                                pathfindingDatum.Vertices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Vertex>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Vertices.Count : pathfindingDatum.Vertices.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.Vertices.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].Vertices[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Vertex>(dataContext));
                                }

                                for (var objRefIdx = 0; objRefIdx < pathfindingDatum.ObjectReferences.Count; objRefIdx++)
                                {
                                    for (var bspRefIdx = 0; bspRefIdx < pathfindingDatum.ObjectReferences[objRefIdx].Bsps.Count; bspRefIdx++)
                                    {
                                        var bspRef = pathfindingDatum.ObjectReferences[objRefIdx].Bsps[bspRefIdx];

                                        StreamUtil.Align(dataStream, 0x4);
                                        if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                        {
                                            blamResourceStream.Position = bspRef.Bsp2dRefs.Address.Offset;
                                        }
                                        bspRef.Bsp2dRefs.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);

                                        for (var bsp2dRefIdx = 0; bsp2dRefIdx < bspRef.Bsp2dRefs.Count; bsp2dRefIdx++)
                                        {
                                            var bsp2dRef = BlamCache.Version < CacheVersion.Halo3ODST ?
                                                           bsp.PathfindingData[0].ObjectReferences[objRefIdx].Bsps[bspRefIdx].Bsp2dRefs[bsp2dRefIdx] :
                                                           BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.ObjectReference.BspReference.Bsp2dRef>(dataContext);

                                            CacheContext.Serializer.Serialize(dataContext, bsp2dRef);
                                        }
                                    }
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset;
                                }
                                pathfindingDatum.PathfindingHints = new TagBlock <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].PathfindingHints.Count : pathfindingDatum.PathfindingHints.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++)
                                {
                                    var hint = BlamCache.Version < CacheVersion.Halo3ODST ?
                                               bsp.PathfindingData[0].PathfindingHints[i] :
                                               BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>(dataContext);

                                    if (BlamCache.Version < CacheVersion.Halo3ODST &&
                                        (hint.HintType == JumpLink || hint.HintType == WallJumpLink))
                                    {
                                        hint.Data[3] = (hint.Data[3] & ~ushort.MaxValue) | ((hint.Data[2] >> 16) & ushort.MaxValue);
                                        hint.Data[2] = (hint.Data[2] & ~(ushort.MaxValue << 16));                     //remove old landing sector
                                        hint.Data[2] = (hint.Data[2] | ((hint.Data[2] & (byte.MaxValue << 8)) << 8)); //move jump height flags
                                        hint.Data[2] = (hint.Data[2] & ~(byte.MaxValue << 8));                        //remove old flags
                                    }

                                    CacheContext.Serializer.Serialize(dataContext, hint);
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.InstancedGeometryReferences.Address.Offset;
                                }
                                pathfindingDatum.InstancedGeometryReferences = new TagBlock <ScenarioStructureBsp.PathfindingDatum.InstancedGeometryReference>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].InstancedGeometryReferences.Count : pathfindingDatum.InstancedGeometryReferences.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.InstancedGeometryReferences.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].InstancedGeometryReferences[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.InstancedGeometryReference>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.GiantPathfinding.Address.Offset;
                                }
                                pathfindingDatum.GiantPathfinding = new TagBlock <ScenarioStructureBsp.PathfindingDatum.GiantPathfindingBlock>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].GiantPathfinding.Count : pathfindingDatum.GiantPathfinding.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.GiantPathfinding.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].GiantPathfinding[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.GiantPathfindingBlock>(dataContext));
                                }

                                for (var unk2Idx = 0; unk2Idx < pathfindingDatum.Seams.Count; unk2Idx++)
                                {
                                    var unknown2 = pathfindingDatum.Seams[unk2Idx];

                                    StreamUtil.Align(dataStream, 0x4);

                                    if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                    {
                                        blamResourceStream.Position = unknown2.LinkIndices.Address.Offset;
                                    }

                                    unknown2.LinkIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>(
                                        (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Seams[unk2Idx].LinkIndices.Count : unknown2.LinkIndices.Count),
                                        new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));

                                    for (var unkIdx = 0; unkIdx < unknown2.LinkIndices.Count; unkIdx++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext,
                                                                          BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                          bsp.PathfindingData[0].Seams[unk2Idx].LinkIndices[unkIdx] :
                                                                          BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>(dataContext));
                                    }
                                }

                                for (var unk3Idx = 0; unk3Idx < pathfindingDatum.JumpSeams.Count; unk3Idx++)
                                {
                                    var unknown3 = pathfindingDatum.JumpSeams[unk3Idx];

                                    StreamUtil.Align(dataStream, 0x4);

                                    if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                    {
                                        blamResourceStream.Position = unknown3.JumpIndices.Address.Offset;
                                    }

                                    unknown3.JumpIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>(
                                        (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].JumpSeams[unk3Idx].JumpIndices.Count : unknown3.JumpIndices.Count),
                                        new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));

                                    for (var unk4Idx = 0; unk4Idx < unknown3.JumpIndices.Count; unk4Idx++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext,
                                                                          BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                          bsp.PathfindingData[0].JumpSeams[unk3Idx].JumpIndices[unk4Idx] :
                                                                          BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>(dataContext));
                                    }
                                }

                                StreamUtil.Align(dataStream, 0x4);
                                if (BlamCache.Version >= CacheVersion.Halo3ODST)
                                {
                                    blamResourceStream.Position = pathfindingDatum.Doors.Address.Offset;
                                }
                                pathfindingDatum.Doors = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Door>(
                                    (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Doors.Count : pathfindingDatum.Doors.Count),
                                    new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position));
                                for (var i = 0; i < pathfindingDatum.Doors.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext,
                                                                      BlamCache.Version < CacheVersion.Halo3ODST ?
                                                                      bsp.PathfindingData[0].Doors[i] :
                                                                      BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Door>(dataContext));
                                }
                            }

                            CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.PathfindingResource), resourceDefinition);
                            resourceWriter.BaseStream.Position = 0;
                            dataStream.Position = 0;

                            bsp.PathfindingResource.ChangeLocation(ResourceLocation.ResourcesB);
                            var resource = bsp.PathfindingResource;

                            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(ResourceLocation.ResourcesB);

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

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

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

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

            if (BlamCache.Version < CacheVersion.Halo3ODST)
            {
                bsp.SurfacePlanes.Clear();
                bsp.Planes.Clear();
                bsp.EdgeToSeams.Clear();
                bsp.PathfindingData.Clear();
            }

            return(bsp.PathfindingResource);
        }
Ejemplo n.º 17
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);
        }
Ejemplo n.º 18
0
        private PageableResource ConvertStructureBspTagResources(ScenarioStructureBsp bsp, Dictionary <ResourceLocation, Stream> resourceStreams)
        {
            //
            // Set up ElDorado resource reference
            //

            bsp.CollisionBspResource = new PageableResource
            {
                Page = new RawPage
                {
                    Index = -1
                },
                Resource = new TagResourceGen3
                {
                    ResourceType             = TagResourceTypeGen3.Collision,
                    DefinitionData           = new byte[0x30],
                    DefinitionAddress        = new CacheResourceAddress(CacheResourceAddressType.Definition, 0),
                    ResourceFixups           = new List <TagResourceGen3.ResourceFixup>(),
                    ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(),
                    Unknown2 = 1
                }
            };

            //
            // Port Blam resource definition
            //

            var resourceEntry = BlamCache.ResourceGestalt.TagResources[bsp.ZoneAssetIndex3.Index];

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

            StructureBspTagResources resourceDefinition = null;

            using (var definitionStream = new MemoryStream(bsp.CollisionBspResource.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)
                        {
                            var newFixup = new TagResourceGen3.ResourceFixup
                            {
                                BlockOffset = (uint)fixup.BlockOffset,
                                Address     = new CacheResourceAddress(
                                    fixup.Type == 4 ?
                                    CacheResourceAddressType.Resource :
                                    CacheResourceAddressType.Definition,
                                    fixup.Offset)
                            };

                            definitionStream.Position = newFixup.BlockOffset;
                            definitionWriter.Write(newFixup.Address.Value);

                            bsp.CollisionBspResource.Resource.ResourceFixups.Add(newFixup);
                        }

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

                        definitionStream.Position = bsp.CollisionBspResource.Resource.DefinitionAddress.Offset;
                        resourceDefinition        = BlamCache.Deserializer.Deserialize <StructureBspTagResources>(dataContext);

                        //
                        // Apply game-specific fixes to the resource definition
                        //

                        if (BlamCache.Version < CacheVersion.Halo3ODST)
                        {
                            resourceDefinition.LargeCollisionBsps = new List <StructureBspTagResources.LargeCollisionBspBlock>();
                            resourceDefinition.HavokData          = new List <StructureBspTagResources.HavokDatum>();
                        }

                        foreach (var instance in resourceDefinition.InstancedGeometry)
                        {
                            instance.Unknown5 = new TagBlock <StructureBspTagResources.InstancedGeometryBlock.Unknown4Block>();
                            instance.Unknown2 = new TagBlock <StructureBspTagResources.InstancedGeometryBlock.Unknown2Block>();
                        }
                    }

            //
            // Load Blam resource data
            //

            var resourceData = BlamCache.GetRawFromID(bsp.ZoneAssetIndex3);

            if (resourceData == null)
            {
                CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.CollisionBspResource), resourceDefinition);
                return(bsp.CollisionBspResource);
            }

            //
            // Port Blam resource to ElDorado resource cache
            //

            using (var blamResourceStream = resourceData != null ? new MemoryStream(resourceData) : new MemoryStream())
                using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian))
                    using (var dataStream = new MemoryStream())
                        using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian))
                        {
                            var dataContext = new DataSerializationContext(resourceReader, resourceWriter);

                            foreach (var collisionBsp in resourceDefinition.CollisionBsps)
                            {
                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position     = collisionBsp.Bsp3dNodes.Address.Offset;
                                collisionBsp.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Bsp3dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position = collisionBsp.Planes.Address.Offset;
                                collisionBsp.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Planes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position = collisionBsp.Leaves.Address.Offset;
                                collisionBsp.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Leaves.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position          = collisionBsp.Bsp2dReferences.Address.Offset;
                                collisionBsp.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Bsp2dReferences.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position     = collisionBsp.Bsp2dNodes.Address.Offset;
                                collisionBsp.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Bsp2dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position   = collisionBsp.Surfaces.Address.Offset;
                                collisionBsp.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Surfaces.Count; i++)
                                {
                                    var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext);
                                    CacheContext.Serializer.Serialize(dataContext, surface);
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position = collisionBsp.Edges.Address.Offset;
                                collisionBsp.Edges.Address  = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Edges.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position   = collisionBsp.Vertices.Address.Offset;
                                collisionBsp.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < collisionBsp.Vertices.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext));
                                }
                            }

                            foreach (var largeCollisionBsp in resourceDefinition.LargeCollisionBsps)
                            {
                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position          = largeCollisionBsp.Bsp3dNodes.Address.Offset;
                                largeCollisionBsp.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Bsp3dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position      = largeCollisionBsp.Planes.Address.Offset;
                                largeCollisionBsp.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Planes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position      = largeCollisionBsp.Leaves.Address.Offset;
                                largeCollisionBsp.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Leaves.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position = largeCollisionBsp.Bsp2dReferences.Address.Offset;
                                largeCollisionBsp.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Bsp2dReferences.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position          = largeCollisionBsp.Bsp2dNodes.Address.Offset;
                                largeCollisionBsp.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Bsp2dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position        = largeCollisionBsp.Surfaces.Address.Offset;
                                largeCollisionBsp.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Surfaces.Count; i++)
                                {
                                    var surface = BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Surface>(dataContext);
                                    // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material);
                                    CacheContext.Serializer.Serialize(dataContext, surface);
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position     = largeCollisionBsp.Edges.Address.Offset;
                                largeCollisionBsp.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Edges.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Edge>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position        = largeCollisionBsp.Vertices.Address.Offset;
                                largeCollisionBsp.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < largeCollisionBsp.Vertices.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Vertex>(dataContext));
                                }
                            }

                            foreach (var instance in resourceDefinition.InstancedGeometry)
                            {
                                StreamUtil.Align(dataStream, 0x10); // 0x8 > 0x10
                                blamResourceStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset;
                                instance.CollisionInfo.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position           = instance.CollisionInfo.Planes.Address.Offset;
                                instance.CollisionInfo.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position           = instance.CollisionInfo.Leaves.Address.Offset;
                                instance.CollisionInfo.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset;
                                instance.CollisionInfo.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset;
                                instance.CollisionInfo.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position             = instance.CollisionInfo.Surfaces.Address.Offset;
                                instance.CollisionInfo.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++)
                                {
                                    var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext);
                                    // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material);
                                    CacheContext.Serializer.Serialize(dataContext, surface);
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position          = instance.CollisionInfo.Edges.Address.Offset;
                                instance.CollisionInfo.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext));
                                }

                                StreamUtil.Align(dataStream, 0x10);
                                blamResourceStream.Position             = instance.CollisionInfo.Vertices.Address.Offset;
                                instance.CollisionInfo.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext));
                                }

                                foreach (var collisionGeometry in instance.CollisionGeometries)
                                {
                                    StreamUtil.Align(dataStream, 0x10); // 0x8 > 0x10
                                    blamResourceStream.Position          = collisionGeometry.Bsp3dNodes.Address.Offset;
                                    collisionGeometry.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Bsp3dNodes.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position      = collisionGeometry.Planes.Address.Offset;
                                    collisionGeometry.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Planes.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position      = collisionGeometry.Leaves.Address.Offset;
                                    collisionGeometry.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Leaves.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position = collisionGeometry.Bsp2dReferences.Address.Offset;
                                    collisionGeometry.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Bsp2dReferences.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position          = collisionGeometry.Bsp2dNodes.Address.Offset;
                                    collisionGeometry.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Bsp2dNodes.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position        = collisionGeometry.Surfaces.Address.Offset;
                                    collisionGeometry.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Surfaces.Count; i++)
                                    {
                                        var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext);
                                        // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material);
                                        CacheContext.Serializer.Serialize(dataContext, surface);
                                    }

                                    StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                    blamResourceStream.Position     = collisionGeometry.Edges.Address.Offset;
                                    collisionGeometry.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Edges.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext));
                                    }

                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position        = collisionGeometry.Vertices.Address.Offset;
                                    collisionGeometry.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    for (var i = 0; i < collisionGeometry.Vertices.Count; i++)
                                    {
                                        CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext));
                                    }
                                }

                                foreach (var moppCode in instance.BspPhysics)
                                {
                                    StreamUtil.Align(dataStream, 0x10);
                                    blamResourceStream.Position = moppCode.Data.Address.Offset;
                                    moppCode.Data.Address       = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                    var moppData = resourceReader.ReadBytes(moppCode.Data.Count).Select(i => new CollisionMoppCode.Datum {
                                        Value = i
                                    }).ToList();
                                    if (BlamCache.Version < CacheVersion.Halo3ODST)
                                    {
                                        moppData = ConvertCollisionMoppData(moppData);
                                    }
                                    resourceWriter.Write(moppData.Select(i => i.Value).ToArray());
                                }

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position = instance.Unknown1.Address.Offset;
                                instance.Unknown1.Address   = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.Unknown1.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.InstancedGeometryBlock.Unknown1Block>(dataContext));
                                }

                                /*
                                 * StreamUtil.Align(dataStream, 0x4); // 0x4 > 0x10
                                 * blamResourceStream.Position = instance.Unknown2.Address.Offset;
                                 * instance.Unknown2.Address = new CacheAddress(CacheAddressType.Resource, (int)dataStream.Position);
                                 * for (var i = 0; i < instance.Unknown2.Count; i++)
                                 * {
                                 *  var element = BlamCache.Deserializer.Deserialize<StructureBspTagResources.InstancedGeometryBlock.Unknown2Block>(dataContext);
                                 *  if (BlamCache.Version <= CacheVersion.Halo3ODST)
                                 *  {
                                 *      element.Unknown1 = element.Unknown1_H3;
                                 *      element.Unknown2 = element.Unknown2_H3;
                                 *  }
                                 *  CacheContext.Serializer.Serialize(dataContext, element);
                                 */

                                StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10
                                blamResourceStream.Position = instance.Unknown3.Address.Offset;
                                instance.Unknown3.Address   = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position);
                                for (var i = 0; i < instance.Unknown3.Count; i++)
                                {
                                    CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.InstancedGeometryBlock.Unknown3Block>(dataContext));
                                }
                            }

                            dataStream.Position = 0;

                            CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.CollisionBspResource), resourceDefinition);

                            bsp.CollisionBspResource.ChangeLocation(ResourceLocation.ResourcesB);
                            var resource = bsp.CollisionBspResource;

                            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(ResourceLocation.ResourcesB);

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

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

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

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

            return(bsp.CollisionBspResource);
        }
Ejemplo n.º 19
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);
        }
Ejemplo n.º 20
0
        public override object Execute(List <string> args)
        {
            if (args.Count < 1 || args.Count > 5)
            {
                return(false);
            }

            bool   promptTags = false;
            bool   promptMaps = false;
            int?   fromIndex  = null;
            int?   toIndex    = null;
            string forCache   = null;

            while (args.Count != 1)
            {
                switch (args[0].ToLower())
                {
                case "prompttags":
                    promptTags = true;
                    args.RemoveRange(0, 1);
                    break;

                case "promptmaps":
                    promptMaps = true;
                    args.RemoveRange(0, 1);
                    break;

                case "from:":
                    if (CacheContext.TryGetTag(args[1], out var fromInstance) && fromInstance != null)
                    {
                        fromIndex = fromInstance.Index;
                    }
                    args.RemoveRange(0, 2);
                    break;

                case "to:":
                    if (CacheContext.TryGetTag(args[1], out var toInstance) && toInstance != null)
                    {
                        toIndex = toInstance.Index;
                    }
                    args.RemoveRange(0, 2);
                    break;

                case "forCache:":
                    forCache = args[1];
                    args.RemoveRange(0, 2);
                    break;

                default:
                    throw new ArgumentException(args[0]);
                }
            }

            if (!promptTags && !promptMaps && !fromIndex.HasValue && !toIndex.HasValue)
            {
                promptTags = true;
                promptMaps = true;
            }

            if (fromIndex.HasValue && !toIndex.HasValue)
            {
                toIndex = CacheContext.TagCache.Index.NonNull().Last().Index;
            }

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

            var    tagIndices = new HashSet <int>();
            var    mapFiles   = new HashSet <string>();
            string line       = null;

            if (fromIndex.HasValue && toIndex.HasValue)
            {
                foreach (var entry in Enumerable.Range(fromIndex.Value, toIndex.Value - fromIndex.Value))
                {
                    if (!tagIndices.Contains(entry) && CacheContext.GetTag(entry) != null)
                    {
                        tagIndices.Add(entry);
                    }
                }
            }

            if (promptTags)
            {
                Console.WriteLine("Please specify the tags to be used (enter an empty line to finish):");

                while ((line = Console.ReadLine().TrimStart().TrimEnd()) != "")
                {
                    if (CacheContext.TryGetTag(line, out var instance) && instance != null && !tagIndices.Contains(instance.Index))
                    {
                        tagIndices.Add(instance.Index);
                    }
                }
            }

            if (promptMaps)
            {
                Console.WriteLine("Please specify the .map files to be used (enter an empty line to finish):");

                while ((line = Console.ReadLine().TrimStart().TrimEnd()) != "")
                {
                    var mapFile = new FileInfo(line);

                    if (mapFile.Exists && mapFile.Extension == ".map" && !mapFiles.Contains(mapFile.FullName))
                    {
                        mapFiles.Add(mapFile.FullName);
                    }
                }
            }

            using (var srcTagStream = CacheContext.OpenTagCacheRead())
                using (var destTagsStream = new MemoryStream())
                    using (var destResourceStream = new MemoryStream())
                    {
                        var destTagCache      = CacheContext.CreateTagCache(destTagsStream);
                        var destResourceCache = CacheContext.CreateResourceCache(destResourceStream);

                        var resourceIndices = new Dictionary <ResourceLocation, Dictionary <int, PageableResource> >
                        {
                            [ResourceLocation.Audio]        = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.Lightmaps]    = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.Mods]         = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.RenderModels] = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.Resources]    = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.ResourcesB]   = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.Textures]     = new Dictionary <int, PageableResource>(),
                            [ResourceLocation.TexturesB]    = new Dictionary <int, PageableResource>()
                        };

                        var srcResourceCaches  = new Dictionary <ResourceLocation, ResourceCache>();
                        var srcResourceStreams = new Dictionary <ResourceLocation, Stream>();

                        foreach (var value in Enum.GetValues(typeof(ResourceLocation)))
                        {
                            ResourceCache resourceCache = null;
                            var           location      = (ResourceLocation)value;

                            if (location == ResourceLocation.None)
                            {
                                continue;
                            }

                            try
                            {
                                resourceCache = CacheContext.GetResourceCache(location);
                            }
                            catch (FileNotFoundException)
                            {
                                continue;
                            }

                            srcResourceCaches[location]  = resourceCache;
                            srcResourceStreams[location] = CacheContext.OpenResourceCacheRead(location);
                        }

                        for (var tagIndex = 0; tagIndex < CacheContext.TagCache.Index.Count; tagIndex++)
                        {
                            if (!tagIndices.Contains(tagIndex))
                            {
                                destTagCache.AllocateTag();
                                continue;
                            }

                            var srcTag  = CacheContext.GetTag(tagIndex);
                            var destTag = destTagCache.AllocateTag(srcTag.Group, srcTag.Name);

                            using (var tagDataStream = new MemoryStream(CacheContext.TagCache.ExtractTagRaw(srcTagStream, srcTag)))
                                using (var tagDataReader = new EndianReader(tagDataStream))
                                    using (var tagDataWriter = new EndianWriter(tagDataStream))
                                    {
                                        var resourcePointerOffsets = new HashSet <uint>();

                                        foreach (var resourcePointerOffset in srcTag.ResourcePointerOffsets)
                                        {
                                            if (resourcePointerOffset == 0 || resourcePointerOffsets.Contains(resourcePointerOffset))
                                            {
                                                continue;
                                            }

                                            resourcePointerOffsets.Add(resourcePointerOffset);

                                            tagDataStream.Position = resourcePointerOffset;
                                            var resourcePointer = tagDataReader.ReadUInt32();

                                            if (resourcePointer == 0)
                                            {
                                                continue;
                                            }

                                            var resourceOffset = srcTag.PointerToOffset(resourcePointer);

                                            tagDataReader.BaseStream.Position = resourceOffset + 2;
                                            var locationFlags = (OldRawPageFlags)tagDataReader.ReadByte();

                                            var resourceLocation =
                                                locationFlags.HasFlag(OldRawPageFlags.InAudio) ?
                                                ResourceLocation.Audio :
                                                locationFlags.HasFlag(OldRawPageFlags.InResources) ?
                                                ResourceLocation.Resources :
                                                locationFlags.HasFlag(OldRawPageFlags.InResourcesB) ?
                                                ResourceLocation.ResourcesB :
                                                locationFlags.HasFlag(OldRawPageFlags.InTextures) ?
                                                ResourceLocation.Textures :
                                                locationFlags.HasFlag(OldRawPageFlags.InTexturesB) ?
                                                ResourceLocation.TexturesB :
                                                ResourceLocation.ResourcesB;

                                            tagDataReader.BaseStream.Position = resourceOffset + 4;
                                            var resourceIndex  = tagDataReader.ReadInt32();
                                            var compressedSize = tagDataReader.ReadUInt32();

                                            if (resourceIndex == -1)
                                            {
                                                continue;
                                            }

                                            PageableResource pageable = null;

                                            if (resourceIndices[resourceLocation].ContainsKey(resourceIndex))
                                            {
                                                pageable = resourceIndices[resourceLocation][resourceIndex];
                                            }
                                            else
                                            {
                                                var newResourceIndex = destResourceCache.AddRaw(
                                                    destResourceStream,
                                                    srcResourceCaches[resourceLocation].ExtractRaw(
                                                        srcResourceStreams[resourceLocation],
                                                        resourceIndex,
                                                        compressedSize));

                                                pageable = resourceIndices[resourceLocation][resourceIndex] = new PageableResource
                                                {
                                                    Page = new RawPage
                                                    {
                                                        OldFlags = OldRawPageFlags.InMods,
                                                        Index    = newResourceIndex
                                                    }
                                                };
                                            }

                                            tagDataReader.BaseStream.Position = resourceOffset + 2;
                                            tagDataWriter.Write((byte)pageable.Page.OldFlags);

                                            tagDataReader.BaseStream.Position = resourceOffset + 4;
                                            tagDataWriter.Write(pageable.Page.Index);
                                        }

                                        destTagCache.SetTagDataRaw(destTagsStream, destTag, tagDataStream.ToArray());
                                    }
                        }

                        destTagCache.UpdateTagOffsets(new BinaryWriter(destTagsStream, Encoding.Default, true));

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

                        using (var packageStream = packageFile.Create())
                            using (var writer = new BinaryWriter(packageStream))
                            {
                                //
                                // reserve header space
                                //

                                writer.Write(new byte[48]);

                                //
                                // write tag cache
                                //

                                var tagCacheOffset = (uint)packageStream.Position;

                                destTagsStream.Position = 0;
                                StreamUtil.Copy(destTagsStream, packageStream, (int)destTagsStream.Length);
                                StreamUtil.Align(packageStream, 4);

                                //
                                // write tag names table
                                //

                                var names = new Dictionary <int, string>();

                                foreach (var entry in destTagCache.Index)
                                {
                                    if (entry != null && entry.Name != null)
                                    {
                                        names[entry.Index] = entry.Name;
                                    }
                                }

                                var tagNamesTableOffset = (uint)packageStream.Position;
                                var tagNamesTableCount  = names.Count;

                                foreach (var entry in names)
                                {
                                    writer.Write(entry.Key);

                                    var chars = new char[256];

                                    for (var i = 0; i < entry.Value.Length; i++)
                                    {
                                        chars[i] = entry.Value[i];
                                    }

                                    writer.Write(chars);
                                }

                                //
                                // write resource cache
                                //

                                var resourceCacheOffset = (uint)packageStream.Position;

                                destResourceStream.Position = 0;
                                StreamUtil.Copy(destResourceStream, packageStream, (int)destResourceStream.Length);
                                StreamUtil.Align(packageStream, 4);

                                //
                                // write map file table
                                //

                                var mapFileTableOffset = (uint)packageStream.Position;
                                var mapFileTableCount  = mapFiles.Count;

                                var mapFileInfo = new List <(uint, uint)>();
                                writer.Write(new byte[8 * mapFileTableCount]);

                                foreach (var entry in mapFiles)
                                {
                                    var mapFile = new FileInfo(entry);

                                    using (var mapFileStream = mapFile.OpenRead())
                                    {
                                        mapFileInfo.Add(((uint)packageStream.Position, (uint)mapFileStream.Length));
                                        StreamUtil.Copy(mapFileStream, packageStream, (int)mapFileStream.Length);
                                        StreamUtil.Align(packageStream, 4);
                                    }
                                }

                                packageStream.Position = mapFileTableOffset;

                                foreach (var entry in mapFileInfo)
                                {
                                    writer.Write(entry.Item1);
                                    writer.Write(entry.Item2);
                                }

                                //
                                // calculate package sha1
                                //

                                packageStream.Position = 48;
                                var packageSha1 = new SHA1Managed().ComputeHash(packageStream);

                                //
                                // update package header
                                //

                                packageStream.Position = 0;

                                writer.Write(new Tag("mod!"));
                                writer.Write(packageSha1);
                                writer.Write(tagCacheOffset);
                                writer.Write(tagNamesTableCount == 0 ? 0 : tagNamesTableOffset);
                                writer.Write(tagNamesTableCount);
                                writer.Write(resourceCacheOffset);
                                writer.Write(mapFileTableCount == 0 ? 0 : mapFileTableOffset);
                                writer.Write(mapFileTableCount);
                            }
                    }

            return(true);
        }
Ejemplo n.º 21
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);
        }
Ejemplo n.º 22
0
 private void WriteFontFileSection(EndianWriter writer)
 {
     FontPackage.Position = 0;
     StreamUtil.Copy(FontPackage, writer.BaseStream, (int)FontPackage.Length);
     StreamUtil.Align(writer.BaseStream, 4);
 }
Ejemplo n.º 23
0
 private void WriteCampaignFileSection(EndianWriter writer)
 {
     CampaignFileStream.Position = 0;
     StreamUtil.Copy(CampaignFileStream, writer.BaseStream, (int)CampaignFileStream.Length);
     StreamUtil.Align(writer.BaseStream, 4);
 }
Ejemplo n.º 24
0
 private void WriteTagsSection(EndianWriter writer)
 {
     TagsStream.Position = 0;
     StreamUtil.Copy(TagsStream, writer.BaseStream, (int)TagsStream.Length);
     StreamUtil.Align(writer.BaseStream, 4);
 }
Ejemplo n.º 25
0
 private void WriteResourcesSection(EndianWriter writer)
 {
     ResourcesStream.Position = 0;
     StreamUtil.Copy(ResourcesStream, writer.BaseStream, ResourcesStream.Length);
     StreamUtil.Align(writer.BaseStream, 4);
 }
Ejemplo n.º 26
0
        public override void SerializeTagBlock(CacheVersion version, ISerializationContext context, MemoryStream tagStream, IDataBlock block, object list, Type listType, TagFieldAttribute valueInfo)
        {
            if (context.GetType() != typeof(ResourceDefinitionSerializationContext))
            {
                throw new Exception($"Invalid context type given resource deserialization");
            }

            if (block.GetType() != typeof(ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock))
            {
                throw new Exception($"Invalid block type given resource deserialization");
            }

            var resourceBlock   = block as ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock;
            var resourceContext = context as ResourceDefinitionSerializationContext;

            var writer = block.Writer;
            var count  = 0;

            if (list != null)
            {
                // Use reflection to get the number of elements in the list
                var countProperty = listType.GetProperty("Count");
                count = (int)countProperty.GetValue(list);
            }
            if (count == 0)
            {
                writer.Write(0);
                writer.Write(0);
                writer.Write(0);
                return;
            }

            var elementType = listType.GenericTypeArguments[0];

            CacheAddressType addressType = (CacheAddressType)listType.GetField("AddressType").GetValue(list);

            // Serialize each value in the list to a data block
            var resourceBlock2 = (ResourceDefinitionSerializationContext.ResourceDefinitionDataBlock)resourceContext.CreateBlock();

            resourceBlock2.BlockType = addressType;
            var addressTypeStream = (MemoryStream)resourceContext.GetWriter(addressType).BaseStream;

            var enumerableList = (System.Collections.IEnumerable)list;

            foreach (var val in enumerableList)
            {
                SerializeValue(version, resourceContext, tagStream, resourceBlock2, val, null, elementType);
            }

            // Ensure the block is aligned correctly
            var align = 0x10;

            StreamUtil.Align(resourceBlock2.Stream, align);

            // Finalize the block and write the tag block reference using a cache address
            var offset = resourceBlock2.Finalize(addressTypeStream);    // offset of the data in the tagblock on the actual stream
            //var blockOffset = addressTypeStream.Position;               // no need to fix that particular fixup later
            var address = new CacheAddress(addressType, (int)offset);

            var resourceFixup = new ResourceFixupLocation
            {
                Address     = address,
                BlockOffset = (uint)writer.BaseStream.Position + 0x4
            };

            foreach (var fixup in resourceBlock2.FixupLocations)
            {
                fixup.BlockOffset += offset;
                resourceContext.FixupLocations.Add(fixup);
            }

            foreach (var location in resourceBlock2.InteropLocations)
            {
                location.Address = new CacheAddress(location.Address.Type, (int)(location.Address.Offset + offset));
                resourceContext.InteropLocations.Add(location);
            }

            resourceBlock.FixupLocations.Add(resourceFixup);

            writer.Write(count);
            writer.Write(address.Value);    // write address as 0, we use the fixups
            writer.Write(0);
        }