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; }
/// <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); }
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); } }
/// <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); }
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); }
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); }
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); }
/// <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); }
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); }
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); }
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())); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
private void WriteFontFileSection(EndianWriter writer) { FontPackage.Position = 0; StreamUtil.Copy(FontPackage, writer.BaseStream, (int)FontPackage.Length); StreamUtil.Align(writer.BaseStream, 4); }
private void WriteCampaignFileSection(EndianWriter writer) { CampaignFileStream.Position = 0; StreamUtil.Copy(CampaignFileStream, writer.BaseStream, (int)CampaignFileStream.Length); StreamUtil.Align(writer.BaseStream, 4); }
private void WriteTagsSection(EndianWriter writer) { TagsStream.Position = 0; StreamUtil.Copy(TagsStream, writer.BaseStream, (int)TagsStream.Length); StreamUtil.Align(writer.BaseStream, 4); }
private void WriteResourcesSection(EndianWriter writer) { ResourcesStream.Position = 0; StreamUtil.Copy(ResourcesStream, writer.BaseStream, ResourcesStream.Length); StreamUtil.Align(writer.BaseStream, 4); }
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); }