public void Read(EndianReader reader) { EndianFormat = DetectEndianFormat(reader); MapVersion = GetMapFileVersion(reader); Version = DetectCacheVersion(reader); var deserializer = new TagDeserializer(Version); reader.SeekTo(0); var dataContext = new DataSerializationContext(reader); Header = (CacheFileHeader)deserializer.Deserialize(dataContext, typeof(CacheFileHeader)); // temporary code until map file format cleanup if (MapVersion == CacheFileVersion.HaloOnline) { var mapFileHeaderSize = (int)TagStructure.GetTagStructureInfo(typeof(CacheFileHeader), Version).TotalSize; // Seek to the blf reader.SeekTo(mapFileHeaderSize); // Read blf MapFileBlf = new Blf(Version); if (!MapFileBlf.Read(reader)) { MapFileBlf = null; } } }
/// <summary> /// Verifies if the stream points to a valid blf start chunk and set the endian format. /// </summary> /// <param name="reader"></param> /// <returns></returns> private bool IsValid(EndianReader reader) { var position = reader.Position; try { Format = FindChunkEndianFormat(reader); reader.Format = Format; } catch (Exception e) { Console.WriteLine($"BLF file is invalid: {e.Message}"); return(false); } var deserializer = new TagDeserializer(Version); var dataContext = new DataSerializationContext(reader); var header = (BlfChunkHeader)deserializer.Deserialize(dataContext, typeof(BlfChunkHeader)); reader.SeekTo(position); if (header.Signature == "_blf") { return(true); } else { return(false); } }
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 Load(FileInfo file) { if (!file.Exists) { throw new FileNotFoundException(file.FullName); } if (file.Length < typeof(ModPackageHeader).GetSize()) { throw new FormatException(file.FullName); } using (var stream = file.OpenRead()) using (var reader = new EndianReader(stream, leaveOpen: true)) { var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(CacheVersion.HaloOnline106708); Header = deserializer.Deserialize <ModPackageHeaderExtended>(dataContext); ReadMetadataSection(reader, dataContext, deserializer); ReadTagsSection(reader); ReadTagNamesSection(reader, dataContext, deserializer); ReadResourcesSection(reader); ReadMapFilesSection(reader); ReadCampaignFileSection(reader); Tags = new TagCache(TagsStream, TagNames); Resources = new ResourceCache(ResourcesStream); } }
private static BitmapTextureInterleavedInteropResource GetInterleavedResourceDefinition(CacheFile cache, DatumIndex handle) { var resourceEntry = cache.ResourceGestalt.TagResources[handle.Index]; var definitionData = cache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(); BitmapTextureInterleavedInteropResource definition; using (var definitionStream = new MemoryStream(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 CacheAddress(CacheAddressType.Definition, fixup.Offset) }; definitionStream.Position = newFixup.BlockOffset; definitionWriter.Write(newFixup.Address.Value); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheAddressType.Definition); definitionStream.Position = resourceEntry.DefinitionAddress.Offset; definition = cache.Deserializer.Deserialize <BitmapTextureInterleavedInteropResource>(dataContext); } return(definition); }
private void Read(Stream stream) { // don't use using{}, we want to maintain the stream open. reader/writers will automatically close the stream when done in an using. var reader = new EndianReader(stream, EndianFormat.LittleEndian); stream.Position = 0; var addresses = new List <uint>(); var sizes = new List <uint>(); var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(Version); Header = deserializer.Deserialize <ResourceCacheHaloOnlineHeader>(dataContext); reader.SeekTo(Header.ResourceTableOffset); // read all resource offsets if (Header.ResourceCount == 0) { return; } for (var i = 0; i < Header.ResourceCount; i++) { var address = reader.ReadUInt32(); if (!addresses.Contains(address) && (address != uint.MaxValue)) { addresses.Add(address); } Resources.Add(new Resource { Offset = address }); } // compute chunk sizes addresses.Sort((a, b) => a.CompareTo(b)); for (var i = 0; i < addresses.Count - 1; i++) { sizes.Add(addresses[i + 1] - addresses[i]); } sizes.Add(Header.ResourceTableOffset - addresses.Last()); foreach (var resource in Resources) { if (resource.Offset == uint.MaxValue) { continue; } resource.ChunkSize = sizes[addresses.IndexOf(resource.Offset)]; } }
private void ReadMetadataSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.Metadata); if (!GoToSectionHeaderOffset(reader, section)) { return; } Metadata = deserializer.Deserialize <ModPackageMetadata>(context); }
public void SerializeToFile(EndianWriter writer) { var context = new DataSerializationContext(writer); var serializer = new TagSerializer(CacheVersion.HaloOnline106708); writer.BaseStream.Position = Constants.HeaderSize; context.PointerOffset = Constants.HeaderSize; serializer.Serialize(context, this); Header.Offset = (int)context.MainStructOffset + Constants.HeaderSize; writer.BaseStream.Position = 0x0; serializer.Serialize(context, Header); }
public TagCacheGen1(EndianReader reader, MapFile mapFile) { var tagDataSectionOffset = mapFile.Header.TagsHeaderAddress32; reader.SeekTo(tagDataSectionOffset); var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(mapFile.Version); Header = deserializer.Deserialize <TagCacheGen1Header>(dataContext); if (mapFile.Version == CacheVersion.HaloXbox) { BaseTagAddress = 0x803A6000; } else { BaseTagAddress = 0x40440000; } // // Read all tags offsets are all broken, need some proper look // reader.SeekTo(Header.CachedTagArrayAddress - BaseTagAddress + tagDataSectionOffset); for (int i = 0; i < Header.TagCount; i++) { var group = new TagGroup() { Tag = new Tag(reader.ReadInt32()), ParentTag = new Tag(reader.ReadInt32()), GrandparentTag = new Tag(reader.ReadInt32()) }; var tagID = reader.ReadUInt32(); var tagPathNameAddress = reader.ReadUInt32(); var currentPos = reader.Position; string name = ""; if (tagPathNameAddress != 0) { reader.SeekTo(tagPathNameAddress - BaseTagAddress + tagDataSectionOffset); name = reader.ReadNullTerminatedString(); reader.SeekTo(currentPos); } var tagDataAddress = reader.ReadUInt32(); var weird2 = reader.ReadUInt32(); var unused = reader.ReadUInt32(); Tags.Add(new CachedTagGen1((int)(tagID & 0xFFFF), tagID, group, tagDataAddress, name)); } }
public void Write(EndianWriter writer) { var dataContext = new DataSerializationContext(writer); var serializer = new TagSerializer(Version, EndianFormat); serializer.Serialize(dataContext, Header); if (Version == CacheVersion.HaloOnline106708) { if (MapFileBlf != null) { MapFileBlf.Write(writer); } } }
private void CreateEmptyResourceCache(Stream stream) { Header = new ResourceCacheHaloOnlineHeader { ResourceTableOffset = 0x20, CreationTime = 0x01D0631BCC92931B }; stream.Position = 0; var writer = new EndianWriter(stream, EndianFormat.LittleEndian); var dataContext = new DataSerializationContext(writer); var serializer = new TagSerializer(CacheVersion.HaloOnline106708); serializer.Serialize(dataContext, Header); stream.Position = 0; }
public TagCacheHaloOnline CreateTagCache(Stream stream) { TagCacheHaloOnlineHeader header = new TagCacheHaloOnlineHeader { TagTableOffset = 0x20, CreationTime = 0x01D0631BCC791704 }; stream.Position = 0; var writer = new EndianWriter(stream, EndianFormat.LittleEndian); var dataContext = new DataSerializationContext(writer); var serializer = new TagSerializer(CacheVersion.HaloOnline106708); serializer.Serialize(dataContext, header); stream.Position = 0; return(new TagCacheHaloOnline(stream, new Dictionary <int, string>())); }
private void ReadTagNamesSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.TagNames); if (!GoToSectionHeaderOffset(reader, section)) { return; } var tagNamesHeader = new GenericSectionEntry(reader); reader.BaseStream.Position = tagNamesHeader.TableOffset; for (int i = 0; i < tagNamesHeader.Count; i++) { var tagNamesEntry = deserializer.Deserialize <ModPackageTagNamesEntry>(context); TagNames.Add(tagNamesEntry.TagIndex, tagNamesEntry.Name); } }
private PhysicsModel ConvertPhysicsModel(CachedTagInstance instance, PhysicsModel phmo) { /* * // Allow syncing of specific tags in MP (hax) * // * * switch (instance.Name) * { * case @"objects\levels\solo\060_floodship\flood_danglers\large_dangler\large_dangler": * case @"objects\levels\solo\060_floodship\flood_danglers\small_dangler\small_dangler": * phmo.Flags &= ~PhysicsModel.PhysicsModelFlags.MakePhysicalChildrenKeyframed; * break; * }*/ // // Fix mopp code array headers for both H3 and ODST // byte[] result = new byte[phmo.MoppCodes.Length]; using (var inputReader = new EndianReader(new MemoryStream(phmo.MoppCodes), BlamCache.Reader.Format)) using (var outputWriter = new EndianWriter(new MemoryStream(result), EndianFormat.LittleEndian)) { var dataContext = new DataSerializationContext(inputReader, outputWriter); for (int i = 0; i < phmo.Mopps.Count; i++) { var header = BlamCache.Deserializer.Deserialize <MoppCode>(dataContext); CacheContext.Serializer.Serialize(dataContext, header); var adjustedDataSize = header.DataSize % 16 == 0 ? header.DataSize : (header.DataSize / 16 + 1) * 16; //Align on 16 bytes. Array.Copy(phmo.MoppCodes, inputReader.Position, result, inputReader.Position, adjustedDataSize); inputReader.SeekTo(inputReader.Position + adjustedDataSize); outputWriter.Seek((int)inputReader.Position, 0); } phmo.MoppCodes = result; } return(phmo); }
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); }
private void WriteTagNamesSection(EndianWriter writer, DataSerializationContext context, TagSerializer serializer) { uint sectionOffset = (uint)writer.BaseStream.Position; GenericSectionEntry tagNameFileEntry = new GenericSectionEntry(TagCacheNames.Count, 0x8); tagNameFileEntry.Write(writer); // make room for table writer.Write(new byte[0x8 * TagCacheNames.Count]); for (int i = 0; i < TagCacheNames.Count; i++) { //prepare tag names var names = new Dictionary <int, string>(); foreach (var entry in TagCaches[i].TagTable) { if (entry != null && entry.Name != null) { names.Add(entry.Index, entry.Name); } } uint offset = (uint)writer.BaseStream.Position; GenericSectionEntry tagNameTable = new GenericSectionEntry(names.Count, offset - sectionOffset + 0x8); tagNameTable.Write(writer); foreach (var entry in names) { var tagNameEntry = new ModPackageTagNamesEntry(entry.Key, entry.Value); serializer.Serialize(context, tagNameEntry); } uint size = (uint)(writer.BaseStream.Position - offset); writer.BaseStream.Seek(tagNameFileEntry.TableOffset + 0x8 * i + sectionOffset, SeekOrigin.Begin); var tableEntry = new GenericTableEntry(size, offset - sectionOffset); tableEntry.Write(writer); writer.BaseStream.Seek(0, SeekOrigin.End); } }
public void Load(FileInfo file) { if (!file.Exists) { throw new FileNotFoundException(file.FullName); } if (file.Length < typeof(ModPackageHeader).GetSize()) { throw new FormatException(file.FullName); } using (var stream = file.OpenRead()) using (var reader = new EndianReader(stream, leaveOpen: true)) { var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(CacheVersion.HaloOnline106708); Header = deserializer.Deserialize <ModPackageHeader>(dataContext); ReadMetadataSection(reader, dataContext, deserializer); ReadTagsSection(reader, dataContext, deserializer); ReadTagNamesSection(reader, dataContext, deserializer); ReadResourcesSection(reader); ReadMapFilesSection(reader); ReadCampaignFileSection(reader); ReadStringIdSection(reader); ReadFontSection(reader); ReadFileEntries(reader, dataContext, deserializer); int tagCacheCount = TagCachesStreams.Count; TagCaches = new List <TagCacheHaloOnline>(); for (int i = 0; i < tagCacheCount; i++) { TagCaches.Add(new TagCacheHaloOnline(TagCachesStreams[i], TagCacheNames[i])); } Resources = new ResourceCacheHaloOnline(CacheVersion.HaloOnline106708, ResourcesStream); } }
private void ReadTagNamesSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.TagNames); if (!GoToSectionHeaderOffset(reader, section)) { return; } var entry = new GenericSectionEntry(reader); var cacheCount = entry.Count; TagCacheNames = new List <Dictionary <int, string> >(); for (int i = 0; i < cacheCount; i++) { var nameDict = new Dictionary <int, string>(); reader.BaseStream.Position = entry.TableOffset + 0x8 * i + section.Offset; var tagNamesTableEntry = new GenericTableEntry(reader); if (tagNamesTableEntry.Size == 0) { throw new Exception("invalid tag name table entry size!"); } reader.BaseStream.Position = tagNamesTableEntry.Offset + section.Offset; var tagNamesHeader = new GenericSectionEntry(reader); reader.BaseStream.Position = tagNamesHeader.TableOffset + section.Offset; for (int j = 0; j < tagNamesHeader.Count; j++) { var tagNamesEntry = deserializer.Deserialize <ModPackageTagNamesEntry>(context); nameDict.Add(tagNamesEntry.TagIndex, tagNamesEntry.Name); } TagCacheNames.Add(nameDict); } }
private void ReadTagsSection(EndianReader reader, DataSerializationContext context, TagDeserializer deserializer) { var section = GetSectionHeader(reader, ModPackageSection.Tags); if (!GoToSectionHeaderOffset(reader, section)) { return; } var entry = new GenericSectionEntry(reader); var cacheCount = entry.Count; TagCachesStreams = new List <Stream>(); CacheNames = new List <string>(); for (int i = 0; i < cacheCount; i++) { var tagStream = new MemoryStream(); reader.BaseStream.Position = entry.TableOffset + 0x28 * i + section.Offset; var tableEntry = deserializer.Deserialize <CacheTableEntry>(context); CacheNames.Add(tableEntry.CacheName); reader.BaseStream.Position = tableEntry.Offset + section.Offset; if (section.Size > int.MaxValue) { throw new Exception("Tag cache size not supported"); } int size = (int)section.Size; byte[] data = new byte[size]; reader.Read(data, 0, size); tagStream.Write(data, 0, size); tagStream.Position = 0; TagCachesStreams.Add(tagStream); } }
private void WriteTagNamesSection(EndianWriter writer, DataSerializationContext context, TagSerializer serializer) { //prepare tag names var names = new Dictionary <int, string>(); foreach (var entry in Tags.Index) { if (entry != null && entry.Name != null) { names.Add(entry.Index, entry.Name); } } // create entry and immediatly write the tag names table GenericSectionEntry mapEntry = new GenericSectionEntry(names.Count, (int)writer.BaseStream.Position + 0x8); mapEntry.Write(writer); foreach (var entry in names) { var tagNameEntry = new ModPackageTagNamesEntry(entry.Key, entry.Value); serializer.Serialize(context, tagNameEntry); } }
/// <summary> /// GenerateForBlenderPhmoJson /// </summary> /// <param name="jsonString">String containing blender exported JSON using the jsonPhmoExporter plugin.</param> /// <returns>A memory stream with Havok 6.5.0 moppcode block</returns> public static MemoryStream GenerateForBlenderPhmoJson(string jsonString) { JsonToMopp jtm = new JsonToMopp(); MemoryStream moppStream = jtm.CreateMopp(jsonString); moppStream.Position = 0; CollisionMoppCode resource = SynthesizeMoppBlock(moppStream); MemoryStream outStream = null; #if !DEBUG try { #endif var resourceWriter = new EndianWriter(new MemoryStream(), EndianFormat.LittleEndian); var dataContext = new DataSerializationContext(null, resourceWriter); var block = dataContext.CreateBlock(); var info = TagStructure.GetTagStructureInfo(resource.GetType(), CacheVersion.HaloOnline235640); new TagSerializer(CacheVersion.HaloOnline235640).Serialize(dataContext, resource); block.Stream.Position = 0; outStream = block.Stream; #if !DEBUG } catch { return(null); } #endif outStream.Position = 48; //position of Data member. Data will be inlined moppStream.CopyTo(outStream); //remainder of moppStream is mopp program outStream.Position = 0; return(outStream); //this is the one with serialized data in it }
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 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); }
public TagCacheGen2(EndianReader reader, MapFile mapFile) { Version = mapFile.Version; var tagDataSectionOffset = mapFile.Header.TagsHeaderAddress32; reader.SeekTo(tagDataSectionOffset); var dataContext = new DataSerializationContext(reader); var deserializer = new TagDeserializer(mapFile.Version); Header = deserializer.Deserialize <TagCacheGen2Header>(dataContext); BaseTagAddress = (Header.TagGroupsOffset - 0x20); // // Read tag groups // //seek to the tag groups offset, seems to be contiguous to the header reader.SeekTo(tagDataSectionOffset + Header.TagGroupsOffset - BaseTagAddress); // TODO: check how halo 2 xbox uses this for (int i = 0; i < Header.TagGroupCount; i++) { var tag = new Tag(reader.ReadInt32()); var group = new TagGroup() { Tag = tag, ParentTag = new Tag(reader.ReadInt32()), GrandparentTag = new Tag(reader.ReadInt32()), Name = StringId.Invalid // has no stringids in tag groups }; TagGroups[tag] = group; } // // Read cached tags // reader.SeekTo(tagDataSectionOffset + Header.TagsOffset - BaseTagAddress); for (int i = 0; i < Header.TagCount; i++) { var tag = new Tag(reader.ReadInt32()); uint ID = reader.ReadUInt32(); uint address = reader.ReadUInt32(); int size = reader.ReadInt32(); if (tag.Value == -1 || tag.Value == 0 || size == -1 || address == 0xFFFFFFFF || ID == 0 || ID == 0xFFFFFFFF) { Tags.Add(null); } else { Tags.Add(new CachedTagGen2((int)(ID & 0xFFFF), ID, TagGroups[tag], address, size, null)); } } reader.SeekTo(mapFile.Header.TagNameIndicesOffset); var tagNamesOffset = new int[Header.TagCount]; for (int i = 0; i < Header.TagCount; i++) { tagNamesOffset[i] = reader.ReadInt32(); } // // Read tag names // reader.SeekTo(mapFile.Header.TagNamesBufferOffset); for (int i = 0; i < tagNamesOffset.Length; i++) { if (Tags[i] == null) { continue; } if (tagNamesOffset[i] == -1) { continue; } reader.SeekTo(tagNamesOffset[i] + mapFile.Header.TagNamesBufferOffset); Tags[i].Name = reader.ReadNullTerminatedString(); } // // Set hardcoded tags from the header // var scnrTag = GetTag(Header.ScenarioID); HardcodedTags[scnrTag.Group.Tag] = (CachedTagGen2)scnrTag; var globalTag = GetTag(Header.GlobalsID); HardcodedTags[globalTag.Group.Tag] = (CachedTagGen2)globalTag; }
public override object Execute(List <string> args) { if (args.Count < 2) { return(false); } // // Verify Blam tag instance // var unitName = args[0].ToLower(); if (unitName != "spartan" && unitName != "elite") { Console.WriteLine("ERROR: Only 'spartan' and 'elite' armor variants are allowed."); return(false); } args.RemoveAt(0); var blamTagName = unitName == "spartan" ? @"objects\characters\masterchief\mp_masterchief\mp_masterchief" : @"objects\characters\elite\mp_elite\mp_elite"; Console.Write($"Verifying {blamTagName}.render_model..."); CacheFile.IndexItem blamTag = null; foreach (var tag in BlamCache.IndexItems) { if ((tag.GroupTag == "mode") && (tag.Name == blamTagName)) { blamTag = tag; break; } } if (blamTag == null) { Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.render_model"); return(true); } Console.WriteLine("done."); // // Load the Blam tag definition // var variantName = args[0]; args.RemoveAt(0); CachedTagInstance edModeTag = null; var isScenery = false; var regionNames = new List <string>(); while (args.Count != 0) { switch (args[0].ToLower()) { case "scenery": isScenery = true; args.RemoveAt(0); break; case "replace:": edModeTag = CacheContext.GetTag(args[1]); args.RemoveAt(1); args.RemoveAt(0); break; case "regions:": regionNames.AddRange(args.Skip(1)); args.Clear(); break; default: throw new InvalidDataException($"{args}"); } } var blamContext = new CacheSerializationContext(ref BlamCache, blamTag); var edModeDefinition = BlamCache.Deserializer.Deserialize <RenderModel>(blamContext); var materials = edModeDefinition.Materials.Select(i => new RenderMaterial { BreakableSurfaceIndex = i.BreakableSurfaceIndex, Properties = i.Properties, RenderMethod = i.RenderMethod, Skins = i.Skins, Unknown = i.Unknown, Unknown2 = i.Unknown2, Unknown3 = i.Unknown3, Unknown4 = i.Unknown4 }).ToList(); edModeDefinition = (RenderModel)ConvertData(null, edModeDefinition, false); var variantRegions = new List <RenderModel.Region>(); foreach (var region in edModeDefinition.Regions) { if (regionNames.Count != 0 && !regionNames.Contains(CacheContext.GetString(region.Name))) { continue; } var variantRegion = new RenderModel.Region { Name = region.Name, Permutations = new List <RenderModel.Region.Permutation>() }; foreach (var permutation in region.Permutations) { if (variantName == CacheContext.GetString(permutation.Name)) { variantRegion.Permutations.Add(permutation); } } variantRegions.Add(variantRegion); } var variantMeshes = new List <int>(); var variantMaterials = new List <int>(); var variantVertexBuffers = new List <int>(); var variantIndexBuffers = new List <int>(); foreach (var region in variantRegions) { foreach (var permutation in region.Permutations) { for (var i = permutation.MeshIndex; i < (short)(permutation.MeshIndex + permutation.MeshCount); i++) { var mesh = edModeDefinition.Geometry.Meshes[i]; foreach (var part in mesh.Parts) { if (part.MaterialIndex != -1 && !variantMaterials.Contains(part.MaterialIndex)) { variantMaterials.Add(part.MaterialIndex); } } foreach (var vertexBuffer in mesh.VertexBufferIndices) { if (vertexBuffer != ushort.MaxValue && !variantVertexBuffers.Contains(vertexBuffer)) { variantVertexBuffers.Add(vertexBuffer); } } foreach (var indexBuffer in mesh.IndexBufferIndices) { if (indexBuffer != ushort.MaxValue && !variantIndexBuffers.Contains(indexBuffer)) { variantIndexBuffers.Add(indexBuffer); } } if (!variantMeshes.Contains(i)) { variantMeshes.Add(i); } } } } variantMeshes.Sort(); variantMaterials.Sort(); variantVertexBuffers.Sort(); variantIndexBuffers.Sort(); foreach (var meshIndex in variantMeshes) { var mesh = edModeDefinition.Geometry.Meshes[meshIndex]; foreach (var part in mesh.Parts) { if (part.MaterialIndex != -1) { part.MaterialIndex = (short)variantMaterials.IndexOf(part.MaterialIndex); } } } foreach (var region in variantRegions) { foreach (var permutation in region.Permutations) { if (permutation.MeshIndex != -1) { permutation.MeshIndex = (short)variantMeshes.IndexOf(permutation.MeshIndex); } } } foreach (var meshIndex in variantMeshes) { var mesh = edModeDefinition.Geometry.Meshes[meshIndex]; for (var i = 0; i < mesh.VertexBufferIndices.Length; i++) { if (!variantVertexBuffers.Contains(mesh.VertexBufferIndices[i])) { mesh.VertexBufferIndices[i] = ushort.MaxValue; } else { mesh.VertexBufferIndices[i] = (ushort)variantVertexBuffers.IndexOf(mesh.VertexBufferIndices[i]); } } for (var i = 0; i < mesh.IndexBufferIndices.Length; i++) { if (!variantIndexBuffers.Contains(mesh.IndexBufferIndices[i])) { mesh.IndexBufferIndices[i] = ushort.MaxValue; } else { mesh.IndexBufferIndices[i] = (ushort)variantIndexBuffers.IndexOf(mesh.IndexBufferIndices[i]); } } } edModeDefinition.Regions = variantRegions; edModeDefinition.Geometry.Meshes = edModeDefinition.Geometry.Meshes.Where(i => variantMeshes.Contains(edModeDefinition.Geometry.Meshes.IndexOf(i))).ToList(); // // Port Blam render_model materials // materials = materials.Where(i => variantMaterials.Contains(materials.IndexOf(i))).ToList(); using (var stream = CacheContext.OpenTagCacheReadWrite()) { for (var i = 0; i < materials.Count; i++) { var material = materials[i]; if (material.RenderMethod.Index == -1) { continue; } var blamRenderMethod = materials[i].RenderMethod; var blamRenderMethodTag = BlamCache.IndexItems.GetItemByID(blamRenderMethod.Index); var renderMethodExists = false; foreach (var instance in CacheContext.TagCache.Index.FindAllInGroup("rm ")) { if (instance?.Name == blamRenderMethodTag.Name) { renderMethodExists = true; material.RenderMethod = instance; break; } } if (!renderMethodExists) { material.RenderMethod = CacheContext.GetTag <Shader>(@"shaders\invalid"); } } } edModeDefinition.Materials = materials; // // Load Blam resource data // var resourceData = BlamCache.GetRawFromID(edModeDefinition.Geometry.ZoneAssetHandle); if (resourceData == null) { Console.WriteLine("Blam render_geometry resource contains no data."); return(true); } // // Load Blam resource definition // Console.Write("Loading Blam render_geometry resource definition..."); var definitionEntry = BlamCache.ResourceGestalt.TagResources[edModeDefinition.Geometry.ZoneAssetHandle.Index]; var resourceDefinition = new RenderGeometryApiResourceDefinition { VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(), IndexBuffers = new List <TagStructureReference <IndexBufferDefinition> >() }; using (var definitionStream = new MemoryStream(BlamCache.ResourceGestalt.FixupInformation)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) { var dataContext = new DataSerializationContext(definitionReader, null, CacheResourceAddressType.Definition); definitionReader.SeekTo(definitionEntry.FixupInformationOffset + (definitionEntry.FixupInformationLength - 24)); var vertexBufferCount = definitionReader.ReadInt32(); definitionReader.Skip(8); var indexBufferCount = definitionReader.ReadInt32(); definitionReader.SeekTo(definitionEntry.FixupInformationOffset); for (var i = 0; i < vertexBufferCount; i++) { resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition> { Definition = new VertexBufferDefinition { Count = definitionReader.ReadInt32(), Format = (VertexBufferFormat)definitionReader.ReadInt16(), VertexSize = definitionReader.ReadInt16(), Data = new TagData { Size = definitionReader.ReadInt32(), Unused4 = definitionReader.ReadInt32(), Unused8 = definitionReader.ReadInt32(), Address = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()), Unused10 = definitionReader.ReadInt32() } } }); } definitionReader.Skip(vertexBufferCount * 12); for (var i = 0; i < indexBufferCount; i++) { resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition> { Definition = new IndexBufferDefinition { Format = (IndexBufferFormat)definitionReader.ReadInt32(), Data = new TagData { Size = definitionReader.ReadInt32(), Unused4 = definitionReader.ReadInt32(), Unused8 = definitionReader.ReadInt32(), Address = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()), Unused10 = definitionReader.ReadInt32() } } }); } } Console.WriteLine("done."); // // Convert Blam resource data // using (var edResourceStream = new MemoryStream()) { // // Convert Blam render_geometry_api_resource_definition // using (var blamResourceStream = new MemoryStream(resourceData)) { // // Convert Blam vertex buffers // Console.Write("Converting vertex buffers..."); var previousVertexBufferCount = -1; for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++) { if (!variantVertexBuffers.Contains(i)) { continue; } blamResourceStream.Position = definitionEntry.ResourceFixups[i].Offset; if (i > 0) { previousVertexBufferCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count; } GeometryConverter.ConvertVertexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i, previousVertexBufferCount); } Console.WriteLine("done."); // // Convert Blam index buffers // Console.Write("Converting index buffers..."); for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++) { if (!variantIndexBuffers.Contains(i)) { continue; } blamResourceStream.Position = definitionEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset; GeometryConverter.ConvertIndexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i); } Console.WriteLine("done."); } resourceDefinition.VertexBuffers = resourceDefinition.VertexBuffers.Where(i => variantVertexBuffers.Contains(resourceDefinition.VertexBuffers.IndexOf(i))).ToList(); resourceDefinition.IndexBuffers = resourceDefinition.IndexBuffers.Where(i => variantIndexBuffers.Contains(resourceDefinition.IndexBuffers.IndexOf(i))).ToList(); // // Finalize the new ElDorado geometry resource // Console.Write("Writing resource data..."); edModeDefinition.Geometry.Resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.RenderGeometry, ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; edResourceStream.Position = 0; var resourceContext = new ResourceSerializationContext(CacheContext, edModeDefinition.Geometry.Resource); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); edModeDefinition.Geometry.Resource.ChangeLocation(ResourceLocation.ResourcesB); CacheContext.AddResource(edModeDefinition.Geometry.Resource, edResourceStream); Console.WriteLine("done."); } edModeDefinition.Name = CacheContext.GetStringId(variantName); if (edModeTag == null) { for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edModeTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("mode")]); break; } } if (edModeTag == null) { edModeTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("mode")]); } } // // Create a new armor model tag // Model edHlmtDefinition = null; CachedTagInstance edHlmtTag = null; if (isScenery) { Console.Write($"Verifying {blamTagName}.model..."); CacheFile.IndexItem blamHlmtTag = null; foreach (var tag in BlamCache.IndexItems) { if ((tag.GroupTag == "hlmt") && (tag.Name == blamTagName)) { blamHlmtTag = tag; break; } } if (blamHlmtTag == null) { Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.model"); return(true); } Console.WriteLine("done."); blamContext = new CacheSerializationContext(ref BlamCache, blamHlmtTag); edHlmtDefinition = (Model)ConvertData(null, BlamCache.Deserializer.Deserialize <Model>(blamContext), false); edHlmtDefinition.RenderModel = edModeTag; edHlmtDefinition.ReduceToL1SuperLow = 36.38004f; edHlmtDefinition.ReduceToL2Low = 27.28503f; edHlmtDefinition.Variants = new List <Model.Variant>(); edHlmtDefinition.Materials = new List <Model.Material>(); edHlmtDefinition.NewDamageInfo = new List <Model.GlobalDamageInfoBlock>(); edHlmtDefinition.Targets = new List <Model.Target>(); var collisionRegions = new List <Model.CollisionRegion>(); foreach (var collisionRegion in edHlmtDefinition.CollisionRegions) { var found = false; foreach (var variantRegion in variantRegions) { if (collisionRegion.Name == variantRegion.Name) { found = true; break; } } if (!found) { continue; } found = false; foreach (var permutation in collisionRegion.Permutations) { if (permutation.Name == CacheContext.GetStringId(variantName)) { found = true; break; } } if (found) { collisionRegions.Add(collisionRegion); } } foreach (var collisionRegion in collisionRegions) { Model.CollisionRegion.Permutation permutation = null; foreach (var collisionPermutation in collisionRegion.Permutations) { if (collisionPermutation.Name == CacheContext.GetStringId(variantName)) { permutation = collisionPermutation; break; } } if (permutation == null) { throw new KeyNotFoundException(); } collisionRegion.Permutations = new List <Model.CollisionRegion.Permutation> { permutation }; } edHlmtDefinition.CollisionRegions = collisionRegions; for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edHlmtTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("hlmt")]); break; } } if (edHlmtTag == null) { edHlmtTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("hlmt")]); } } // // Create a new armor scenery tag // Scenery edScenDefinition = null; CachedTagInstance edScenTag = null; if (isScenery) { edScenDefinition = new Scenery { ObjectType = new GameObjectType { Halo2 = GameObjectTypeHalo2.Scenery, Halo3Retail = GameObjectTypeHalo3Retail.Scenery, Halo3ODST = GameObjectTypeHalo3ODST.Scenery, HaloOnline = GameObjectTypeHaloOnline.Scenery }, BoundingRadius = 0.44f, BoundingOffset = new RealPoint3d(-0.02f, 0.0f, 0.0f), AccelerationScale = 1.2f, SweetenerSize = GameObject.SweetenerSizeValue.Medium, Model = edHlmtTag, ChangeColors = new List <GameObject.ChangeColor> { new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor() }, NodeMaps = new List <GameObject.NodeMap>() }; for (sbyte i = 0; i < 51; i++) { edScenDefinition.NodeMaps.Add(new GameObject.NodeMap { TargetNode = i }); } for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edScenTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("scen")]); break; } } if (edScenTag == null) { edScenTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("scen")]); } } // // Serialize new ElDorado tag definitions // using (var cacheStream = CacheContext.OpenTagCacheReadWrite()) { CacheContext.Serialize(cacheStream, edModeTag, edModeDefinition); edModeTag.Name = isScenery ? (unitName == "spartan" ? $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" : $@"objects\characters\elite\mp_elite\armor\{variantName}") : (unitName == "spartan" ? @"objects\characters\masterchief\mp_masterchief\mp_masterchief" : @"objects\characters\elite\mp_elite\mp_elite"); if (isScenery) { CacheContext.Serialize(cacheStream, edHlmtTag, edHlmtDefinition); CacheContext.Serialize(cacheStream, edScenTag, edScenDefinition); edScenTag.Name = unitName == "spartan" ? $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" : $@"objects\characters\elite\mp_elite\armor\{variantName}"; } } return(true); }
public override object Execute(List <string> args) { if (Definition.PathfindingResource == null) { Console.WriteLine("ERROR: Pathfinding geometry does not have a resource associated with it."); return(true); } var resourceContext = new ResourceSerializationContext(CacheContext, Definition.PathfindingResource); var resourceDefinition = CacheContext.Deserializer.Deserialize <StructureBspCacheFileTagResources>(resourceContext); using (var resourceStream = new MemoryStream()) using (var reader = new EndianReader(resourceStream)) using (var writer = new EndianWriter(resourceStream)) { CacheContext.ExtractResource(Definition.PathfindingResource, resourceStream); var dataContext = new DataSerializationContext(reader, writer); foreach (var pathfindingDatum in resourceDefinition.PathfindingData) { resourceStream.Position = pathfindingDatum.Sectors.Address.Offset; for (var i = 0; i < pathfindingDatum.Sectors.Count; i++) { pathfindingDatum.Sectors.Add( CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Sector>(dataContext)); } resourceStream.Position = pathfindingDatum.Links.Address.Offset; for (var i = 0; i < pathfindingDatum.Links.Count; i++) { pathfindingDatum.Links.Add( CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Link>(dataContext)); } resourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset; for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++) { pathfindingDatum.PathfindingHints.Add( CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>(dataContext)); } resourceStream.Position = pathfindingDatum.Vertices.Address.Offset; for (var i = 0; i < pathfindingDatum.Vertices.Count; i++) { pathfindingDatum.Vertices.Add( CacheContext.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Vertex>(dataContext)); } for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++) { var hint = pathfindingDatum.PathfindingHints[i]; if (hint.HintType != JumpLink && hint.HintType != WallJumpLink) { continue; } var hintverts = new List <short>(); var success = false; hintverts.Add((short)(hint.Data[1] & ushort.MaxValue)); hintverts.Add((short)((hint.Data[1] >> 16) & ushort.MaxValue)); if (hintverts[0] == -1 || hintverts[1] == -1) { continue; } float hint_x = (pathfindingDatum.Vertices[hintverts[0]].Position.X + pathfindingDatum.Vertices[hintverts[1]].Position.X) / 2.0f; float hint_y = (pathfindingDatum.Vertices[hintverts[0]].Position.Y + pathfindingDatum.Vertices[hintverts[1]].Position.Y) / 2.0f; float hint_z = (pathfindingDatum.Vertices[hintverts[0]].Position.Z + pathfindingDatum.Vertices[hintverts[1]].Position.Z) / 2.0f; var sectorlist = new List <int>(); var backupsectorlist = new List <int>(); var zavelist = new List <float>(); var backupzavelist = new List <float>(); for (var s = 0; s < pathfindingDatum.Sectors.Count; s++) { var sector = pathfindingDatum.Sectors[s]; var vertices = new HashSet <short>(); if (sector.FirstLink == -1) { continue; } var link = pathfindingDatum.Links[sector.FirstLink]; while (true) { if (link.LeftSector == s) { vertices.Add(link.Vertex1); vertices.Add(link.Vertex2); if (link.ForwardLink == sector.FirstLink) { break; } else { link = pathfindingDatum.Links[link.ForwardLink]; } } else if (link.RightSector == s) { vertices.Add(link.Vertex1); vertices.Add(link.Vertex2); if (link.ReverseLink == sector.FirstLink) { break; } else { link = pathfindingDatum.Links[link.ReverseLink]; } } } var points = new List <RealPoint3d>(); var xlist = new List <float>(); var ylist = new List <float>(); var zlist = new List <float>(); foreach (var vert in vertices) { points.Add(pathfindingDatum.Vertices[vert].Position); xlist.Add(pathfindingDatum.Vertices[vert].Position.X); ylist.Add(pathfindingDatum.Vertices[vert].Position.Y); zlist.Add(pathfindingDatum.Vertices[vert].Position.Z); } float xmin = xlist.Min(); float xmax = xlist.Max(); float ymin = ylist.Min(); float ymax = ylist.Max(); float zmin = zlist.Min(); float zmax = zlist.Max(); float zave = zlist.Average(); bool pnpoly(List <RealPoint3d> polygon, RealPoint3d testPoint) { var result = false; for (int p = 0, j = polygon.Count - 1; p < polygon.Count(); j = p++) { if (polygon[p].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[p].Y >= testPoint.Y) { if (polygon[p].X + (testPoint.Y - polygon[p].Y) / (polygon[j].Y - polygon[p].Y) * (polygon[j].X - polygon[p].X) < testPoint.X) { result = !result; } } } // TODO: maybe check Z? return(result); } if (pnpoly(points, new RealPoint3d(hint_x, hint_y, hint_z))) { sectorlist.Add(s); zavelist.Add(Math.Abs(hint_z - zave)); } else if (xmin < hint_x && xmax > hint_x && ymin < hint_y && ymax > hint_y) { backupsectorlist.Add(s); backupzavelist.Add(Math.Abs(hint_z - zave)); } } if (sectorlist.Count > 0) { var s = sectorlist[zavelist.IndexOf(zavelist.Min())]; var hiword = (short)(hint.Data[3] >> 16); hint.Data[3] = hiword << 16 | s; success = true; } else if (backupsectorlist.Count > 0) { var s = backupsectorlist[backupzavelist.IndexOf(backupzavelist.Min())]; var hiword = (short)(hint.Data[3] >> 16); hint.Data[3] = hiword << 16 | s; success = true; } if (!success) { Console.WriteLine($"Pathfinding Jump Hint {i} sector not found!"); } } resourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset; for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++) { CacheContext.Serializer.Serialize(dataContext, pathfindingDatum.PathfindingHints[i]); } resourceStream.Position = 0; CacheContext.ReplaceResource(Definition.PathfindingResource, resourceStream); } } return(true); }
public List <ModelAnimationGraph.ResourceGroup> ConvertModelAnimationGraphResourceGroups(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, List <ModelAnimationGraph.ResourceGroup> resourceGroups) { if (BlamCache.ResourceGestalt == null) { BlamCache.LoadResourceTags(); } var resourceDefinition = new List <ModelAnimationTagResource>(); foreach (var group in resourceGroups) { var resourceEntry = BlamCache.ResourceGestalt.TagResources[group.ZoneAssetHandle.Index]; group.Resource = new PageableResource { Page = new RawPage { Index = -1, }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Animation, DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(), DefinitionAddress = resourceEntry.DefinitionAddress, ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // Convert blam fixups // get the list of members in this resourcegroup. this list contains address, various offsets, and other info about the member. if (group.Resource.Resource.DefinitionData.Length != 0) { using (var definitionStream = new MemoryStream(group.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) { var newFixup = new TagResourceGen3.ResourceFixup { BlockOffset = (uint)fixup.BlockOffset, Address = new CacheResourceAddress(CacheResourceAddressType.Resource, fixup.Offset) }; definitionStream.Position = newFixup.BlockOffset; definitionWriter.Write(newFixup.Address.Value); group.Resource.Resource.ResourceFixups.Add(newFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheResourceAddressType.Definition); definitionStream.Position = group.Resource.Resource.DefinitionAddress.Offset + 0x4; definitionWriter.Write(0x20000000); // ODST's resource type is 4 when it's supposed to be 2 because the resource definition is in the tag and not as a raw resource definitionStream.Position = group.Resource.Resource.DefinitionAddress.Offset; resourceDefinition.Add(BlamCache.Deserializer.Deserialize <ModelAnimationTagResource>(dataContext)); } } } var diffLines = new List <string>(); var resDefIndex = -1; foreach (var group in resourceGroups) { resDefIndex++; if (resourceDefinition.Count < resDefIndex + 1) { continue; // rare cases, might break the game } // Get the resource group real size, which is probably not in the resource definition var groupSize = 0; foreach (var groupMember in resourceDefinition[resDefIndex].GroupMembers) { groupSize += groupMember.AnimationData.Size; while (groupSize % 0x10 != 0) // align to 0x10. { groupSize += 4; } } var resourceData = BlamCache.GetRawFromID(group.ZoneAssetHandle, groupSize); if (resourceData == null) { return(null); } using (var blamResourceStream = new MemoryStream(resourceData)) using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian)) using (var dataStream = new MemoryStream(new byte[groupSize])) using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian)) { var dataContext = new DataSerializationContext(resourceReader, resourceWriter); var memberOffset = 0; for (var memberIndex = 0; memberIndex < resourceDefinition[resDefIndex].GroupMembers.Count; memberIndex++) { var member = resourceDefinition[resDefIndex].GroupMembers[memberIndex]; ModelAnimationTagResource.GroupMember.Codec codec; ModelAnimationTagResource.GroupMember.FrameInfo frameInfo; if (member.BaseHeader != ModelAnimationTagResource.GroupMemberHeaderType.Overlay) { blamResourceStream.Position = member.AnimationData.Address.Offset; dataStream.Position = member.AnimationData.Address.Offset; codec = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Codec>(dataContext); CacheContext.Serializer.Serialize(dataContext, codec); var Format1 = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format1>(dataContext); CacheContext.Serializer.Serialize(dataContext, Format1); // blamResourceStream.Position = (long)member.AnimationData.Address.Offset + headerSize; // edResourceStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.RotationNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } blamResourceStream.Position = member.AnimationData.Address.Offset + Format1.DataStart; dataStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.PositionNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } blamResourceStream.Position = member.AnimationData.Address.Offset + Format1.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.ScaleNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } // If the overlay header is alone, member.OverlayOffset = 0 blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset; dataStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset; codec = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Codec>(dataContext); CacheContext.Serializer.Serialize(dataContext, codec); // deserialize second header. or as first header if the type1/format1 header isn't used. switch (codec.AnimationCodec) { case ModelAnimationTagResource.AnimationCompressionFormats.Type3: // should merge with type1 var header = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format1>(dataContext); CacheContext.Serializer.Serialize(dataContext, header); for (int nodeIndex = 0; nodeIndex < codec.RotationNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + header.DataStart; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.PositionNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + header.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.ScaleNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; case ModelAnimationTagResource.AnimationCompressionFormats.Type4: case ModelAnimationTagResource.AnimationCompressionFormats.Type5: case ModelAnimationTagResource.AnimationCompressionFormats.Type6: case ModelAnimationTagResource.AnimationCompressionFormats.Type7: var overlay = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Overlay>(dataContext); CacheContext.Serializer.Serialize(dataContext, overlay); #region Description // Description by DemonicSandwich from http://remnantmods.com/forums/viewtopic.php?f=13&t=1574 (matches my previous observations) // Format 6 uses Keyframes the way there are supposed to be used. As KEY frames, with the majority of the frames being Tweens. // // This format adds two extra blocks of data to it's structure. // One block that determines how many Keyframes each Node will have, and an offset to to where it's Markers start from. // // Advantages: // This format requires far fewer Keyframes to make a complex animation. // You do not need a keyframe for each render frame. // Disadvantages: // It's a bit more complex to work with. // Since it's Keyrame Markers are only 1 byte in size, you're animation cannot be longer than 256 frames, or ~8.5 seconds for non - machine objects. > 12 bits for gen3, max 0xFFF frames // Machines are still limited to 256 frames but the frames can be stretched out. #endregion var RotationFrameCount = new List <uint>(); var PositionFrameCount = new List <uint>(); var ScaleFrameCount = new List <uint>(); for (int i = 0; i < codec.RotationNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; // unused in this conversion var keyframes = frameInfo.FrameCount & 0x00000FFF; RotationFrameCount.Add(keyframes); } for (int i = 0; i < codec.PositionNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; var keyframes = frameInfo.FrameCount & 0x00000FFF; PositionFrameCount.Add(keyframes); } for (int i = 0; i < codec.ScaleNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; var keyframes = frameInfo.FrameCount & 0x00000FFF; ScaleFrameCount.Add(keyframes); } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.RotationKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in RotationFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.PositionKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in PositionFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.ScaleKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in ScaleFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.RotationFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in RotationFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.PositionFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in PositionFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in ScaleFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; case ModelAnimationTagResource.AnimationCompressionFormats.Type8: // Type 8 is basically a type 3 but with rotation frames using 4 floats, or a realQuaternion var Format8 = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format8>(dataContext); CacheContext.Serializer.Serialize(dataContext, Format8); for (int nodeIndex = 0; nodeIndex < codec.RotationNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrameFloat>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + Format8.PositionFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.PositionNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + Format8.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.ScaleNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; default: throw new DataMisalignedException(); } #region How Footer/Flags works // Better description by DemonicSandwich from http://remnantmods.com/forums/viewtopic.php?f=13&t=1574 : Node List Block: (matches my previous observations) // Just a block of flags. Tick a flag and the respective node will be affected by animation. // The size of this block should always be a multiple of 12. It's size is determined my the meta value Node List Size [byte, offset: 61] // When set to 12, the list can handle objects with a node count up to 32 (0-31). // When set to 24, the object can have 64 nodes and so on. // The block is split into 3 groups of flags. // The first group determines what nodes are affected by rotation, the second group for position, and the third group for scale. // // If looking at it in hex, the Node ticks for each group will be in order as follows: // [7][6][5][4][3][2][1][0] - [15][14][13][12][11][10][9][8] - etc. // Each flag corresponding to a Node index. #endregion #region Footer/Flag block // There's one bitfield32 for every 32 nodes that are animated which i'll call a node flags. // There's at least 3 flags if the animation only has an overlay header, which i'll call a flag set. // There's at least 6 flags if the animation has both a base header and an overlay header, so 2 sets. // If the animated nodes count is over 32, then a new flags set is added. // 1 set per header is added, such as 32 nodes = 1 set, 64 = 2 sets, 96 = 3 sets etc , 128-256 maybe max blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + member.FlagsOffset; dataStream.Position = blamResourceStream.Position; var footerSizeBase = (byte)member.BaseHeader / 4; for (int flagsCount = 0; flagsCount < footerSizeBase; flagsCount++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } var footerSizeOverlay = (byte)member.OverlayHeader / 4; for (int flagsCount = 0; flagsCount < footerSizeOverlay; flagsCount++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } #endregion switch (member.MovementDataType) { case ModelAnimationTagResource.GroupMemberMovementDataType.None: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDy>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy_dyaw: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy_dz_dyaw: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDzDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDzDyaw>(dataContext)); } } break; default: break; } dataStream.Position = memberOffset + member.AnimationData.Size; // Before the next animation member, there's some padding that is garbage data in H3/ODST, but zeroed in HO. // In order to compare converted to original raw easily, copy the original data. while (blamResourceStream.Position % 0x10 != 0) // align to 0x10, useless padding of garbage data, it's zeroed in 1:1 HO raw, just read as 4 lame bytes { if (blamResourceStream.Position == blamResourceStream.Length) { break; } CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } // Align the next animation member to 0x10. memberOffset += member.AnimationData.Size; while (memberOffset % 0x10 != 0) { memberOffset += 4; } } dataStream.Position = 0; CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, group.Resource), resourceDefinition[resDefIndex]); group.Resource.ChangeLocation(ResourceLocation.ResourcesB); var resource = group.Resource; 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(resourceGroups); }
private void WriteMetadataSection(DataSerializationContext context, TagSerializer serializer) { serializer.Serialize(context, Metadata); }
public override object Execute(List <string> args) { if (args.Count != 0) { return(false); } ResourcesB = CacheContext.GetResourceCache(ResourceLocation.ResourcesB); Resources = CacheContext.GetResourceCache(ResourceLocation.Resources); var tags = new Dictionary <int, CachedTagInstance>(); var relocatedResources = new Dictionary <int, PageableResource>(); using (var tagsStream = CacheContext.OpenTagCacheReadWrite()) using (var sourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.ResourcesB)) using (var destStream = CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Resources)) { for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (tags.ContainsKey(i)) { continue; } var tag = tags[i] = CacheContext.GetTag(i); if (tag == null || tag.ResourcePointerOffsets.Count == 0) { continue; } var isB = false; using (var dataStream = new MemoryStream(CacheContext.TagCache.ExtractTagRaw(tagsStream, tag))) using (var reader = new EndianReader(dataStream)) { var dataContext = new DataSerializationContext(reader, null, CacheResourceAddressType.Resource); foreach (var resourcePointerOffset in tag.ResourcePointerOffsets) { reader.BaseStream.Position = resourcePointerOffset; var resourcePointer = reader.ReadUInt32(); reader.BaseStream.Position = tag.PointerToOffset(resourcePointer); var resource = CacheContext.Deserializer.Deserialize <PageableResource>(dataContext); if (resource.Page.Index == -1) { continue; } if (resource.GetLocation(out var location) && location == ResourceLocation.ResourcesB) { isB = true; break; } } } if (!isB) { continue; } var tagDefinition = CacheContext.Deserialize(tagsStream, tag); tagDefinition = ConvertData(tagsStream, sourceStream, destStream, tagDefinition); CacheContext.Serialize(tagsStream, tag, tagDefinition); } } return(true); }
public void Save(FileInfo file) { if (!file.Directory.Exists) { file.Directory.Create(); } using (var packageStream = file.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite)) using (var writer = new EndianWriter(packageStream, leaveOpen: true)) { var serializer = new TagSerializer(CacheVersion.HaloOnline106708); var dataContext = new DataSerializationContext(writer); packageStream.SetLength(0); // // reserve header space // writer.Write(new byte[typeof(ModPackageHeader).GetSize()]); // // build section table // Header.SectionTable.Count = (int)ModPackageSection.SectionCount; Header.SectionTable.Offset = (uint)writer.BaseStream.Position; writer.Write(new byte[typeof(ModPackageSectionHeader).GetSize() * (int)ModPackageSection.SectionCount]); uint size; uint offset; // // Write metadata // offset = (uint)writer.BaseStream.Position; WriteMetadataSection(dataContext, serializer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.Metadata, writer, size, offset); // // Write tag cache // offset = (uint)writer.BaseStream.Position; WriteTagsSection(writer, dataContext, serializer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.Tags, writer, size, offset); // // Write tag names table // offset = (uint)writer.BaseStream.Position; WriteTagNamesSection(writer, dataContext, serializer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.TagNames, writer, size, offset); // // write resource cache // offset = (uint)writer.BaseStream.Position; WriteResourcesSection(writer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.Resources, writer, size, offset); // // Write map file section // if (MapFileStreams.Count > 0) { offset = (uint)writer.BaseStream.Position; WriteMapsSection(writer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.MapFiles, writer, size, offset); DetermineMapFlags(); } // // Write campaign file section // if (CampaignFileStream != null && CampaignFileStream.Length > 0) { offset = (uint)writer.BaseStream.Position; WriteCampaignFileSection(writer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.CampaignFiles, writer, size, offset); } // // Write font file section // if (FontPackage != null && FontPackage.Length > 0) { offset = (uint)writer.BaseStream.Position; WriteFontFileSection(writer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.Fonts, writer, size, offset); } // // Write stringid file section // if (StringTable != null) { offset = (uint)writer.BaseStream.Position; WriteStringIdsSection(writer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.StringIds, writer, size, offset); } // // Write files section // if (Files != null && Files.Count > 0) { offset = (uint)writer.BaseStream.Position; WriteFileEntries(writer, dataContext, serializer); size = (uint)(writer.BaseStream.Position - offset); WriteSectionEntry((int)ModPackageSection.Files, writer, size, offset); } // // calculate package sha1 // packageStream.Position = typeof(ModPackageHeader).GetSize(); Header.SHA1 = new SHA1Managed().ComputeHash(packageStream); // // update package header // Header.FileSize = (uint)packageStream.Length; packageStream.Position = 0; serializer.Serialize(dataContext, Header); if (packageStream.Length > uint.MaxValue) { Console.WriteLine($"WARNING: Mod package size exceeded 0x{uint.MaxValue.ToString("X8")} bytes, it will fail to load."); } } }