public void ExtractDds(TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream outStream) { // TODO: Make sure 3D textures and cube maps work // Deserialize the resource definition and verify it var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(resource.Resource); var definition = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var dataReference = definition.Texture.Definition.Data; if (dataReference.Address.Type != ResourceAddressType.Resource) { throw new InvalidOperationException("Invalid resource data address"); } var header = CreateDdsHeader(definition); var resourceDataStream = new MemoryStream(); _resourceManager.Extract(resource.Resource, resourceDataStream); header.WriteTo(outStream); resourceDataStream.Position = dataReference.Address.Offset; StreamUtil.Copy(resourceDataStream, outStream, dataReference.Size); }
public override object Execute(List <string> args) { if (args.Count != 1) { return(false); } var binkFile = new FileInfo(args[0]); var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Resource); var resourceDefinition = CacheContext.Deserializer.Deserialize <BinkResource>(resourceContext); using (var resourceStream = new MemoryStream()) using (var resourceReader = new BinaryReader(resourceStream)) using (var fileStream = binkFile.Create()) using (var fileWriter = new BinaryWriter(fileStream)) { CacheContext.ExtractResource(Definition.Resource, resourceStream); resourceReader.BaseStream.Position = resourceDefinition.Data.Address.Offset; fileWriter.Write(resourceReader.ReadBytes(resourceDefinition.Data.Size)); } Console.WriteLine($"Created \"{binkFile.FullName}\" successfully."); return(true); }
public void InjectDds(Bitmap bitmap, int imageIndex, Stream ddsStream) { // Deserialize the old definition var resourceContext = new ResourceSerializationContext(bitmap.Resources[imageIndex].Resource); var definition = TagDeserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var texture = definition.Texture.Definition; // Read the DDS header and modify the definition to match var dds = DdsHeader.Read(ddsStream); var dataSize = (int)(ddsStream.Length - ddsStream.Position); texture.Data = new ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (definition.Texture.Definition.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: definition.Texture.Definition.Flags = BitmapFlags.Compressed; break; default: definition.Texture.Definition.Flags = BitmapFlags.None; break; } // Inject the resource data _resourceManager.Replace(bitmap.Resources[imageIndex].Resource, ddsStream); // Serialize the new resource definition TagSerializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition var imageData = bitmap.Images[imageIndex]; imageData.Width = texture.Width; imageData.Height = texture.Height; imageData.Depth = texture.Depth; imageData.Type = texture.Type; imageData.Format = texture.Format; imageData.Flags = texture.Flags; imageData.MipmapCount = (sbyte)(texture.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; }
public override object Execute(List <string> args) { if (args.Count != 3) { return(false); } var variantName = args[0]; var fileType = args[1].ToLower(); var modelFileName = args[2]; switch (fileType) { case "obj": break; default: throw new NotImplementedException(fileType); } if (Definition.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return(true); } // // Deserialize the resource definition // var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Geometry.Resource); var resourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // // Extract the resource data // CacheContext.ExtractResource(Definition.Geometry.Resource, resourceStream); var modelFile = new FileInfo(modelFileName); if (!modelFile.Directory.Exists) { modelFile.Directory.Create(); } switch (fileType) { case "obj": return(ExtractObj(variantName, modelFile, Definition, resourceDefinition, resourceStream)); default: throw new NotImplementedException(fileType); } } }
public void ExtractBitmap(Bitmap bitmap, int imageIndex, Stream outStream) { var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(CacheContext, resource.Resource); var definition = ExtractResourceDefinition(resource, resourceContext); var header = new DDSHeader(definition.Texture.Definition); header.Write(new EndianWriter(outStream)); ExtractResourceData(definition, resource, outStream); }
private void SerializeDefinitionData(TagSerializer serializer, RenderGeometryResourceDefinition definition) { _model.Geometry.Resource = new ResourceReference { Type = 5, // FIXME: hax DefinitionFixups = new List <ResourceDefinitionFixup>(), D3DObjectFixups = new List <D3DObjectFixup>(), Unknown68 = 1, }; var context = new ResourceSerializationContext(_model.Geometry.Resource); serializer.Serialize(context, definition); }
private void SerializeDefinitionData(TagSerializer serializer, RenderGeometryApiResourceDefinition definition) { _model.Geometry.Resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.RenderGeometry, ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; var context = new ResourceSerializationContext(CacheContext, _model.Geometry.Resource); serializer.Serialize(context, definition); }
public ModelExtractor(HaloOnlineCacheContext cacheContext, RenderModel renderModel) { Scene = new Scene(); CacheContext = cacheContext; RenderModel = renderModel; MeshMapping = new Dictionary <int, int>(); BoneNodes = new List <Node>(); // Deserialize the render_model resource var resourceContext = new ResourceSerializationContext(CacheContext, RenderModel.Geometry.Resource); RenderModelResourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); RenderModelResourceStream = new MemoryStream(); CacheContext.ExtractResource(RenderModel.Geometry.Resource, RenderModelResourceStream); }
private BitmapTextureInteropResource ExtractResourceDefinition(Bitmap.BitmapResource resource, ResourceSerializationContext context) { var definition = CacheContext.Deserializer.Deserialize <BitmapTextureInteropResource>(context); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } return(definition); }
public RenderGeometry Convert(Stream cacheStream, RenderGeometry geometry, Dictionary <ResourceLocation, Stream> resourceStreams, PortTagCommand.PortingFlags portingFlags) { if (BlamCache.ResourceGestalt == null || BlamCache.ResourceLayoutTable == null) { BlamCache.LoadResourceTags(); } // // Convert byte[] of UnknownBlock // foreach (var block in geometry.Unknown2) { var data = block.Unknown3; if (data != null || data.Length != 0) { var result = new byte[data.Length]; using (var inputReader = new EndianReader(new MemoryStream(data), EndianFormat.BigEndian)) using (var outputWriter = new EndianWriter(new MemoryStream(result), EndianFormat.LittleEndian)) { while (!inputReader.EOF) { outputWriter.Write(inputReader.ReadUInt32()); } block.Unknown3 = result; } } } // // Convert UnknownSection.Unknown byte[] endian // for (int i = 0; i < geometry.UnknownSections.Count; i++) { byte[] dataref = geometry.UnknownSections[i].Unknown; if (dataref.Length == 0) { continue; } using (var outStream = new MemoryStream()) using (var outReader = new BinaryReader(outStream)) using (var outWriter = new EndianWriter(outStream, EndianFormat.LittleEndian)) using (var stream = new MemoryStream(dataref)) using (var reader = new EndianReader(stream, EndianFormat.BigEndian)) { var dataContext = new DataSerializationContext(reader, outWriter); var header = CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext); var section = new ScenarioLightmapBspDataSection { Headers = new List <ScenarioLightmapBspDataSection.Header> { header }, VertexLists = new ScenarioLightmapBspDataSection.VertexList { Vertex = new List <ScenarioLightmapBspDataSection.VertexList.Datum>() } }; CacheContext.Serializer.Serialize(dataContext, header); while (reader.BaseStream.Position < dataref.Length) // read the rest of dataref { if (section.Headers.Count == 2) // remove "wrongfully" added ones { section.Headers.RemoveAt(1); } section.Headers.Add(CacheContext.Deserializer.Deserialize <ScenarioLightmapBspDataSection.Header>(dataContext)); // if some values match header1, continue if (section.Headers[0].Position == section.Headers[1].Position) { header = section.Headers[1]; CacheContext.Serializer.Serialize(dataContext, header); while (reader.BaseStream.Position < dataref.Length) { section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); } } else // if read data doesn't match, go back and just read 4 bytes { reader.BaseStream.Position = reader.BaseStream.Position - 0x2C; // if read data doesn't match, go back and serialize section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); section.VertexLists.Vertex.Add(new ScenarioLightmapBspDataSection.VertexList.Datum { Value = reader.ReadByte() }); outWriter.Write(section.VertexLists.Vertex[section.VertexLists.Vertex.Count - 1].Value); } } // Write back to tag outStream.Position = 0; geometry.UnknownSections[i].Unknown = outStream.ToArray(); } } // // Set up ElDorado resource reference // geometry.Resource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.RenderGeometry, DefinitionData = new byte[0x30], DefinitionAddress = new CacheAddress(CacheAddressType.Definition, 0), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // // Port Blam resource definition // var resourceEntry = BlamCache.ResourceGestalt.TagResources[geometry.ZoneAssetHandle & ushort.MaxValue]; geometry.Resource.Resource.DefinitionAddress = resourceEntry.DefinitionAddress; geometry.Resource.Resource.DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(); RenderGeometryApiResourceDefinition resourceDefinition = null; if (geometry.Resource.Resource.DefinitionData.Length < 0x30) { resourceDefinition = new RenderGeometryApiResourceDefinition { VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(), IndexBuffers = new List <TagStructureReference <IndexBufferDefinition> >() }; } else { using (var definitionStream = new MemoryStream(geometry.Resource.Resource.DefinitionData, true)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian)) { foreach (var fixup in resourceEntry.ResourceFixups) { definitionStream.Position = fixup.BlockOffset; definitionWriter.Write(fixup.Address.Value); geometry.Resource.Resource.ResourceFixups.Add(fixup); } foreach (var definitionFixup in resourceEntry.ResourceDefinitionFixups) { var newDefinitionFixup = new TagResourceGen3.ResourceDefinitionFixup { Address = definitionFixup.Address, ResourceStructureTypeIndex = definitionFixup.ResourceStructureTypeIndex }; geometry.Resource.Resource.ResourceDefinitionFixups.Add(newDefinitionFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheAddressType.Definition); definitionStream.Position = geometry.Resource.Resource.DefinitionAddress.Offset; resourceDefinition = BlamCache.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(dataContext); } } // // Load Blam resource data // var resourceData = BlamCache.GetRawFromID(geometry.ZoneAssetHandle); var generateParticles = false; if (resourceData == null) { if (geometry.Meshes.Count == 1 && geometry.Meshes[0].Type == VertexType.ParticleModel) { generateParticles = true; resourceData = new byte[0]; } else { geometry.Resource.Resource.ResourceType = TagResourceTypeGen3.None; return(geometry); } } // // Convert Blam data to ElDorado data // using (var dataStream = new MemoryStream()) using (var blamResourceStream = new MemoryStream(resourceData)) { for (int i = 0; i < geometry.Meshes.Count(); i++) { var mesh = geometry.Meshes[i]; if (mesh.VertexBufferIndices[6] != 0xFFFF && mesh.VertexBufferIndices[7] != 0xFFFF) { ushort temp = mesh.VertexBufferIndices[6]; mesh.VertexBufferIndices[6] = mesh.VertexBufferIndices[7]; mesh.VertexBufferIndices[7] = temp; // Get total amount of indices int indexCount = 0; foreach (var subpart in mesh.SubParts) { indexCount += subpart.IndexCount; } WaterConversionData waterData = new WaterConversionData() { IndexBufferLength = indexCount, }; for (int j = 0; j < mesh.Parts.Count(); j++) { var part = mesh.Parts[j]; waterData.PartData.Add(new Tuple <int, int, bool>(part.FirstIndex, part.IndexCount, part.FlagsNew.HasFlag(Mesh.Part.PartFlagsNew.CanBeRenderedInDrawBundles))); } waterData.Sort(); WaterData.Add(waterData); } } if (generateParticles) { var outVertexStream = VertexStreamFactory.Create(CacheContext.Version, dataStream); StreamUtil.Align(dataStream, 4); resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition> { Definition = new VertexBufferDefinition { Format = VertexBufferFormat.ParticleModel, Data = new TagData { Size = 32, Unused4 = 0, Unused8 = 0, Address = new CacheAddress(CacheAddressType.Resource, (int)dataStream.Position), Unused10 = 0 } } }); var vertexBuffer = resourceDefinition.VertexBuffers.Last().Definition; for (var j = 0; j < 3; j++) { outVertexStream.WriteParticleModelVertex(new ParticleModelVertex { Position = new RealVector3d(), Texcoord = new RealVector2d(), Normal = new RealVector3d() }); } geometry.Meshes[0].VertexBufferIndices[0] = (ushort)resourceDefinition.VertexBuffers.IndexOf(resourceDefinition.VertexBuffers.Last()); geometry.Meshes[0].IndexBufferIndices[0] = CreateIndexBuffer(resourceDefinition, dataStream, 3); } else { for (int i = 0, prevVertCount = -1; i < resourceDefinition.VertexBuffers.Count; i++, prevVertCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count) { blamResourceStream.Position = resourceDefinition.VertexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[i].Offset; ConvertVertexBuffer(resourceDefinition, blamResourceStream, dataStream, i, prevVertCount); } for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++) { blamResourceStream.Position = resourceDefinition.IndexBuffers[i].Definition.Data.Address.Offset; // resourceEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset; ConvertIndexBuffer(resourceDefinition, blamResourceStream, dataStream, i); } foreach (var mesh in geometry.Meshes) { if (!mesh.Flags.HasFlag(MeshFlags.MeshIsUnindexed)) { continue; } var indexCount = 0; foreach (var part in mesh.Parts) { indexCount += part.IndexCount; } mesh.IndexBufferIndices[0] = CreateIndexBuffer(resourceDefinition, dataStream, indexCount); } } // // Swap order of water vertex buffers // for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++) { var vertexBuffer = resourceDefinition.VertexBuffers[i]; if (vertexBuffer.Definition.Format == VertexBufferFormat.Unknown1B) { TagStructureReference <VertexBufferDefinition> temp = vertexBuffer; resourceDefinition.VertexBuffers[i] = resourceDefinition.VertexBuffers[i - 1]; resourceDefinition.VertexBuffers[i - 1] = temp; } } // // Finalize the new ElDorado geometry resource // var cache = CacheContext.GetResourceCache(ResourceLocation.Resources); if (!resourceStreams.ContainsKey(ResourceLocation.Resources)) { resourceStreams[ResourceLocation.Resources] = portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Resources); if (portingFlags.HasFlag(PortTagCommand.PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Resources)) resourceStream.CopyTo(resourceStreams[ResourceLocation.Resources]); } } geometry.Resource.ChangeLocation(ResourceLocation.Resources); geometry.Resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Resources], dataStream.ToArray(), out uint compressedSize); geometry.Resource.Page.CompressedBlockSize = compressedSize; geometry.Resource.Page.UncompressedBlockSize = (uint)dataStream.Length; geometry.Resource.DisableChecksum(); var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); } return(geometry); }
public static GeometryReference ConvertGeometry(GeometryReference geometry, OpenTagCache srcInfo, ResourceDataManager srcResources, OpenTagCache destInfo, ResourceDataManager destResources) { if (geometry == null || geometry.Resource == null || geometry.Resource.Index < 0) { return(geometry); } // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally var srcCompare = Definition.Compare(srcInfo.Version, DefinitionSet.HaloOnline235640); var destCompare = Definition.Compare(destInfo.Version, DefinitionSet.HaloOnline235640); if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0)) { geometry.Resource = ConvertResource(geometry.Resource, srcInfo, srcResources, destInfo, destResources); return(geometry); } Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.Index, geometry.Resource.GetLocation()); using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcResources.Extract(geometry.Resource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(srcInfo.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(geometry.Resource); var definition = srcInfo.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } // Update the definition data destInfo.Serializer.Serialize(resourceContext, definition); // Now inject the new resource data var newLocation = FixResourceLocation(geometry.Resource.GetLocation(), srcInfo.Version, destInfo.Version); outStream.Position = 0; destResources.Add(geometry.Resource, newLocation, outStream); } return(geometry); }
public override object Execute(List <string> args) { if (args.Count != 3) { return(false); } var variantName = args[0]; var fileType = args[1].ToLower(); var modelFileName = args[2]; switch (fileType) { case "obj": case "amf": case "dae": break; default: throw new NotImplementedException(fileType); } // // Find the variant to extract // if (Definition.RenderModel == null) { Console.WriteLine("The model does not have a render model associated with it."); return(true); } var modelVariant = Definition.Variants.FirstOrDefault(v => (CacheContext.GetString(v.Name) ?? v.Name.ToString()) == variantName); if (modelVariant == null && Definition.Variants.Count > 0 && fileType != "dae") { Console.WriteLine("Unable to find variant \"{0}\"", variantName); Console.WriteLine("Use \"listvariants\" to list available variants."); return(true); } // // Deserialize the render model tag // RenderModel renderModel; using (var cacheStream = CacheContext.TagCacheFile.OpenRead()) { renderModel = CacheContext.Deserialize <RenderModel>(cacheStream, Definition.RenderModel); } if (renderModel.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return(true); } // // Deserialize the resource definition // var resourceContext = new ResourceSerializationContext(CacheContext, renderModel.Geometry.Resource); var resourceDefinition = CacheContext.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // // Extract the resource data // CacheContext.ExtractResource(renderModel.Geometry.Resource, resourceStream); var modelFile = new FileInfo(modelFileName); if (!modelFile.Directory.Exists) { modelFile.Directory.Create(); } switch (fileType) { case "obj": ExtractObj(modelFile, renderModel, modelVariant, resourceDefinition, resourceStream); break; case "amf": ExtractAmf(modelFile, renderModel, modelVariant, resourceDefinition, resourceStream); break; case "dae": ModelExtractor extractor = new ModelExtractor(CacheContext, renderModel); extractor.ExtractRenderModel(); extractor.ExportCollada(modelFile); break; default: throw new NotImplementedException(fileType); } } Console.WriteLine("Done!"); return(true); }
private Sound ConvertSound(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, Sound sound, string blamTag_Name) { if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } if (!File.Exists(@"Tools\ffmpeg.exe") || !File.Exists(@"Tools\towav.exe")) { Console.WriteLine("Failed to locate sound conversion tools, please install ffmpeg and towav in the Tools folder"); return(null); } // // Convert Sound Tag Data // var platformCodec = BlamSoundGestalt.PlatformCodecs[sound.SoundReference.PlatformCodecIndex]; var playbackParameters = BlamSoundGestalt.PlaybackParameters[sound.SoundReference.PlaybackParameterIndex]; var scale = BlamSoundGestalt.Scales[sound.SoundReference.ScaleIndex]; var promotion = sound.SoundReference.PromotionIndex != -1 ? BlamSoundGestalt.Promotions[sound.SoundReference.PromotionIndex] : new Promotion(); var customPlayBack = sound.SoundReference.CustomPlaybackIndex != -1 ? new List <CustomPlayback> { BlamSoundGestalt.CustomPlaybacks[sound.SoundReference.CustomPlaybackIndex] } : new List <CustomPlayback>(); var loop = sound.Flags.HasFlag(Sound.FlagsValue.LoopingSound); sound.PlaybackParameters = playbackParameters; sound.Scale = scale; sound.PlatformCodec = platformCodec.DeepClone(); sound.Promotion = promotion; sound.CustomPlayBacks = customPlayBack; // // Tag fixes // sound.SampleRate = platformCodec.SampleRate; sound.ImportType = ImportType.SingleLayer; // helps looping sound? there is another value, 10 for Unknown2 but I don't know when to activate it. if (sound.SoundReference.PitchRangeCount > 1) { sound.ImportType = ImportType.MultiLayer; } sound.PlatformCodec.LoadMode = 0; // // Process all the pitch ranges // var xmaFileSize = BlamSoundGestalt.GetFileSize(sound.SoundReference.PitchRangeIndex, sound.SoundReference.PitchRangeCount); if (xmaFileSize < 0) { return(null); } var xmaData = BlamCache.GetSoundRaw(sound.SoundReference.ZoneAssetHandle, xmaFileSize); if (xmaData == null) { return(null); } sound.PitchRanges = new List <PitchRange>(sound.SoundReference.PitchRangeCount); var soundDataAggregate = new byte[0].AsEnumerable(); var currentSoundDataOffset = 0; var totalSampleCount = (uint)0; for (int pitchRangeIndex = sound.SoundReference.PitchRangeIndex; pitchRangeIndex < sound.SoundReference.PitchRangeIndex + sound.SoundReference.PitchRangeCount; pitchRangeIndex++) { totalSampleCount += BlamSoundGestalt.GetSamplesPerPitchRange(pitchRangeIndex); // // Convert Blam pitch range to ElDorado format // var pitchRange = BlamSoundGestalt.PitchRanges[pitchRangeIndex]; pitchRange.ImportName = ConvertStringId(BlamSoundGestalt.ImportNames[pitchRange.ImportNameIndex].Name); pitchRange.PitchRangeParameters = BlamSoundGestalt.PitchRangeParameters[pitchRange.PitchRangeParametersIndex]; pitchRange.Unknown1 = 0; pitchRange.Unknown2 = 0; pitchRange.Unknown3 = 0; pitchRange.Unknown4 = 0; pitchRange.Unknown5 = -1; pitchRange.Unknown6 = -1; //I suspect this unknown7 to be a flag to tell if there is a Unknownblock in extrainfo. (See a sound in udlg for example) pitchRange.Unknown7 = 0; pitchRange.PermutationCount = (byte)BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); pitchRange.Unknown8 = -1; // Add pitch range to ED sound sound.PitchRanges.Add(pitchRange); var newPitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex; sound.PitchRanges[newPitchRangeIndex].Permutations = new List <Permutation>(); // // Determine the audio channel count // var channelCount = Encoding.GetChannelCount(sound.PlatformCodec.Encoding); // // Set compression format // sound.PlatformCodec.Compression = Compression.MP3; // // Convert Blam permutations to ElDorado format // var useCache = Sounds.UseAudioCacheCommand.AudioCacheDirectory != null; var basePermutationCacheName = Path.Combine( useCache ? Sounds.UseAudioCacheCommand.AudioCacheDirectory.FullName : TempDirectory.FullName, GetTagFileFriendlyName(blamTag_Name)); var permutationCount = BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); var permutationOrder = BlamSoundGestalt.GetPermutationOrder(pitchRangeIndex); var relativePitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex; for (int i = 0; i < permutationCount; i++) { var permutationIndex = pitchRange.FirstPermutationIndex + i; var permutationSize = BlamSoundGestalt.GetPermutationSize(permutationIndex); var permutationOffset = BlamSoundGestalt.GetPermutationOffset(permutationIndex); var permutation = BlamSoundGestalt.GetPermutation(permutationIndex).DeepClone(); permutation.ImportName = ConvertStringId(BlamSoundGestalt.ImportNames[permutation.ImportNameIndex].Name); permutation.SkipFraction = new Bounds <float>(0.0f, permutation.Gain); permutation.PermutationChunks = new List <PermutationChunk>(); permutation.PermutationNumber = (uint)permutationOrder[i]; permutation.IsNotFirstPermutation = (uint)(permutation.PermutationNumber == 0 ? 0 : 1); string permutationName = $"{basePermutationCacheName}_{relativePitchRangeIndex}_{i}"; string extension = "mp3"; var cacheFileName = $"{permutationName}.{extension}"; bool exists = File.Exists(cacheFileName); byte[] permutationData = null; if (!exists || !useCache) { BlamSound blamSound = SoundConverter.ConvertGen3Sound(BlamCache, BlamSoundGestalt, sound, relativePitchRangeIndex, i, xmaData); permutationData = blamSound.Data; if (useCache) { using (EndianWriter output = new EndianWriter(new FileStream(cacheFileName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { output.WriteBlock(blamSound.Data); } } } else { permutationData = File.ReadAllBytes(cacheFileName); } permutation.PermutationChunks.Add(new PermutationChunk(currentSoundDataOffset, permutationData.Length)); currentSoundDataOffset += permutationData.Length; pitchRange.Permutations.Add(permutation); soundDataAggregate = soundDataAggregate.Concat(permutationData); } } sound.Promotion.LongestPermutationDuration = (uint)sound.SoundReference.MaximumPlayTime; sound.Promotion.TotalSampleSize = totalSampleCount; // // Convert Blam extra info to ElDorado format // var extraInfo = new ExtraInfo() { LanguagePermutations = new List <ExtraInfo.LanguagePermutation>(), EncodedPermutationSections = new List <ExtraInfo.EncodedPermutationSection>() }; for (int u = 0; u < sound.SoundReference.PitchRangeCount; u++) { var pitchRange = BlamSoundGestalt.PitchRanges[sound.SoundReference.PitchRangeIndex + u]; for (int i = 0; i < pitchRange.PermutationCount; i++) { var permutation = BlamSoundGestalt.GetPermutation(pitchRange.FirstPermutationIndex + i); var permutationChunk = BlamSoundGestalt.GetPermutationChunk(permutation.FirstPermutationChunkIndex); extraInfo.LanguagePermutations.Add(new ExtraInfo.LanguagePermutation { RawInfo = new List <ExtraInfo.LanguagePermutation.RawInfoBlock> { new ExtraInfo.LanguagePermutation.RawInfoBlock { SkipFractionName = StringId.Invalid, UnknownList = new List <ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown> { new ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown { Unknown1 = permutationChunk.UnknownA, Unknown2 = permutationChunk.UnknownSize, Unknown3 = 0, Unknown4 = permutation.SampleSize, Unknown5 = 0, Unknown6 = permutationChunk.EncodedSize & 0xFFFF } }, Compression = 8, ResourceSampleSize = pitchRange.Permutations[i].SampleSize, ResourceSampleOffset = (uint)pitchRange.Permutations[i].PermutationChunks[0].Offset, SampleCount = (uint)pitchRange.Permutations[i].PermutationChunks[0].EncodedSize & 0x3FFFFFF, //SampleCount = (uint)Math.Floor(pitchRange.Permutations[i].SampleSize * 128000.0 / (8 * sound.SampleRate.GetSampleRateHz())), Unknown24 = 480 } } }); } } if (sound.SoundReference.ExtraInfoIndex != -1) { foreach (var section in BlamSoundGestalt.ExtraInfo[sound.SoundReference.ExtraInfoIndex].EncodedPermutationSections) { var newSection = section.DeepClone(); foreach (var info in newSection.SoundDialogueInfo) { for (var i = ((info.MouthDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.MouthDataLength; i += 2) { Array.Reverse(newSection.EncodedData, (int)(info.MouthDataOffset + i), 2); } for (var i = ((info.LipsyncDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.LipsyncDataLength; i += 2) { Array.Reverse(newSection.EncodedData, (int)(info.LipsyncDataOffset + i), 2); } } extraInfo.EncodedPermutationSections.Add(newSection); } } sound.ExtraInfo = new List <ExtraInfo> { extraInfo }; // // Convert Blam languages to ElDorado format // if (sound.SoundReference.LanguageIndex != -1) { sound.Languages = new List <LanguageBlock>(); foreach (var language in BlamSoundGestalt.Languages) { sound.Languages.Add(new LanguageBlock { Language = language.Language, PermutationDurations = new List <LanguageBlock.PermutationDurationBlock>(), PitchRangeDurations = new List <LanguageBlock.PitchRangeDurationBlock>(), }); //Add all the Pitch Range Duration block (pitch range count dependent) var curLanguage = sound.Languages.Last(); for (int i = 0; i < sound.SoundReference.PitchRangeCount; i++) { curLanguage.PitchRangeDurations.Add(language.PitchRangeDurations[sound.SoundReference.LanguageIndex + i]); } //Add all the Permutation Duration Block and adjust offsets for (int i = 0; i < curLanguage.PitchRangeDurations.Count; i++) { var curPRD = curLanguage.PitchRangeDurations[i]; //Get current block count for new index short newPermutationIndex = (short)curLanguage.PermutationDurations.Count(); for (int j = curPRD.PermutationStartIndex; j < curPRD.PermutationStartIndex + curPRD.PermutationCount; j++) { curLanguage.PermutationDurations.Add(language.PermutationDurations[j]); } //apply new index curPRD.PermutationStartIndex = newPermutationIndex; } } } // // Prepare resource // sound.Unused = new byte[] { 0, 0, 0, 0 }; sound.Unknown12 = 0; sound.Resource = new PageableResource { Page = new RawPage { Index = -1, }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Sound, DefinitionData = new byte[20], DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 536870912), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; var data = soundDataAggregate.ToArray(); var resourceContext = new ResourceSerializationContext(CacheContext, sound.Resource); CacheContext.Serializer.Serialize(resourceContext, new SoundResourceDefinition { Data = new TagData(data.Length, new CacheResourceAddress(CacheResourceAddressType.Resource, 0)) }); var definitionFixup = new TagResourceGen3.ResourceFixup() { BlockOffset = 12, Address = new CacheResourceAddress(CacheResourceAddressType.Resource, 1073741824) }; sound.Resource.Resource.ResourceFixups.Add(definitionFixup); sound.Resource.ChangeLocation(ResourceLocation.Audio); var resource = sound.Resource; if (resource == null) { throw new ArgumentNullException("resource"); } var cache = CacheContext.GetResourceCache(ResourceLocation.Audio); if (!resourceStreams.ContainsKey(ResourceLocation.Audio)) { resourceStreams[ResourceLocation.Audio] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Audio); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Audio)) resourceStream.CopyTo(resourceStreams[ResourceLocation.Audio]); } } resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Audio], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)data.Length; resource.DisableChecksum(); for (int i = 0; i < 4; i++) { sound.Resource.Resource.DefinitionData[i] = (byte)(sound.Resource.Page.UncompressedBlockSize >> (i * 8)); } return(sound); }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream) { var resource = bitmap.Resources[imageIndex].Resource; var newResource = (resource == null); ResourceSerializationContext resourceContext; BitmapTextureResourceDefinition definition; if (newResource) { // Create a new resource reference resource = new ResourceReference { DefinitionFixups = new List <ResourceDefinitionFixup>(), D3DObjectFixups = new List <D3DObjectFixup>(), Type = 1, // TODO: Map out this type enum instead of using numbers Unknown68 = 1 }; bitmap.Resources[imageIndex].Resource = resource; resourceContext = new ResourceSerializationContext(resource); definition = new BitmapTextureResourceDefinition { Texture = new D3DPointer <BitmapTextureResourceDefinition.BitmapDefinition> { Definition = new BitmapTextureResourceDefinition.BitmapDefinition() } }; } else { // Deserialize the old definition resourceContext = new ResourceSerializationContext(resource); definition = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); } if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var texture = definition.Texture.Definition; var imageData = bitmap.Images[imageIndex]; // Read the DDS header and modify the definition to match var dds = DdsHeader.Read(ddsStream); var dataSize = (int)(ddsStream.Length - ddsStream.Position); texture.Data = new ResourceDataReference(dataSize, new ResourceAddress(ResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.Levels = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormatUnused = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (texture.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: texture.Flags = BitmapFlags.Compressed; break; default: texture.Flags = BitmapFlags.None; break; } if ((texture.Width & (texture.Width - 1)) == 0 && (texture.Height & (texture.Height - 1)) == 0) { texture.Flags |= BitmapFlags.PowerOfTwoDimensions; } // If creating a new image, then add a new resource, otherwise replace the existing one if (newResource) { _resourceManager.Add(resource, ResourceLocation.Textures, ddsStream); } else { _resourceManager.Replace(resource, ddsStream); } // Serialize the new resource definition serializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition imageData.Width = texture.Width; imageData.Height = texture.Height; imageData.Depth = texture.Depth; imageData.Type = texture.Type; imageData.Format = texture.Format; imageData.Flags = texture.Flags; imageData.MipmapCount = (sbyte)(texture.Levels - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; imageData.Unknown15 = texture.Unknown35; }
public override object Execute(List <string> args) { if (args.Count != 1) { return(false); } var outDirectory = args[0]; if (!Directory.Exists(outDirectory)) { Console.Write("Destination directory does not exist. Create it? [y/n] "); var answer = Console.ReadLine().ToLower(); if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n"))) { return(false); } if (answer.StartsWith("y")) { Directory.CreateDirectory(outDirectory); } else { return(false); } } var resource = Definition.Resource; var resourceContext = new ResourceSerializationContext(CacheContext, resource); var resourceDefinition = CacheContext.Deserializer.Deserialize <SoundResourceDefinition>(resourceContext); if (resourceDefinition.Data == null) { Console.WriteLine("Invalid sound definition"); return(false); } var dataReference = resourceDefinition.Data; byte[] soundData = new byte[dataReference.Size]; var resourceDataStream = new MemoryStream(soundData); CacheContext.ExtractResource(resource, resourceDataStream); for (int i = 0; i < Definition.PitchRanges.Count; i++) { var pitchRange = Definition.PitchRanges[i]; for (int j = 0; j < pitchRange.Permutations.Count; j++) { var permutation = pitchRange.Permutations[j]; var filename = Tag.Index.ToString("X8") + "_" + i.ToString() + "_" + j.ToString(); byte[] permutationData = new byte[permutation.PermutationChunks[0].EncodedSize & 0x3FFFFFF]; Array.Copy(soundData, permutation.PermutationChunks[0].Offset, permutationData, 0, permutationData.Length); switch (Definition.PlatformCodec.Compression) { case Compression.PCM: filename += ".wav"; break; case Compression.MP3: filename += ".mp3"; break; case Compression.FSB4: filename += ".fsb"; break; } var outPath = Path.Combine(outDirectory, filename); using (EndianWriter writer = new EndianWriter(new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { var channelCount = Encoding.GetChannelCount(Definition.PlatformCodec.Encoding); var sampleRate = Definition.SampleRate.GetSampleRateHz(); switch (Definition.PlatformCodec.Compression) { case Compression.PCM: WAVFile WAVfile = new WAVFile(permutationData, channelCount, sampleRate); WAVfile.Write(writer); break; case Compression.MP3: case Compression.FSB4: writer.Write(permutationData); break; } } } } Console.WriteLine("Done!"); return(true); }
public override bool Execute(List <string> args) { if (args.Count != 2) { return(false); } var destDir = new DirectoryInfo(args[1]); if (!destDir.Exists) { WriteLine($"Destination cache directory does not exist: {destDir.FullName}"); return(false); } var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); if (!destTagsFile.Exists) { WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}"); return(false); } var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); if (!destStringIDsFile.Exists) { WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}"); return(false); } var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat")); if (!destResourcesFile.Exists) { WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}"); return(false); } var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat")); if (!destTexturesFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}"); return(false); } var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat")); if (!destTexturesBFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}"); return(false); } var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat")); if (!destAudioFile.Exists) { WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}"); return(false); } TagCache destTagCache; using (var stream = destTagsFile.OpenRead()) destTagCache = new TagCache(stream); DefinitionSet guessedVersion; var destVersion = Detect(destTagCache, out guessedVersion); if (destVersion == Unknown) { WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})"); return(true); } WriteLine($"Destination cache version: {GetVersionString(destVersion)}"); StringIDCache destStringIDCache; using (var stream = destStringIDsFile.OpenRead()) destStringIDCache = new StringIDCache(stream, Create(destVersion)); var destResources = new ResourceDataManager(); destResources.LoadCachesFromDirectory(destDir.FullName); var srcResources = new ResourceDataManager(); srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var destSerializer = new TagSerializer(destVersion); var destDeserializer = new TagDeserializer(destVersion); var destInfo = new OpenTagCache { Cache = destTagCache, CacheFile = destTagsFile, StringIDs = destStringIDCache, StringIDsFile = destStringIDsFile, Version = destVersion, Serializer = destSerializer, Deserializer = destDeserializer }; var destTag = ParseTagIndex(destInfo, args[0]); if (destTag == null || !destTag.IsInGroup(new Tag("mode"))) { WriteLine("Destination tag must be of group 'mode'."); return(false); } RenderModel destDefinition; using (var destStream = destInfo.OpenCacheRead()) { var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag); destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context); } using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcResources.Extract(Definition.Geometry.Resource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(Info.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource); var definition = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } destInfo.Serializer.Serialize(resourceContext, definition); outStream.Position = 0; destResources.Replace(destDefinition.Geometry.Resource, outStream); } return(true); }
private RenderGeometry ConvertGeometry(RenderGeometry geometry, GameCacheContextHaloOnline srcCacheContext, GameCacheContextHaloOnline destCacheContext) { if (geometry == null || geometry.Resource.HaloOnlinePageableResource == null || geometry.Resource.HaloOnlinePageableResource.Page.Index < 0 || !geometry.Resource.HaloOnlinePageableResource.GetLocation(out var location)) { return(geometry); } // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally var srcCompare = CacheVersionDetection.Compare(srcCacheContext.Version, CacheVersion.HaloOnline235640); var destCompare = CacheVersionDetection.Compare(destCacheContext.Version, CacheVersion.HaloOnline235640); if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0)) { geometry.Resource.HaloOnlinePageableResource = ConvertResource(geometry.Resource.HaloOnlinePageableResource, srcCacheContext, destCacheContext); return(geometry); } Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.HaloOnlinePageableResource.Page.Index, location); using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcCacheContext.ExtractResource(geometry.Resource.HaloOnlinePageableResource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(srcCacheContext.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destCacheContext.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(CacheContext, geometry.Resource.HaloOnlinePageableResource); var definition = srcCacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { ConvertVertexBuffer(srcCacheContext, destCacheContext, buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new CacheAddress(CacheAddressType.Data, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } // Update the definition data destCacheContext.Serializer.Serialize(resourceContext, definition); // Now inject the new resource data var newLocation = FixResourceLocation(location, srcCacheContext.Version, destCacheContext.Version); outStream.Position = 0; geometry.Resource.HaloOnlinePageableResource.ChangeLocation(newLocation); destCacheContext.AddResource(geometry.Resource.HaloOnlinePageableResource, outStream); } return(geometry); }
public override object Execute(List <string> args) { if (args.Count != 0) { return(false); } var soundDataAggregate = new byte[0].AsEnumerable(); int currentFileOffset = 0; int totalSampleCount = 0; int maxPermutationSampleCount = 0; int pitchRangeCount = GetPitchRangeCountUser(); if (pitchRangeCount <= 0) { return(false); } // // Get basic information on the sounds // if (pitchRangeCount > 1) { Definition.ImportType = ImportType.MultiLayer; } else { Definition.ImportType = ImportType.SingleLayer; } Definition.SampleRate.value = GetSoundSampleRateUser(); Definition.PlatformCodec.Compression = GetSoundCompressionUser(); Definition.PlatformCodec.Encoding = GetSoundEncodingUser(); Definition.PitchRanges = new List <PitchRange>(); // // For each pitch range, get all the permutations and append sound data. // for (int u = 0; u < pitchRangeCount; u++) { int permutationCount = GetPermutationCountUser(); if (permutationCount <= 0) { return(false); } var pitchRange = new PitchRange { ImportName = new StringId(5221), //|default| Unknown5 = -1, Unknown6 = -1, Unknown7 = -1, Unknown8 = -1, PermutationCount = (short)permutationCount, PitchRangeParameters = new PitchRangeParameter() }; pitchRange.PitchRangeParameters.UnknownBounds = new Bounds <short>(-32768, 32767); pitchRange.Permutations = new List <Permutation>(); // // Permutation section // for (int i = 0; i < permutationCount; i++) { string soundFile = GetPermutationFileUser(i); if (soundFile == null) { return(false); } int sampleCount = GetPermutationSampleCountUser(i); var perm = new Permutation { ImportName = StringId.Invalid, SampleSize = (uint)sampleCount }; if (i != 0) { perm.IsNotFirstPermutation = 1; } perm.PermutationNumber = (uint)i; var permutationData = File.ReadAllBytes(soundFile); perm.PermutationChunks = new List <PermutationChunk>(); var chunk = new PermutationChunk(currentFileOffset, permutationData.Length); perm.PermutationChunks.Add(chunk); currentFileOffset += permutationData.Length; totalSampleCount += sampleCount; if (maxPermutationSampleCount < sampleCount) { maxPermutationSampleCount = sampleCount; } soundDataAggregate = soundDataAggregate.Concat(permutationData); pitchRange.Permutations.Add(perm); } Definition.PitchRanges.Add(pitchRange); } Definition.Promotion.LongestPermutationDuration = (uint)(1000 * (double)maxPermutationSampleCount / (Definition.SampleRate.GetSampleRateHz())); Definition.Promotion.TotalSampleSize = (uint)totalSampleCount; // remove extra info for now Definition.ExtraInfo = new List <ExtraInfo>(); // // Create new resource // Console.Write("Creating new sound resource..."); var data = soundDataAggregate.ToArray(); Definition.Unknown12 = 0; using (var dataStream = new MemoryStream(data)) { var fileSize = (int)dataStream.Length; var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Resource); CacheContext.Serializer.Serialize(resourceContext, new SoundResourceDefinition { Data = new TagData(fileSize, new CacheResourceAddress(CacheResourceAddressType.Resource, 0)) }); Definition.Resource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Sound, DefinitionData = new byte[20], DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 536870912), ResourceFixups = new List <TagResourceGen3.ResourceFixup> { new TagResourceGen3.ResourceFixup { BlockOffset = 12, Address = new CacheResourceAddress(CacheResourceAddressType.Resource, 1073741824) } }, ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; Definition.Resource.ChangeLocation(ResourceLocation.ResourcesB); CacheContext.AddResource(Definition.Resource, dataStream); for (int i = 0; i < 4; i++) { Definition.Resource.Resource.DefinitionData[i] = (byte)(Definition.Resource.Page.UncompressedBlockSize >> (i * 8)); } Console.WriteLine("done."); } return(true); }
public override object Execute(List <string> args) { string variantName; string fileType; string modelFileName; if (args.Count == 2) { variantName = "*"; fileType = args[0].ToLower(); modelFileName = args[1]; } else if (args.Count == 3) { variantName = args[0]; fileType = args[1].ToLower(); modelFileName = args[2]; } else { return(false); } switch (fileType) { case "obj": case "dae": break; default: throw new NotImplementedException(fileType); } if (Definition.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return(true); } // // Deserialize the resource definition // var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Geometry.Resource); var resourceDefinition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // // Extract the resource data // CacheContext.ExtractResource(Definition.Geometry.Resource, resourceStream); var modelFile = new FileInfo(modelFileName); if (!modelFile.Directory.Exists) { modelFile.Directory.Create(); } ModelExtractor extractor = new ModelExtractor(CacheContext, Definition); extractor.ExtractRenderModel(variantName); switch (fileType) { case "obj": return(extractor.ExportObject(modelFile)); case "dae": return(extractor.ExportCollada(modelFile)); default: throw new NotImplementedException(fileType); } } }
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 override object Execute(List <string> args) { // Deserialize the definition data var resourceContext = new ResourceSerializationContext(CacheContext, BSP.CollisionBspResource); var definition = CacheContext.Deserializer.Deserialize <StructureBspTagResources>(resourceContext); // Extract the resource data var resourceDataStream = new MemoryStream(); CacheContext.ExtractResource(BSP.CollisionBspResource, resourceDataStream); using (var reader = new EndianReader(resourceDataStream)) { #region collision bsps foreach (var cbsp in definition.CollisionBsps) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); cbsp.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); cbsp.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); cbsp.Vertices.Add((CollisionGeometry.Vertex)element); } } #endregion #region large collision bsps foreach (var cbsp in definition.LargeCollisionBsps) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)); cbsp.Bsp3dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)); cbsp.Bsp2dReferences.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)); cbsp.Bsp2dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Surface)); cbsp.Surfaces.Add((StructureBspTagResources.LargeCollisionBspBlock.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Edge)); cbsp.Edges.Add((StructureBspTagResources.LargeCollisionBspBlock.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Vertex)); cbsp.Vertices.Add((StructureBspTagResources.LargeCollisionBspBlock.Vertex)element); } } #endregion #region compressions foreach (var instance in definition.InstancedGeometry) { #region compression's resource data reader.BaseStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); instance.CollisionInfo.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = instance.CollisionInfo.Planes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); instance.CollisionInfo.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = instance.CollisionInfo.Leaves.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); instance.CollisionInfo.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); instance.CollisionInfo.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); instance.CollisionInfo.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = instance.CollisionInfo.Surfaces.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); instance.CollisionInfo.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = instance.CollisionInfo.Edges.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); instance.CollisionInfo.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = instance.CollisionInfo.Vertices.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); instance.CollisionInfo.Vertices.Add((CollisionGeometry.Vertex)element); } #endregion #region compression's other resource data foreach (var cbsp in instance.CollisionGeometries) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); cbsp.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); cbsp.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); cbsp.Vertices.Add((CollisionGeometry.Vertex)element); } } #endregion #region Unknown Data for (var i = 0; i < instance.Unknown1.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)); instance.Unknown1.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)element); } for (var i = 0; i < instance.Unknown2.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)); instance.Unknown2.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)element); } for (var i = 0; i < instance.Unknown3.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)); instance.Unknown3.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)element); } #endregion #region compression's havok collision data foreach (var collision in instance.BspPhysics) { for (var i = 0; i < collision.Data.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(byte)); collision.Data.Add(new StructureBspTagResources.CollisionBspPhysicsBlock.Datum { Value = (byte)element }); } } #endregion } #endregion } return(true); }
public override object Execute(List <string> args) { if (args.Count != 2) { return(false); } var fileType = args[0]; var fileName = args[1]; if (fileType != "obj") { throw new NotSupportedException(fileType); } if (Definition.Geometry2.Resource == null) { Console.WriteLine("ERROR: Render geometry does not have a resource associated with it."); return(true); } // // Deserialize the resource definition // var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Geometry2.Resource); var definition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // // Extract the resource data // CacheContext.ExtractResource(Definition.Geometry2.Resource, resourceStream); var file = new FileInfo(fileName); if (!file.Directory.Exists) { file.Directory.Create(); } using (var objFile = new StreamWriter(file.Create())) { var objExtractor = new ObjExtractor(objFile); foreach (var cluster in Definition.Clusters) { var meshReader = new MeshReader(CacheContext.Version, Definition.Geometry2.Meshes[cluster.MeshIndex], definition); objExtractor.ExtractMesh(meshReader, null, resourceStream); } foreach (var instance in Definition.InstancedGeometryInstances) { var vertexCompressor = new VertexCompressor(Definition.Geometry2.Compression[0]); var meshReader = new MeshReader(CacheContext.Version, Definition.Geometry2.Meshes[instance.InstanceDefinition], definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } objExtractor.Finish(); } } Console.WriteLine("Done!"); return(true); }
public override object Execute(List <string> args) { if (args.Count != 1) { return(false); } var resourceFile = new FileInfo(args[0]); var fileSize = 0; if (!resourceFile.Exists) { Console.WriteLine($"ERROR: File not found: \"{resourceFile.FullName}\""); return(true); } // // Create new resource // Console.Write("Creating new sound resource..."); Definition.Unknown12 = 0; using (var dataStream = resourceFile.OpenRead()) { fileSize = (int)dataStream.Length; var resourceContext = new ResourceSerializationContext(CacheContext, Definition.Resource); CacheContext.Serializer.Serialize(resourceContext, new SoundResourceDefinition { Data = new TagData(fileSize, new CacheAddress(CacheAddressType.Resource, 0)) }); Definition.Resource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Sound, DefinitionData = new byte[20], DefinitionAddress = new CacheAddress(CacheAddressType.Definition, 536870912), ResourceFixups = new List <TagResourceGen3.ResourceFixup> { new TagResourceGen3.ResourceFixup { BlockOffset = 12, Address = new CacheAddress(CacheAddressType.Resource, 1073741824) } }, ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; Definition.Resource.ChangeLocation(ResourceLocation.ResourcesB); CacheContext.AddResource(Definition.Resource, dataStream); for (int i = 0; i < 4; i++) { Definition.Resource.Resource.DefinitionData[i] = (byte)(Definition.Resource.Page.UncompressedBlockSize >> (i * 8)); } Console.WriteLine("done."); } // // Adjust tag definition to use correctly the sound file. // var chunkSize = (ushort)fileSize; var permutationChunk = new PermutationChunk(0, chunkSize); var permutation = Definition.PitchRanges[0].Permutations[0]; permutation.PermutationChunks = new List <PermutationChunk> { permutationChunk }; permutation.PermutationNumber = 0; permutation.SampleSize = 0; permutation.IsNotFirstPermutation = 0; Definition.PitchRanges[0].Permutations = new List <Permutation> { permutation }; Definition.PlatformCodec.Compression = Compression.MP3; return(true); }
public override object Execute(List <string> args) { if (args.Count != 1) { return(false); } if (Definition.CollisionBspResource == null) { Console.WriteLine("ERROR: Collision geometry does not have a resource associated with it."); return(true); } var resourceContext = new ResourceSerializationContext(CacheContext, Definition.CollisionBspResource); var resourceDefinition = CacheContext.Deserializer.Deserialize <StructureBspTagResources>(resourceContext); using (var resourceStream = new MemoryStream()) { CacheContext.ExtractResource(Definition.CollisionBspResource, resourceStream); using (var reader = new EndianReader(resourceStream)) { foreach (var cbsp in resourceDefinition.CollisionBsps) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); cbsp.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); cbsp.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); cbsp.Vertices.Add((CollisionGeometry.Vertex)element); } } foreach (var cbsp in resourceDefinition.LargeCollisionBsps) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)); cbsp.Bsp3dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)); cbsp.Bsp2dReferences.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)); cbsp.Bsp2dNodes.Add((StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Surface)); cbsp.Surfaces.Add((StructureBspTagResources.LargeCollisionBspBlock.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Edge)); cbsp.Edges.Add((StructureBspTagResources.LargeCollisionBspBlock.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.LargeCollisionBspBlock.Vertex)); cbsp.Vertices.Add((StructureBspTagResources.LargeCollisionBspBlock.Vertex)element); } } foreach (var instance in resourceDefinition.InstancedGeometry) { reader.BaseStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); instance.CollisionInfo.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = instance.CollisionInfo.Planes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); instance.CollisionInfo.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = instance.CollisionInfo.Leaves.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); instance.CollisionInfo.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); instance.CollisionInfo.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); instance.CollisionInfo.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = instance.CollisionInfo.Surfaces.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); instance.CollisionInfo.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = instance.CollisionInfo.Edges.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); instance.CollisionInfo.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = instance.CollisionInfo.Vertices.Address.Offset; for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); instance.CollisionInfo.Vertices.Add((CollisionGeometry.Vertex)element); } foreach (var cbsp in instance.CollisionGeometries) { reader.BaseStream.Position = cbsp.Bsp3dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp3dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp3dNode)); cbsp.Bsp3dNodes.Add((CollisionGeometry.Bsp3dNode)element); } reader.BaseStream.Position = cbsp.Planes.Address.Offset; for (var i = 0; i < cbsp.Planes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Plane)); cbsp.Planes.Add((CollisionGeometry.Plane)element); } reader.BaseStream.Position = cbsp.Leaves.Address.Offset; for (var i = 0; i < cbsp.Leaves.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Leaf)); cbsp.Leaves.Add((CollisionGeometry.Leaf)element); } reader.BaseStream.Position = cbsp.Bsp2dReferences.Address.Offset; for (var i = 0; i < cbsp.Bsp2dReferences.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dReference)); cbsp.Bsp2dReferences.Add((CollisionGeometry.Bsp2dReference)element); } reader.BaseStream.Position = cbsp.Bsp2dNodes.Address.Offset; for (var i = 0; i < cbsp.Bsp2dNodes.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Bsp2dNode)); cbsp.Bsp2dNodes.Add((CollisionGeometry.Bsp2dNode)element); } reader.BaseStream.Position = cbsp.Surfaces.Address.Offset; for (var i = 0; i < cbsp.Surfaces.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Surface)); cbsp.Surfaces.Add((CollisionGeometry.Surface)element); } reader.BaseStream.Position = cbsp.Edges.Address.Offset; for (var i = 0; i < cbsp.Edges.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Edge)); cbsp.Edges.Add((CollisionGeometry.Edge)element); } reader.BaseStream.Position = cbsp.Vertices.Address.Offset; for (var i = 0; i < cbsp.Vertices.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(CollisionGeometry.Vertex)); cbsp.Vertices.Add((CollisionGeometry.Vertex)element); } } for (var i = 0; i < instance.Unknown1.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)); instance.Unknown1.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown1Block)element); } for (var i = 0; i < instance.Unknown2.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)); instance.Unknown2.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown2Block)element); } for (var i = 0; i < instance.Unknown3.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)); instance.Unknown3.Add((StructureBspTagResources.InstancedGeometryBlock.Unknown3Block)element); } foreach (var collision in instance.BspPhysics) { for (var i = 0; i < collision.Data.Count; i++) { var element = CacheContext.Deserializer.DeserializeValue(reader, null, null, typeof(byte)); collision.Data.Add(new StructureBspTagResources.CollisionBspPhysicsBlock.Datum { Value = (byte)element }); } } } } var file = new FileInfo(args[0]); if (!file.Directory.Exists) { file.Directory.Create(); } using (var writer = new StreamWriter(file.Create())) { var baseVertex = 0; foreach (var bsp in resourceDefinition.CollisionBsps) { for (var i = 0; i < bsp.Vertices.Count; i++) { var vertex = bsp.Vertices[i]; writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}"); } writer.WriteLine($"g bsp_surfaces_{resourceDefinition.CollisionBsps.IndexOf(bsp)}"); for (var i = 0; i < bsp.Surfaces.Count; i++) { var surface = bsp.Surfaces[i]; var vertices = new HashSet <short>(); var edge = bsp.Edges[surface.FirstEdge]; writer.Write("f"); while (true) { if (edge.LeftSurface == i) { writer.Write($" {baseVertex + edge.StartVertex + 1}"); if (edge.ForwardEdge == surface.FirstEdge) { break; } else { edge = bsp.Edges[edge.ForwardEdge]; } } else if (edge.RightSurface == i) { writer.Write($" {baseVertex + edge.EndVertex + 1}"); if (edge.ReverseEdge == surface.FirstEdge) { break; } else { edge = bsp.Edges[edge.ReverseEdge]; } } } writer.WriteLine(); } baseVertex += bsp.Vertices.Count; } foreach (var largeBsp in resourceDefinition.LargeCollisionBsps) { for (var i = 0; i < largeBsp.Vertices.Count; i++) { var vertex = largeBsp.Vertices[i]; writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}"); } writer.WriteLine($"g large_bsp_surfaces_{resourceDefinition.LargeCollisionBsps.IndexOf(largeBsp)}"); for (var i = 0; i < largeBsp.Surfaces.Count; i++) { var surface = largeBsp.Surfaces[i]; var vertices = new HashSet <short>(); var edge = largeBsp.Edges[surface.FirstEdge]; writer.Write("f"); while (true) { if (edge.LeftSurface == i) { writer.Write($" {baseVertex + edge.StartVertex + 1}"); if (edge.ForwardEdge == surface.FirstEdge) { break; } else { edge = largeBsp.Edges[edge.ForwardEdge]; } } else if (edge.RightSurface == i) { writer.Write($" {baseVertex + edge.EndVertex + 1}"); if (edge.ReverseEdge == surface.FirstEdge) { break; } else { edge = largeBsp.Edges[edge.ReverseEdge]; } } } writer.WriteLine(); } baseVertex += largeBsp.Vertices.Count; } foreach (var instanceDef in Definition.InstancedGeometryInstances) { if (instanceDef.InstanceDefinition == -1) { continue; } var instance = resourceDefinition.InstancedGeometry[instanceDef.InstanceDefinition]; var instanceName = instanceDef.Name != StringId.Invalid ? CacheContext.GetString(instanceDef.Name) : $"instance_{instanceDef.InstanceDefinition}"; for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++) { var vertex = instance.CollisionInfo.Vertices[i]; var point = Vector3.Transform( new Vector3(vertex.Point.X, vertex.Point.Y, vertex.Point.Z), new Matrix4x4( instanceDef.Matrix.m11, instanceDef.Matrix.m12, instanceDef.Matrix.m13, 0.0f, instanceDef.Matrix.m21, instanceDef.Matrix.m22, instanceDef.Matrix.m23, 0.0f, instanceDef.Matrix.m31, instanceDef.Matrix.m32, instanceDef.Matrix.m33, 0.0f, instanceDef.Matrix.m41, instanceDef.Matrix.m42, instanceDef.Matrix.m43, 0.0f)); writer.WriteLine($"v {point.X} {point.Z} {point.Y}"); } writer.WriteLine($"g {instanceName}_main_surfaces"); for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++) { var surface = instance.CollisionInfo.Surfaces[i]; var vertices = new HashSet <short>(); var edge = instance.CollisionInfo.Edges[surface.FirstEdge]; writer.Write("f"); while (true) { if (edge.LeftSurface == i) { writer.Write($" {baseVertex + edge.StartVertex + 1}"); if (edge.ForwardEdge == surface.FirstEdge) { break; } else { edge = instance.CollisionInfo.Edges[edge.ForwardEdge]; } } else if (edge.RightSurface == i) { writer.Write($" {baseVertex + edge.EndVertex + 1}"); if (edge.ReverseEdge == surface.FirstEdge) { break; } else { edge = instance.CollisionInfo.Edges[edge.ReverseEdge]; } } } writer.WriteLine(); } baseVertex += instance.CollisionInfo.Vertices.Count; foreach (var bsp in instance.CollisionGeometries) { for (var i = 0; i < bsp.Vertices.Count; i++) { var vertex = bsp.Vertices[i]; writer.WriteLine($"v {vertex.Point.X} {vertex.Point.Z} {vertex.Point.Y}"); } writer.WriteLine($"g {instanceName}_bsp_surfaces_{resourceDefinition.CollisionBsps.IndexOf(bsp)}"); for (var i = 0; i < bsp.Surfaces.Count; i++) { var surface = bsp.Surfaces[i]; var vertices = new HashSet <short>(); var edge = bsp.Edges[surface.FirstEdge]; writer.Write("f"); while (true) { if (edge.LeftSurface == i) { writer.Write($" {baseVertex + edge.StartVertex + 1}"); if (edge.ForwardEdge == surface.FirstEdge) { break; } else { edge = bsp.Edges[edge.ForwardEdge]; } } else if (edge.RightSurface == i) { writer.Write($" {baseVertex + edge.EndVertex + 1}"); if (edge.ReverseEdge == surface.FirstEdge) { break; } else { edge = bsp.Edges[edge.ReverseEdge]; } } } writer.WriteLine(); } baseVertex += bsp.Vertices.Count; } } } } return(true); }
public void InjectDds(TagSerializer serializer, TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream ddsStream, ResourceLocation location = ResourceLocation.Textures) { var resource = bitmap.Resources[imageIndex].Resource; var newResource = (resource == null); ResourceSerializationContext resourceContext; BitmapTextureInteropResource definition; if (newResource) { // Create a new resource reference resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), ResourceType = TagResourceTypeGen3.Bitmap, Unknown2 = 1 } }; bitmap.Resources[imageIndex].Resource = resource; resourceContext = new ResourceSerializationContext(CacheContext, resource); definition = new BitmapTextureInteropResource { Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition> { Definition = new BitmapTextureInteropResource.BitmapDefinition { Data = new TagData(), UnknownData = new TagData(), } } }; } else { // Deserialize the old definition resourceContext = new ResourceSerializationContext(CacheContext, resource); definition = deserializer.Deserialize <BitmapTextureInteropResource>(resourceContext); } if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var texture = definition.Texture.Definition; var imageData = bitmap.Images[imageIndex]; // Read the DDS header and modify the definition to match var dds = DdsHeader.Read(ddsStream); var dataSize = (int)(ddsStream.Length - ddsStream.Position); texture.Data = new TagData(dataSize, new CacheResourceAddress(CacheResourceAddressType.Resource, 0)); texture.Width = (short)dds.Width; texture.Height = (short)dds.Height; texture.Depth = (sbyte)Math.Max(1, dds.Depth); texture.MipmapCount = (sbyte)Math.Max(1, dds.MipMapCount); texture.Type = BitmapDdsFormatDetection.DetectType(dds); texture.D3DFormat = (int)((dds.D3D10Format != DxgiFormat.Bc5UNorm) ? dds.FourCc : DdsFourCc.FromString("ATI2")); texture.Format = BitmapDdsFormatDetection.DetectFormat(dds); // Set flags based on the format switch (texture.Format) { case BitmapFormat.Dxt1: case BitmapFormat.Dxt3: case BitmapFormat.Dxt5: case BitmapFormat.Dxn: texture.Flags = BitmapFlags.Compressed; break; default: texture.Flags = BitmapFlags.None; break; } if ((texture.Width & (texture.Width - 1)) == 0 && (texture.Height & (texture.Height - 1)) == 0) { texture.Flags |= BitmapFlags.PowerOfTwoDimensions; } // If creating a new image, then add a new resource, otherwise replace the existing one if (newResource) { resource.ChangeLocation(location); CacheContext.AddResource(resource, ddsStream); } else { CacheContext.ReplaceResource(resource, ddsStream); } // Serialize the new resource definition serializer.Serialize(resourceContext, definition); // Modify the image data in the bitmap tag to match the definition imageData.Width = texture.Width; imageData.Height = texture.Height; imageData.Depth = texture.Depth; imageData.Type = texture.Type; imageData.Format = texture.Format; imageData.Flags = texture.Flags; imageData.MipmapCount = (sbyte)(texture.MipmapCount - 1); imageData.DataOffset = texture.Data.Address.Offset; imageData.DataSize = texture.Data.Size; imageData.Curve = (BitmapImageCurve)texture.Curve; }
public override object Execute(List <string> args) { if (args.Count < 2) { return(false); } // // Verify Blam tag instance // var unitName = args[0].ToLower(); if (unitName != "spartan" && unitName != "elite") { Console.WriteLine("ERROR: Only 'spartan' and 'elite' armor variants are allowed."); return(false); } args.RemoveAt(0); var blamTagName = unitName == "spartan" ? @"objects\characters\masterchief\mp_masterchief\mp_masterchief" : @"objects\characters\elite\mp_elite\mp_elite"; Console.Write($"Verifying {blamTagName}.render_model..."); CacheFile.IndexItem blamTag = null; foreach (var tag in BlamCache.IndexItems) { if ((tag.GroupTag == "mode") && (tag.Name == blamTagName)) { blamTag = tag; break; } } if (blamTag == null) { Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.render_model"); return(true); } Console.WriteLine("done."); // // Load the Blam tag definition // var variantName = args[0]; args.RemoveAt(0); CachedTagInstance edModeTag = null; var isScenery = false; var regionNames = new List <string>(); while (args.Count != 0) { switch (args[0].ToLower()) { case "scenery": isScenery = true; args.RemoveAt(0); break; case "replace:": edModeTag = CacheContext.GetTag(args[1]); args.RemoveAt(1); args.RemoveAt(0); break; case "regions:": regionNames.AddRange(args.Skip(1)); args.Clear(); break; default: throw new InvalidDataException($"{args}"); } } var blamContext = new CacheSerializationContext(ref BlamCache, blamTag); var edModeDefinition = BlamCache.Deserializer.Deserialize <RenderModel>(blamContext); var materials = edModeDefinition.Materials.Select(i => new RenderMaterial { BreakableSurfaceIndex = i.BreakableSurfaceIndex, Properties = i.Properties, RenderMethod = i.RenderMethod, Skins = i.Skins, Unknown = i.Unknown, Unknown2 = i.Unknown2, Unknown3 = i.Unknown3, Unknown4 = i.Unknown4 }).ToList(); edModeDefinition = (RenderModel)ConvertData(null, edModeDefinition, false); var variantRegions = new List <RenderModel.Region>(); foreach (var region in edModeDefinition.Regions) { if (regionNames.Count != 0 && !regionNames.Contains(CacheContext.GetString(region.Name))) { continue; } var variantRegion = new RenderModel.Region { Name = region.Name, Permutations = new List <RenderModel.Region.Permutation>() }; foreach (var permutation in region.Permutations) { if (variantName == CacheContext.GetString(permutation.Name)) { variantRegion.Permutations.Add(permutation); } } variantRegions.Add(variantRegion); } var variantMeshes = new List <int>(); var variantMaterials = new List <int>(); var variantVertexBuffers = new List <int>(); var variantIndexBuffers = new List <int>(); foreach (var region in variantRegions) { foreach (var permutation in region.Permutations) { for (var i = permutation.MeshIndex; i < (short)(permutation.MeshIndex + permutation.MeshCount); i++) { var mesh = edModeDefinition.Geometry.Meshes[i]; foreach (var part in mesh.Parts) { if (part.MaterialIndex != -1 && !variantMaterials.Contains(part.MaterialIndex)) { variantMaterials.Add(part.MaterialIndex); } } foreach (var vertexBuffer in mesh.VertexBufferIndices) { if (vertexBuffer != ushort.MaxValue && !variantVertexBuffers.Contains(vertexBuffer)) { variantVertexBuffers.Add(vertexBuffer); } } foreach (var indexBuffer in mesh.IndexBufferIndices) { if (indexBuffer != ushort.MaxValue && !variantIndexBuffers.Contains(indexBuffer)) { variantIndexBuffers.Add(indexBuffer); } } if (!variantMeshes.Contains(i)) { variantMeshes.Add(i); } } } } variantMeshes.Sort(); variantMaterials.Sort(); variantVertexBuffers.Sort(); variantIndexBuffers.Sort(); foreach (var meshIndex in variantMeshes) { var mesh = edModeDefinition.Geometry.Meshes[meshIndex]; foreach (var part in mesh.Parts) { if (part.MaterialIndex != -1) { part.MaterialIndex = (short)variantMaterials.IndexOf(part.MaterialIndex); } } } foreach (var region in variantRegions) { foreach (var permutation in region.Permutations) { if (permutation.MeshIndex != -1) { permutation.MeshIndex = (short)variantMeshes.IndexOf(permutation.MeshIndex); } } } foreach (var meshIndex in variantMeshes) { var mesh = edModeDefinition.Geometry.Meshes[meshIndex]; for (var i = 0; i < mesh.VertexBufferIndices.Length; i++) { if (!variantVertexBuffers.Contains(mesh.VertexBufferIndices[i])) { mesh.VertexBufferIndices[i] = ushort.MaxValue; } else { mesh.VertexBufferIndices[i] = (ushort)variantVertexBuffers.IndexOf(mesh.VertexBufferIndices[i]); } } for (var i = 0; i < mesh.IndexBufferIndices.Length; i++) { if (!variantIndexBuffers.Contains(mesh.IndexBufferIndices[i])) { mesh.IndexBufferIndices[i] = ushort.MaxValue; } else { mesh.IndexBufferIndices[i] = (ushort)variantIndexBuffers.IndexOf(mesh.IndexBufferIndices[i]); } } } edModeDefinition.Regions = variantRegions; edModeDefinition.Geometry.Meshes = edModeDefinition.Geometry.Meshes.Where(i => variantMeshes.Contains(edModeDefinition.Geometry.Meshes.IndexOf(i))).ToList(); // // Port Blam render_model materials // materials = materials.Where(i => variantMaterials.Contains(materials.IndexOf(i))).ToList(); using (var stream = CacheContext.OpenTagCacheReadWrite()) { for (var i = 0; i < materials.Count; i++) { var material = materials[i]; if (material.RenderMethod.Index == -1) { continue; } var blamRenderMethod = materials[i].RenderMethod; var blamRenderMethodTag = BlamCache.IndexItems.GetItemByID(blamRenderMethod.Index); var renderMethodExists = false; foreach (var instance in CacheContext.TagCache.Index.FindAllInGroup("rm ")) { if (instance?.Name == blamRenderMethodTag.Name) { renderMethodExists = true; material.RenderMethod = instance; break; } } if (!renderMethodExists) { material.RenderMethod = CacheContext.GetTag <Shader>(@"shaders\invalid"); } } } edModeDefinition.Materials = materials; // // Load Blam resource data // var resourceData = BlamCache.GetRawFromID(edModeDefinition.Geometry.ZoneAssetHandle); if (resourceData == null) { Console.WriteLine("Blam render_geometry resource contains no data."); return(true); } // // Load Blam resource definition // Console.Write("Loading Blam render_geometry resource definition..."); var definitionEntry = BlamCache.ResourceGestalt.TagResources[edModeDefinition.Geometry.ZoneAssetHandle.Index]; var resourceDefinition = new RenderGeometryApiResourceDefinition { VertexBuffers = new List <TagStructureReference <VertexBufferDefinition> >(), IndexBuffers = new List <TagStructureReference <IndexBufferDefinition> >() }; using (var definitionStream = new MemoryStream(BlamCache.ResourceGestalt.FixupInformation)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) { var dataContext = new DataSerializationContext(definitionReader, null, CacheResourceAddressType.Definition); definitionReader.SeekTo(definitionEntry.FixupInformationOffset + (definitionEntry.FixupInformationLength - 24)); var vertexBufferCount = definitionReader.ReadInt32(); definitionReader.Skip(8); var indexBufferCount = definitionReader.ReadInt32(); definitionReader.SeekTo(definitionEntry.FixupInformationOffset); for (var i = 0; i < vertexBufferCount; i++) { resourceDefinition.VertexBuffers.Add(new TagStructureReference <VertexBufferDefinition> { Definition = new VertexBufferDefinition { Count = definitionReader.ReadInt32(), Format = (VertexBufferFormat)definitionReader.ReadInt16(), VertexSize = definitionReader.ReadInt16(), Data = new TagData { Size = definitionReader.ReadInt32(), Unused4 = definitionReader.ReadInt32(), Unused8 = definitionReader.ReadInt32(), Address = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()), Unused10 = definitionReader.ReadInt32() } } }); } definitionReader.Skip(vertexBufferCount * 12); for (var i = 0; i < indexBufferCount; i++) { resourceDefinition.IndexBuffers.Add(new TagStructureReference <IndexBufferDefinition> { Definition = new IndexBufferDefinition { Format = (IndexBufferFormat)definitionReader.ReadInt32(), Data = new TagData { Size = definitionReader.ReadInt32(), Unused4 = definitionReader.ReadInt32(), Unused8 = definitionReader.ReadInt32(), Address = new CacheResourceAddress(CacheResourceAddressType.Memory, definitionReader.ReadInt32()), Unused10 = definitionReader.ReadInt32() } } }); } } Console.WriteLine("done."); // // Convert Blam resource data // using (var edResourceStream = new MemoryStream()) { // // Convert Blam render_geometry_api_resource_definition // using (var blamResourceStream = new MemoryStream(resourceData)) { // // Convert Blam vertex buffers // Console.Write("Converting vertex buffers..."); var previousVertexBufferCount = -1; for (var i = 0; i < resourceDefinition.VertexBuffers.Count; i++) { if (!variantVertexBuffers.Contains(i)) { continue; } blamResourceStream.Position = definitionEntry.ResourceFixups[i].Offset; if (i > 0) { previousVertexBufferCount = resourceDefinition.VertexBuffers[i - 1].Definition.Count; } GeometryConverter.ConvertVertexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i, previousVertexBufferCount); } Console.WriteLine("done."); // // Convert Blam index buffers // Console.Write("Converting index buffers..."); for (var i = 0; i < resourceDefinition.IndexBuffers.Count; i++) { if (!variantIndexBuffers.Contains(i)) { continue; } blamResourceStream.Position = definitionEntry.ResourceFixups[resourceDefinition.VertexBuffers.Count * 2 + i].Offset; GeometryConverter.ConvertIndexBuffer(resourceDefinition, blamResourceStream, edResourceStream, i); } Console.WriteLine("done."); } resourceDefinition.VertexBuffers = resourceDefinition.VertexBuffers.Where(i => variantVertexBuffers.Contains(resourceDefinition.VertexBuffers.IndexOf(i))).ToList(); resourceDefinition.IndexBuffers = resourceDefinition.IndexBuffers.Where(i => variantIndexBuffers.Contains(resourceDefinition.IndexBuffers.IndexOf(i))).ToList(); // // Finalize the new ElDorado geometry resource // Console.Write("Writing resource data..."); edModeDefinition.Geometry.Resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.RenderGeometry, ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; edResourceStream.Position = 0; var resourceContext = new ResourceSerializationContext(CacheContext, edModeDefinition.Geometry.Resource); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); edModeDefinition.Geometry.Resource.ChangeLocation(ResourceLocation.ResourcesB); CacheContext.AddResource(edModeDefinition.Geometry.Resource, edResourceStream); Console.WriteLine("done."); } edModeDefinition.Name = CacheContext.GetStringId(variantName); if (edModeTag == null) { for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edModeTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("mode")]); break; } } if (edModeTag == null) { edModeTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("mode")]); } } // // Create a new armor model tag // Model edHlmtDefinition = null; CachedTagInstance edHlmtTag = null; if (isScenery) { Console.Write($"Verifying {blamTagName}.model..."); CacheFile.IndexItem blamHlmtTag = null; foreach (var tag in BlamCache.IndexItems) { if ((tag.GroupTag == "hlmt") && (tag.Name == blamTagName)) { blamHlmtTag = tag; break; } } if (blamHlmtTag == null) { Console.WriteLine($"ERROR: Blam tag does not exist: {blamTagName}.model"); return(true); } Console.WriteLine("done."); blamContext = new CacheSerializationContext(ref BlamCache, blamHlmtTag); edHlmtDefinition = (Model)ConvertData(null, BlamCache.Deserializer.Deserialize <Model>(blamContext), false); edHlmtDefinition.RenderModel = edModeTag; edHlmtDefinition.ReduceToL1SuperLow = 36.38004f; edHlmtDefinition.ReduceToL2Low = 27.28503f; edHlmtDefinition.Variants = new List <Model.Variant>(); edHlmtDefinition.Materials = new List <Model.Material>(); edHlmtDefinition.NewDamageInfo = new List <Model.GlobalDamageInfoBlock>(); edHlmtDefinition.Targets = new List <Model.Target>(); var collisionRegions = new List <Model.CollisionRegion>(); foreach (var collisionRegion in edHlmtDefinition.CollisionRegions) { var found = false; foreach (var variantRegion in variantRegions) { if (collisionRegion.Name == variantRegion.Name) { found = true; break; } } if (!found) { continue; } found = false; foreach (var permutation in collisionRegion.Permutations) { if (permutation.Name == CacheContext.GetStringId(variantName)) { found = true; break; } } if (found) { collisionRegions.Add(collisionRegion); } } foreach (var collisionRegion in collisionRegions) { Model.CollisionRegion.Permutation permutation = null; foreach (var collisionPermutation in collisionRegion.Permutations) { if (collisionPermutation.Name == CacheContext.GetStringId(variantName)) { permutation = collisionPermutation; break; } } if (permutation == null) { throw new KeyNotFoundException(); } collisionRegion.Permutations = new List <Model.CollisionRegion.Permutation> { permutation }; } edHlmtDefinition.CollisionRegions = collisionRegions; for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edHlmtTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("hlmt")]); break; } } if (edHlmtTag == null) { edHlmtTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("hlmt")]); } } // // Create a new armor scenery tag // Scenery edScenDefinition = null; CachedTagInstance edScenTag = null; if (isScenery) { edScenDefinition = new Scenery { ObjectType = new GameObjectType { Halo2 = GameObjectTypeHalo2.Scenery, Halo3Retail = GameObjectTypeHalo3Retail.Scenery, Halo3ODST = GameObjectTypeHalo3ODST.Scenery, HaloOnline = GameObjectTypeHaloOnline.Scenery }, BoundingRadius = 0.44f, BoundingOffset = new RealPoint3d(-0.02f, 0.0f, 0.0f), AccelerationScale = 1.2f, SweetenerSize = GameObject.SweetenerSizeValue.Medium, Model = edHlmtTag, ChangeColors = new List <GameObject.ChangeColor> { new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor(), new GameObject.ChangeColor() }, NodeMaps = new List <GameObject.NodeMap>() }; for (sbyte i = 0; i < 51; i++) { edScenDefinition.NodeMaps.Add(new GameObject.NodeMap { TargetNode = i }); } for (var i = 0; i < CacheContext.TagCache.Index.Count; i++) { if (CacheContext.TagCache.Index[i] == null) { CacheContext.TagCache.Index[i] = edScenTag = new CachedTagInstance(i, TagGroup.Instances[new Tag("scen")]); break; } } if (edScenTag == null) { edScenTag = CacheContext.TagCache.AllocateTag(TagGroup.Instances[new Tag("scen")]); } } // // Serialize new ElDorado tag definitions // using (var cacheStream = CacheContext.OpenTagCacheReadWrite()) { CacheContext.Serialize(cacheStream, edModeTag, edModeDefinition); edModeTag.Name = isScenery ? (unitName == "spartan" ? $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" : $@"objects\characters\elite\mp_elite\armor\{variantName}") : (unitName == "spartan" ? @"objects\characters\masterchief\mp_masterchief\mp_masterchief" : @"objects\characters\elite\mp_elite\mp_elite"); if (isScenery) { CacheContext.Serialize(cacheStream, edHlmtTag, edHlmtDefinition); CacheContext.Serialize(cacheStream, edScenTag, edScenDefinition); edScenTag.Name = unitName == "spartan" ? $@"objects\characters\masterchief\mp_masterchief\armor\{variantName}" : $@"objects\characters\elite\mp_elite\armor\{variantName}"; } } return(true); }
public override object Execute(List <string> args) { if (args.Count < 1 || args.Count > 2) { return(false); } if (args.Count == 2 && (args[0].ToLower() != "raw")) { return(false); } var file = ""; if (args.Count == 1) { file = args[0]; } else { file = args[1]; } var resourceContext = new ResourceSerializationContext(CacheContext, Geometry.Resource); var definition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); if (args.Count == 2) { using (var edResourceStream = new MemoryStream()) using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian)) { var directory = args[1]; if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } var dataOutDir = directory; for (var i = 0; i < definition.VertexBuffers.Count; i++) { edResourceStream.Position = definition.VertexBuffers[i].Definition.Data.Address.Offset; var vertexBuffer = definition.VertexBuffers[i].Definition; dataOutDir = Path.Combine(directory, $"{i}_{vertexBuffer.Format}_{vertexBuffer.Count}"); using (EndianWriter output = new EndianWriter(File.OpenWrite(dataOutDir), EndianFormat.LittleEndian)) { byte[] data = edResourceReader.ReadBytes((int)vertexBuffer.Data.Size); output.WriteBlock(data); } } } } else { // // Convert Blam data to ElDorado data // using (var fileStream = File.Create(file)) using (var fileWriter = new StreamWriter(fileStream)) { using (var edResourceStream = new MemoryStream()) using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian)) { // // Convert Blam vertex buffers // Console.Write("Converting vertex buffers..."); CacheContext.ExtractResource(Geometry.Resource, edResourceStream); for (var i = 0; i < definition.VertexBuffers.Count; i++) { edResourceStream.Position = definition.VertexBuffers[i].Definition.Data.Address.Offset; var vertexBuffer = definition.VertexBuffers[i].Definition; //fileWriter.WriteLine($"Offset = {vertexBuffer.Data.Address.Offset.ToString("X8")} Count = {vertexBuffer.Count} Size = {vertexBuffer.VertexSize}, Format = {vertexBuffer.Format.ToString()}"); //fileWriter.WriteLine(Environment.NewLine); //fileWriter.WriteLine($"Vertex buffer index: {i}"); switch (vertexBuffer.Format) { case VertexBufferFormat.TinyPosition: for (var j = 0; j < vertexBuffer.Count; j++) { fileWriter.WriteLine($"Position = ({edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")},{edResourceReader.ReadUInt16().ToString("X4")})"); fileWriter.WriteLine($"Normal = ({edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")},{edResourceReader.ReadByte().ToString("X2")})"); fileWriter.WriteLine($"Color = {edResourceReader.ReadUInt32().ToString("X8")}"); } break; case VertexBufferFormat.StaticPerVertex: for (var j = 0; j < vertexBuffer.Count; j++) { fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + edResourceReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}"); fileWriter.WriteLine(Environment.NewLine); } break; case VertexBufferFormat.Skinned: for (var j = 0; j < vertexBuffer.Count; j++) { fileWriter.WriteLine($"{j}:"); fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle()); fileWriter.WriteLine($"Normal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); fileWriter.WriteLine($"Tangent X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); edResourceReader.ReadUInt32(); edResourceReader.ReadUInt32(); } break; /* * case VertexBufferFormat.Unknown1B: * var goodCount = 0; * for (var j = 0; j < vertexBuffer.Count; j++) * { * //fileWriter.WriteLine($"Index: {j}:"); * string values = $"({ edResourceReader.ReadSingle()},{ edResourceReader.ReadSingle()}," + * $"{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()}" + * $",{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()},{edResourceReader.ReadSingle()})"; * if(values != "(0,0,0,0,0,0,0,0,0)") * { * goodCount++; * //fileWriter.WriteLine($"(1,2,3,4,5,6,7,8,9) = "+values); * } * * * /* * fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 2 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 3 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 4 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 5 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 6 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 7 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 8 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 9 = " + edResourceReader.ReadSingle()); * * } * //fileWriter.WriteLine($"Valid Unknown1B count = {goodCount}"); * break; * * case VertexBufferFormat.Unknown1A: * for (var j = 0; j < vertexBuffer.Count; j++) * { * //fileWriter.WriteLine($"Index: {j}:"); * //fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadUInt32().ToString("X8")); * fileWriter.WriteLine($"(1,2,3) = ({edResourceReader.ReadUInt32().ToString("X8")},{edResourceReader.ReadUInt32().ToString("X8")},{edResourceReader.ReadUInt32().ToString("X8")})"); * } * break; * * case VertexBufferFormat.Rigid: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{j}:"); * fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Normal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Tangent X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * } * break; * * case VertexBufferFormat.StaticPerPixel: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{j} U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle()); * //fileWriter.WriteLine(Environment.NewLine); * } * break; * * case VertexBufferFormat.AmbientPrt: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{j} Blend weight = " + edResourceReader.ReadSingle()); * //fileWriter.WriteLine(Environment.NewLine); * } * break; * * * case VertexBufferFormat.World: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{j}:"); * fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Normal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Tangent X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Binormal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * } * break; * * * */ /* * case VertexBufferFormat.QuadraticPrt: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); * fileWriter.WriteLine($"A X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"B X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"C X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}"); * fileWriter.WriteLine(Environment.NewLine); * } * break; * * * * case VertexBufferFormat.Unknown1B: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); * fileWriter.WriteLine($"Unknown 1 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 2 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 3 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 4 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 5 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 6 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 7 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 8 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Unknown 9 = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}"); * fileWriter.WriteLine(Environment.NewLine); * } * break; * * case VertexBufferFormat.Decorator: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); * fileWriter.WriteLine($"Position X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Texcoord U = " + edResourceReader.ReadSingle() + " V = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"Normal X = " + edResourceReader.ReadSingle() + " Y = " + edResourceReader.ReadSingle() + " Z = " + edResourceReader.ReadSingle()); * fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}"); * fileWriter.WriteLine(Environment.NewLine); * } * break; * * case VertexBufferFormat.LinearPrt: * for (var j = 0; j < vertexBuffer.Count; j++) * { * fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); * fileWriter.WriteLine($"Hex : " + edResourceReader.ReadUInt32().ToString("X8")); * fileWriter.WriteLine($"End of {vertexBuffer.Format} index: {j}"); * fileWriter.WriteLine(Environment.NewLine); * } * break; */ } //fileWriter.WriteLine($"End of Vertex Buffer index: {i}"); //fileWriter.WriteLine(Environment.NewLine); } } } } return(true); }
private PageableResource ConvertBitmap(Bitmap bitmap, Dictionary <ResourceLocation, Stream> resourceStreams, int imageIndex, string tagName) { var image = bitmap.Images[imageIndex]; BaseBitmap baseBitmap = BitmapConverter.ConvertGen3Bitmap(BlamCache, bitmap, imageIndex, BlamCache.Version); if (baseBitmap == null) { return(null); } // fix type enum if (baseBitmap.Type == BitmapType.Array) { baseBitmap.Type = BitmapType.Texture3D; } SetTagData(baseBitmap, image); var dataSize = baseBitmap.Data.Length; var resource = new PageableResource { Page = new RawPage(), Resource = new TagResourceGen3 { ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), ResourceType = TagResourceTypeGen3.Bitmap, Unknown2 = 1 } }; using (var dataStream = new MemoryStream(baseBitmap.Data)) { var bitmapResource = new Bitmap.BitmapResource { Resource = resource, Unknown4 = 0 }; var resourceContext = new ResourceSerializationContext(CacheContext, resource); // Create new definition var resourceDefinition = new BitmapTextureInteropResource { Texture = new TagStructureReference <BitmapTextureInteropResource.BitmapDefinition> { Definition = new BitmapTextureInteropResource.BitmapDefinition { Data = new TagData(), UnknownData = new TagData(), } } }; SetResourceDefinitionData(baseBitmap, image, resourceDefinition.Texture.Definition); // // Serialize the new resource definition // var location = bitmap.Usage == 2 ? ResourceLocation.TexturesB : // bump maps ResourceLocation.Textures; // everything else resource.ChangeLocation(location); if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = CacheContext.GetResourceCache(location); if (!resourceStreams.ContainsKey(location)) { resourceStreams[location] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(location); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(location)) resourceStream.CopyTo(resourceStreams[location]); } } dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(resourceStreams[location], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); CacheContext.Serializer.Serialize(resourceContext, resourceDefinition); } return(resource); }
public override bool Execute(List <string> args) { if (args.Count != 3) { return(false); } var variantName = args[0]; var fileType = args[1]; var fileName = args[2]; if (fileType != "obj") { return(false); } // Find the variant to extract if (Definition.RenderModel == null) { Console.WriteLine("The model does not have a render model associated with it."); return(true); } var variant = Definition.Variants.FirstOrDefault(v => (Info.StringIds.GetString(v.Name) ?? v.Name.ToString()) == variantName); if (variant == null && Definition.Variants.Count > 0) { Console.WriteLine("Unable to find variant \"{0}\"", variantName); Console.WriteLine("Use \"listvariants\" to list available variants."); return(true); } // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return(true); } // Deserialize the render model tag Console.WriteLine("Reading model data..."); RenderModel renderModel; using (var cacheStream = Info.CacheFile.OpenRead()) { var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIds, Definition.RenderModel); renderModel = Info.Deserializer.Deserialize <RenderModel>(renderModelContext); } if (renderModel.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return(true); } // Deserialize the resource definition var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource); var definition = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // Extract the resource data resourceManager.Extract(renderModel.Geometry.Resource, resourceStream); using (var objFile = new StreamWriter(File.Open(fileName, FileMode.Create, FileAccess.Write))) { var objExtractor = new ObjExtractor(objFile); var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]); // Create a (de)compressor from the first compression block if (variant != null) { // Extract each region in the variant foreach (var region in variant.Regions) { // Get the corresonding region in the render model tag if (region.RenderModelRegionIndex >= renderModel.Regions.Count) { continue; } var renderModelRegion = renderModel.Regions[region.RenderModelRegionIndex]; // Get the corresponding permutation in the render model tag // (Just extract the first permutation for now) if (region.Permutations.Count == 0) { continue; } var permutation = region.Permutations[0]; if (permutation.RenderModelPermutationIndex < 0 || permutation.RenderModelPermutationIndex >= renderModelRegion.Permutations.Count) { continue; } var renderModelPermutation = renderModelRegion.Permutations[permutation.RenderModelPermutationIndex]; // Extract each mesh in the permutation var meshIndex = renderModelPermutation.MeshIndex; var meshCount = renderModelPermutation.MeshCount; var regionName = Info.StringIds.GetString(region.Name) ?? region.Name.ToString(); var permutationName = Info.StringIds.GetString(permutation.Name) ?? permutation.Name.ToString(); Console.WriteLine("Extracting {0} mesh(es) for {1}:{2}...", meshCount, regionName, permutationName); for (var i = 0; i < meshCount; i++) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, renderModel.Geometry.Meshes[meshIndex + i], definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } } else { // No variant - just extract every mesh Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Geometry.Meshes.Count); foreach (var mesh in renderModel.Geometry.Meshes) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, mesh, definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } objExtractor.Finish(); } } Console.WriteLine("Done!"); return(true); }
private ScenarioStructureBsp ConvertScenarioStructureBsp(ScenarioStructureBsp sbsp, CachedTagInstance instance, Dictionary <ResourceLocation, Stream> resourceStreams) { sbsp.CollisionBspResource = ConvertStructureBspTagResources(sbsp, resourceStreams); sbsp.PathfindingResource = ConvertStructureBspCacheFileTagResources(sbsp, resourceStreams); sbsp.Unknown86 = 1; // // Set compatibility flag for H3 mopps // if (BlamCache.Version == CacheVersion.Halo3Retail) { sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.UseMoppIndexPatch; } else { sbsp.CompatibilityFlags = ScenarioStructureBsp.StructureBspCompatibilityValue.None; } // // Fix cluster tag ref and decorator grids // var resource = sbsp.Geometry.Resource; if (resource != null && resource.Page.Index >= 0 && resource.GetLocation(out var location)) { var resourceContext = new ResourceSerializationContext(CacheContext, sbsp.Geometry.Resource); var definition = CacheContext.Deserializer.Deserialize <RenderGeometryApiResourceDefinition>(resourceContext); using (var edResourceStream = new MemoryStream()) using (var edResourceReader = new EndianReader(edResourceStream, EndianFormat.LittleEndian)) { var pageable = sbsp.Geometry.Resource; if (pageable == null) { throw new ArgumentNullException("sbsp.Geometry.Resource"); } if (!edResourceStream.CanWrite) { throw new ArgumentException("The output stream is not open for writing", "outStream"); } pageable.GetLocation(out var resourceLocation); var cache = CacheContext.GetResourceCache(resourceLocation); if (!resourceStreams.ContainsKey(resourceLocation)) { resourceStreams[resourceLocation] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(resourceLocation); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(resourceLocation)) resourceStream.CopyTo(resourceStreams[resourceLocation]); } } cache.Decompress(resourceStreams[resourceLocation], pageable.Page.Index, pageable.Page.CompressedBlockSize, edResourceStream); var inVertexStream = VertexStreamFactory.Create(CacheVersion.HaloOnline106708, edResourceStream); foreach (var cluster in sbsp.Clusters) { List <ScenarioStructureBsp.Cluster.DecoratorGrid> newDecoratorGrids = new List <ScenarioStructureBsp.Cluster.DecoratorGrid>(); foreach (var grid in cluster.DecoratorGrids) { grid.DecoratorGeometryIndex_HO = grid.DecoratorGeometryIndex_H3; grid.DecoratorIndex_HO = grid.DecoratorIndex_H3; if (grid.Amount == 0) { newDecoratorGrids.Add(grid); } else { List <TinyPositionVertex> vertices = new List <TinyPositionVertex>(); // Get the buffer the right grid var vertexBuffer = definition.VertexBuffers[grid.DecoratorGeometryIndex_HO].Definition; // Get the offset from the grid edResourceStream.Position = vertexBuffer.Data.Address.Offset + grid.DecoratorGeometryOffset; // Read all vertices and add to the list for (int i = 0; i < grid.Amount; i++) { vertices.Add(inVertexStream.ReadTinyPositionVertex()); } // Get the new grids List <ScenarioStructureBsp.Cluster.DecoratorGrid> newGrids = ConvertDecoratorGrid(vertices, grid); // Add all to list foreach (var newGrid in newGrids) { newDecoratorGrids.Add(newGrid); } } } cluster.DecoratorGrids = newDecoratorGrids; } } } // // Temporary Fixes: // // Without this 005_intro crash on cortana sbsp sbsp.Geometry2.UnknownSections = new List <RenderGeometry.UnknownSection>(); return(sbsp); }