private void WriteDataBlock(DataBlock block, SegmentPointer location, IStream stream, ITag tag = null) { if (tag == null && _dataBlockAddresses.ContainsKey(block)) // Don't write anything if the block has already been written { return; } // Associate the location with the block _dataBlockAddresses[block] = location.AsPointer(); // Create a MemoryStream and write the block data to it (so fixups can be performed before writing it to the file) using (var buffer = new MemoryStream(block.Data.Length)) { var bufferWriter = new EndianWriter(buffer, stream.Endianness); bufferWriter.WriteBlock(block.Data); // Apply fixups FixBlockReferences(block, bufferWriter, stream); FixTagReferences(block, bufferWriter, stream); FixResourceReferences(block, bufferWriter, stream); FixStringIdReferences(block, bufferWriter); if (tag != null) { FixUnicListReferences(block, tag, bufferWriter, stream); } FixModelDataReferences(block, bufferWriter, stream, location); FixEffectReferences(block, bufferWriter); // sort after fixups if (block.Sortable && block.EntrySize >= 4) { var entries = new List <Tuple <uint, byte[]> >(); var bufferReader = new EndianReader(buffer, stream.Endianness); for (int i = 0; i < block.EntryCount; i++) { buffer.Position = i * block.EntrySize; uint sid = bufferReader.ReadUInt32(); byte[] rest = bufferReader.ReadBlock(block.EntrySize - 4); entries.Add(new Tuple <uint, byte[]>(sid, rest)); } buffer.Position = 0; foreach (var entry in entries.OrderBy(e => e.Item1)) { bufferWriter.WriteUInt32(entry.Item1); bufferWriter.WriteBlock(entry.Item2); } } // Write the buffer to the file stream.SeekTo(location.AsOffset()); stream.WriteBlock(buffer.ToArray(), 0, (int)buffer.Length); } // Write shader fixups (they can't be done in-memory because they require cache file expansion) FixShaderReferences(block, stream, location); }
private void WriteDataBlock(DataBlock block, SegmentPointer location, IStream stream) { // Don't write anything if the block has already been written if (_dataBlockAddresses.ContainsKey(block)) { return; } // Associate the location with the block _dataBlockAddresses[block] = location.AsPointer(); // Create a MemoryStream and write the block data to it (so fixups can be performed before writing it to the file) using (var buffer = new MemoryStream(block.Data.Length)) { var bufferWriter = new EndianWriter(buffer, stream.Endianness); bufferWriter.WriteBlock(block.Data); // Apply fixups FixBlockReferences(block, bufferWriter, stream); FixTagReferences(block, bufferWriter, stream); FixResourceReferences(block, bufferWriter, stream); FixStringIDReferences(block, bufferWriter); // Write the buffer to the file stream.SeekTo(location.AsOffset()); stream.WriteBlock(buffer.GetBuffer(), 0, (int)buffer.Length); } // Write shader fixups (they can't be done in-memory because they require cache file expansion) FixShaderReferences(block, stream, (int)location.AsOffset()); }
/// <summary> /// Reads a shader from a stream and then serializes it into a byte array which can then be exported. /// </summary> /// <param name="reader">The stream to read from. It should be positioned at the beginning of the shader pointer.</param> /// <param name="type">The type of shader to read.</param> /// <returns> /// The serialized shader data, or <c>null</c> if reading failed. /// </returns> public byte[] ExportShader(IReader reader, ShaderType type) { var info = ReadShaderInfo(reader, type); if (info == null) { return(null); } using (var memStream = new MemoryStream()) { using (var writer = new EndianWriter(memStream, Endian.BigEndian)) { writer.WriteInt32(SerializationMagic); writer.WriteByte((byte)type); // Write the layout size for compatibility checking when deserializing if (type == ShaderType.Pixel) { writer.WriteInt32(_pixelShaderInfoLayout.Size); } else { writer.WriteInt32(_vertexShaderInfoLayout.Size); } // Write the raw debug info reader.SeekTo(info.DebugInfoOffset); writer.WriteUInt32(info.DebugInfoSize); writer.WriteBlock(reader.ReadBlock((int)info.DebugInfoSize)); // Write the raw shader data var dataOffset = _cacheFile.MetaArea.PointerToOffset(info.DataAddress); reader.SeekTo(dataOffset); writer.WriteUInt32(info.DataSize); writer.WriteBlock(reader.ReadBlock((int)info.DataSize)); // Create the result from the memory stream's buffer var result = new byte[memStream.Length]; Buffer.BlockCopy(memStream.ToArray(), 0, result, 0, (int)memStream.Length); return(result); } } }
public static void Copy(EndianReader input, EndianWriter output, int size) { const int BufferSize = 0x1000; byte[] buffer = new byte[BufferSize]; while (size > 0) { int read = input.ReadBlock(buffer, 0, Math.Min(BufferSize, size)); output.WriteBlock(buffer, 0, read); size -= BufferSize; } }
public static void Copy(EndianReader input, EndianWriter output) { // http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c-sharp const int BufferSize = 0x1000; byte[] buffer = new byte[BufferSize]; int read; while ((read = input.ReadBlock(buffer, 0, BufferSize)) > 0) { output.WriteBlock(buffer, 0, read); } }
public override void Serialize(Stream stream) { using (var writer = new EndianWriter(stream, Endian.BigEndian)) { var buildNameBytes = System.Text.Encoding.UTF8.GetBytes(BuildName); writer.WriteInt32(buildNameBytes.Length); writer.WriteBlock(buildNameBytes); var cacheNameBytes = System.Text.Encoding.UTF8.GetBytes(CacheName); writer.WriteInt32(cacheNameBytes.Length); writer.WriteBlock(cacheNameBytes); var count = Actions.Count; writer.WriteInt32(count); foreach (var action in Actions) { writer.WriteInt64(action.Position); writer.WriteInt32(action.Buffer.Length); writer.WriteBlock(action.Buffer); } } }
public void ExtractWAV(CacheFile.IndexItem blamTag, string directory) { Console.WriteLine($"Extracting {blamTag.Name}.sound"); if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } var blamContext = new CacheSerializationContext(ref BlamCache, blamTag); var sound = BlamCache.Deserializer.Deserialize <Sound>(blamContext); var xmaFileSize = BlamSoundGestalt.GetFileSize(sound.SoundReference.PitchRangeIndex, sound.SoundReference.PitchRangeCount); var xmaData = BlamCache.GetSoundRaw(sound.SoundReference.ZoneAssetHandle, xmaFileSize); if (xmaData == null) { Console.WriteLine($"ERROR: Failed to find sound data!"); return; } var OutDir = directory; string sound_name = blamTag.Name.Replace('\\', '_'); sound_name = Path.Combine(directory, sound_name); for (int pitchRangeIndex = sound.SoundReference.PitchRangeIndex; pitchRangeIndex < sound.SoundReference.PitchRangeIndex + sound.SoundReference.PitchRangeCount; pitchRangeIndex++) { var relativePitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex; var permutationCount = BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); for (int i = 0; i < permutationCount; i++) { string permutationName = $"{sound_name}_{relativePitchRangeIndex}_{i}"; permutationName += ".mp3"; BlamSound blamSound = SoundConverter.ConvertGen3Sound(BlamCache, BlamSoundGestalt, sound, relativePitchRangeIndex, i, xmaData); using (EndianWriter output = new EndianWriter(new FileStream(permutationName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { output.WriteBlock(blamSound.Data); } } } }
/// <summary> /// Write a DDS header /// </summary> /// <param name="writer"></param> public override void Write(EndianWriter writer) { writer.Format = EndianFormat.LittleEndian; writer.Write((uint)0x20534444); // DDS writer.Write(Size); writer.Write((int)Flags); writer.Write(Height); writer.Write(Width); writer.Write(PitchOrLinearSize); writer.Write(Depth); writer.Write(MipMapCount); writer.WriteBlock(Reserved1); PixelFormat.Write(writer); writer.Write((int)Caps); writer.Write((int)Caps2); writer.Write(Caps3); writer.Write(Caps4); writer.Write(Reserved2); }
override public void WriteChunk(EndianWriter writer) { writer.Format = EndianFormat.LittleEndian; writer.Write(Length); writer.WriteBlock(FileName); writer.Write(SampleCount); writer.Write(CompressedFileLength); writer.Write(LoopBegin); writer.Write(LoopEnd); writer.Write(Mode); writer.Write(SampleRate); writer.Write(Volume); writer.Write(Pan); writer.Write(Pri); writer.Write(ChannelCount); writer.Write(MinDistance); writer.Write(MaxDistance); writer.Write(VariableFrequency); writer.Write(VariableVolume); writer.Write(VariablePan); }
private byte[] ConvertGen3SoundData(GameCache cache, Sound definition, byte[] soundData) { // todo: use PortingContextFactory here (currently inaccessible) SoundCacheFileGestalt blamSoundGestalt = null; foreach (var tag in cache.TagCache.TagTable) { if (tag.Group.Tag == "ugh!") { using (var stream = cache.OpenCacheRead()) blamSoundGestalt = cache.Deserialize <SoundCacheFileGestalt>(stream, tag); break; } } // ExportSoundCommand code using (MemoryStream soundDataStream = new MemoryStream()) { using (EndianWriter output = new EndianWriter(soundDataStream, EndianFormat.BigEndian)) { if (blamSoundGestalt != null) { for (int pitchRangeIndex = definition.SoundReference.PitchRangeIndex; pitchRangeIndex < definition.SoundReference.PitchRangeIndex + definition.SoundReference.PitchRangeCount; pitchRangeIndex++) { var relativePitchRangeIndex = pitchRangeIndex - definition.SoundReference.PitchRangeIndex; var permutationCount = blamSoundGestalt.GetPermutationCount(pitchRangeIndex); for (int i = 0; i < permutationCount; i++) { BlamSound blamSound = SoundConverter.ConvertGen3Sound(cache, blamSoundGestalt, definition, relativePitchRangeIndex, i, soundData); output.WriteBlock(blamSound.Data); } } } } return(soundDataStream.ToArray()); } }
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 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 string ConvertToAudioFile(ICollection <byte> data, string path = null) { var tempFile = Path.GetTempFileName(); byte[] footer; var codec = _soundResourceGestalt.SoundPlatformCodecs[_sound.CodecIndex]; switch (codec.Channel) { case Channel.Mono: footer = _monoFooter; break; case Channel.Stereo: footer = _stereoFooter; break; default: throw new NotImplementedException(); } switch (_sound.Encoding) { case Encoding.XMA: using (var fileStream = new FileStream(tempFile, FileMode.OpenOrCreate)) { using (var writer = new EndianWriter(fileStream, Endian.BigEndian)) { // Generate an XMA header // ADAPTED FROM wwisexmabank - I DO NOT TAKE ANY CREDIT WHATSOEVER FOR THE FOLLOWING CODE. // See http://hcs64.com/vgm_ripping.html for more information // 'riff' chunk writer.WriteInt32(0x52494646); // 'RIFF' writer.Endianness = Endian.LittleEndian; writer.WriteInt32(data.Count + 0x34); writer.Endianness = Endian.BigEndian; writer.WriteInt32(0x57415645); // 'data' chunk writer.Endianness = Endian.BigEndian; writer.WriteInt32(0x64617461); // 'data' writer.Endianness = Endian.LittleEndian; writer.WriteInt32(data.Count); writer.WriteBlock(data.ToArray()); // footer writer.WriteBlock(footer); // size writer.SeekTo(0x04); writer.WriteInt32((Int32)writer.Length - 0x08); } } VariousFunctions.RunProgramSilently(@"A:\Xbox\Games\towav.exe", string.Format("\"{0}\"", Path.GetFileName(tempFile)), Path.GetDirectoryName(tempFile)); if (File.Exists(tempFile)) { File.Delete(tempFile); } tempFile = Path.ChangeExtension(tempFile, "wav"); if (path != null) { File.Move(tempFile, path); } return(path ?? tempFile); default: throw new NotImplementedException(); } }
private string ConvertToAudioFile(ICollection <byte> data, string path = null) { string towav = Path.Combine(VariousFunctions.GetApplicationLocation(), "helpers", "towav.exe"); if (!File.Exists(towav)) { MetroMessageBox.Show("Cannot Convert Sound", "Sounds cannot be converted because towav.exe is not present. Copy it to the \\helpers folder inside your Assembly installation."); return(null); } var tempFile = Path.GetTempFileName(); byte[] footer; var codec = _soundResourceTable.Codecs[_sound.CodecIndex]; switch ((SoundEncoding)codec.Encoding) { case SoundEncoding.Mono: footer = _monoFooter; break; case SoundEncoding.Stereo: footer = _stereoFooter; break; default: throw new NotImplementedException(); } switch ((SoundCompression)codec.Compression) { case SoundCompression.XMA2: using (var fileStream = new FileStream(tempFile, FileMode.OpenOrCreate)) { using (var writer = new EndianWriter(fileStream, Endian.BigEndian)) { // Generate an XMA header // ADAPTED FROM wwisexmabank - I DO NOT TAKE ANY CREDIT WHATSOEVER FOR THE FOLLOWING CODE. // See http://hcs64.com/vgm_ripping.html for more information // 'riff' chunk writer.WriteInt32(0x52494646); // 'RIFF' writer.Endianness = Endian.LittleEndian; writer.WriteInt32(data.Count + 0x34); writer.Endianness = Endian.BigEndian; writer.WriteInt32(0x57415645); // 'data' chunk writer.Endianness = Endian.BigEndian; writer.WriteInt32(0x64617461); // 'data' writer.Endianness = Endian.LittleEndian; writer.WriteInt32(data.Count); writer.WriteBlock(data.ToArray()); // footer writer.WriteBlock(footer); // size writer.SeekTo(0x04); writer.WriteInt32((Int32)writer.Length - 0x08); } } VariousFunctions.RunProgramSilently(towav, string.Format("\"{0}\"", Path.GetFileName(tempFile)), Path.GetDirectoryName(tempFile)); if (File.Exists(tempFile)) { File.Delete(tempFile); } tempFile = Path.ChangeExtension(tempFile, "wav"); if (path != null) { File.Move(tempFile, path); } return(path ?? tempFile); default: throw new NotImplementedException(); } }
/// <summary> /// Extracts an xWMA sound and converts it to WAV. /// </summary> /// <param name="reader">The stream to read from.</param> /// <param name="offset">The offset of the data to extract.</param> /// <param name="rifx">The RIFX data for the sound.</param> /// <param name="outPath">The path of the file to save to.</param> public static void ExtractXWMAToWAV(EndianReader reader, int offset, RIFX rifx, string outPath) { // Create a temporary file to write an XWMA to string tempFile = Path.GetTempFileName(); try { using (EndianWriter output = new EndianWriter(File.OpenWrite(tempFile), EndianFormat.Big)) { // Generate a little-endian XWMA header // TODO: move this into a class? output.Write(0x52494646); // 'RIFF' // Recompute the file size because the one Wwise gives us is trash // fileSize = header size (always 0x2C) + dpds data size + data header size (always 0x8) + data size int fileSize = 0x2C + rifx.SeekOffsets.Length * 0x4 + 0x8 + rifx.DataSize; output.Format = EndianFormat.Little; output.Write(fileSize); output.Format = EndianFormat.Big; output.Write((int)RIFFFormat.XWMA); // Generate the 'fmt ' chunk output.Write(0x666D7420); // 'fmt ' output.Format = EndianFormat.Little; output.Write(0x18); // Chunk size output.Write(rifx.Codec); output.Write(rifx.ChannelCount); output.Write(rifx.SampleRate); output.Write(rifx.BytesPerSecond); output.Write(rifx.BlockAlign); output.Write(rifx.BitsPerSample); // Write the extradata // Bytes 4 and 5 have to be flipped because they make up an int16 // TODO: add error checking to make sure the extradata is the correct size (0x6) output.Write((short)0x6); output.WriteBlock(rifx.ExtraData, 0, 4); output.Write(rifx.ExtraData[5]); output.Write(rifx.ExtraData[4]); // Generate the 'dpds' chunk // It's really just the 'seek' chunk from the original data but renamed output.Format = EndianFormat.Big; output.Write(0x64706473); // 'dpds' output.Format = EndianFormat.Little; output.Write(rifx.SeekOffsets.Length * 4); // One uint32 per offset foreach (int seek in rifx.SeekOffsets) { output.Write(seek); } // 'data' chunk output.Format = EndianFormat.Big; output.Write(0x64617461); // 'data' output.Format = EndianFormat.Little; output.Write(rifx.DataSize); // Copy the data chunk contents from the original RIFX reader.SeekTo(offset + rifx.DataOffset); StreamUtil.Copy(reader, output, rifx.DataSize); } // Convert it with xWMAEncode RunProgramSilently("Helpers/xWMAEncode.exe", "\"" + tempFile + "\" \"" + outPath + "\"", Directory.GetCurrentDirectory()); } finally { // Delete the temporary XWMA file if (File.Exists(tempFile)) { File.Delete(tempFile); } } }
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 definition = Cache.ResourceCache.GetRenderGeometryApiResourceDefinition(Geometry.Resource); if (args.Count == 2) { using (var edResourceStream = new MemoryStream()) using (var vertexReader = 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 = 0; 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 = null; output.WriteBlock(data); } } } } else { using (var fileStream = File.Create(file)) using (var fileWriter = new StreamWriter(fileStream)) { for (var i = 0; i < definition.VertexBuffers.Count; i++) { var vertexBuffer = definition.VertexBuffers[i].Definition; using (var vertexStream = new MemoryStream(vertexBuffer.Data.Data)) using (var vertexReader = new EndianReader(vertexStream, EndianFormat.LittleEndian)) { switch (vertexBuffer.Format) { case VertexBufferFormat.TinyPosition: for (var j = 0; j < vertexBuffer.Count; j++) { fileWriter.WriteLine($"Position = ({vertexReader.ReadUInt16().ToString("X4")},{vertexReader.ReadUInt16().ToString("X4")},{vertexReader.ReadUInt16().ToString("X4")},{vertexReader.ReadUInt16().ToString("X4")})"); fileWriter.WriteLine($"Normal = ({vertexReader.ReadByte().ToString("X2")},{vertexReader.ReadByte().ToString("X2")},{vertexReader.ReadByte().ToString("X2")},{vertexReader.ReadByte().ToString("X2")})"); fileWriter.WriteLine($"Color = {vertexReader.ReadUInt32().ToString("X8")}"); } break; case VertexBufferFormat.StaticPerVertex: for (var j = 0; j < vertexBuffer.Count; j++) { fileWriter.WriteLine($"{vertexBuffer.Format} index: {j}:"); fileWriter.WriteLine($"Texcoord = " + vertexReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + vertexReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + vertexReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + vertexReader.ReadUInt32().ToString("X8")); fileWriter.WriteLine($"Texcoord = " + vertexReader.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 = " + vertexReader.ReadSingle() + " Y = " + vertexReader.ReadSingle() + " Z = " + vertexReader.ReadSingle()); fileWriter.WriteLine($"Texcoord U = " + vertexReader.ReadSingle() + " V = " + vertexReader.ReadSingle()); fileWriter.WriteLine($"Normal X = " + vertexReader.ReadSingle() + " Y = " + vertexReader.ReadSingle() + " Z = " + vertexReader.ReadSingle()); fileWriter.WriteLine($"Tangent X = " + vertexReader.ReadSingle() + " Y = " + vertexReader.ReadSingle() + " Z = " + vertexReader.ReadSingle()); fileWriter.WriteLine($"Binormal X = " + vertexReader.ReadSingle() + " Y = " + vertexReader.ReadSingle() + " Z = " + vertexReader.ReadSingle()); vertexReader.ReadUInt32(); vertexReader.ReadUInt32(); } break; } } } } } return(true); }
public override object Execute(List <string> args) { string outDirectory = ""; if (args.Count == 1) { outDirectory = args[0]; } else if (args.Count == 0) { outDirectory = "Sounds"; } else { return(false); } 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 resourceReference = Definition.Resource; var resourceDefinition = Cache.ResourceCache.GetSoundResourceDefinition(resourceReference); if (resourceDefinition.Data == null) { Console.WriteLine("Invalid sound definition"); return(false); } var dataReference = resourceDefinition.Data; byte[] soundData = dataReference.Data; if (Cache is GameCacheHaloOnlineBase) { 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; } } } } } else if (Cache.GetType() == typeof(GameCacheGen3)) { if (BlamSoundGestalt == null) { using (var stream = Cache.OpenCacheRead()) BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(Cache, stream); } for (int pitchRangeIndex = Definition.SoundReference.PitchRangeIndex; pitchRangeIndex < Definition.SoundReference.PitchRangeIndex + Definition.SoundReference.PitchRangeCount; pitchRangeIndex++) { var relativePitchRangeIndex = pitchRangeIndex - Definition.SoundReference.PitchRangeIndex; var permutationCount = BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); for (int i = 0; i < permutationCount; i++) { string permutationName = $"{Tag.Name.Replace('\\', '_')}_{relativePitchRangeIndex}_{i}.mp3"; var outPath = Path.Combine(outDirectory, permutationName); BlamSound blamSound = SoundConverter.ConvertGen3Sound(Cache, BlamSoundGestalt, Definition, relativePitchRangeIndex, i, soundData); using (EndianWriter output = new EndianWriter(new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { output.WriteBlock(blamSound.Data); } } } } Console.WriteLine("Done!"); return(true); }
override public void Write(EndianWriter writer) { ArchiveHeader.WriteChunk(writer); File.WriteChunk(writer); writer.WriteBlock(Data); }
public void Write(EndianWriter writer) { Header.Write(writer); writer.WriteBlock(BitmapData); }