private Dialogue ConvertDialogue(Stream cacheStream, Dialogue dialogue) { if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } CachedTagInstance edAdlg = null; AiDialogueGlobals adlg = null; foreach (var tag in CacheContext.TagCache.Index.FindAllInGroup("adlg")) { edAdlg = tag; break; } adlg = CacheContext.Deserialize <AiDialogueGlobals>(cacheStream, edAdlg); //Create empty udlg vocalization block and fill it with empty blocks matching adlg List <Dialogue.Vocalization> newVocalization = new List <Dialogue.Vocalization>(); foreach (var vocalization in adlg.Vocalizations) { Dialogue.Vocalization block = new Dialogue.Vocalization { Sound = null, Flags = 0, Unknown = 0, Name = vocalization.Name, }; newVocalization.Add(block); } //Match the tags with the proper stringId for (int i = 0; i < 304; i++) { var vocalization = newVocalization[i]; for (int j = 0; j < dialogue.Vocalizations.Count; j++) { var vocalizationH3 = dialogue.Vocalizations[j]; if (CacheContext.StringIdCache.GetString(vocalization.Name).Equals(CacheContext.StringIdCache.GetString(vocalizationH3.Name))) { vocalization.Flags = vocalizationH3.Flags; vocalization.Unknown = vocalizationH3.Unknown; vocalization.Sound = vocalizationH3.Sound; break; } } } dialogue.Vocalizations = newVocalization; return(dialogue); }
private void InitGen3Sound(Sound sound, SoundCacheFileGestalt soundGestalt, int permutationGestaltIndex, byte[] data) { var platformCodec = soundGestalt.PlatformCodecs[sound.SoundReference.PlatformCodecIndex]; var permutation = soundGestalt.Permutations[permutationGestaltIndex]; Encoding = platformCodec.Encoding; SampleRate = platformCodec.SampleRate; SampleCount = permutation.SampleSize; RealPermutationIndex = permutation.OverallPermutationIndex; UpdateFormat(platformCodec.Compression, data); }
public static BlamSound GetXMA(GameCache cache, SoundCacheFileGestalt soundGestalt, Sound sound, int pitchRangeIndex, int permutationIndex, byte[] data) { int pitchRangeGestaltIndex = sound.SoundReference.PitchRangeIndex + pitchRangeIndex; int permutationGestaltIndex = soundGestalt.PitchRanges[pitchRangeGestaltIndex].FirstPermutationIndex + permutationIndex; var permutationSize = soundGestalt.GetPermutationSize(permutationGestaltIndex); var permutationOffset = soundGestalt.GetPermutationOffset(permutationGestaltIndex); byte[] permutationData = new byte[permutationSize]; Array.Copy(data, permutationOffset, permutationData, 0, permutationSize); return(new BlamSound(sound, permutationGestaltIndex, permutationData, cache.Version, soundGestalt)); }
public void ExtractXMA(CacheFile.IndexItem blamTag, string directory) { 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); if (xmaFileSize < 0) { return; } var xmaData = BlamCache.GetSoundRaw(sound.SoundReference.ZoneAssetHandle, xmaFileSize); if (xmaData == null) { Console.WriteLine($"ERROR: Failed to find sound data!"); return; } var parts = blamTag.Name.Split('\\'); string baseName = parts[parts.Length - 1]; 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++) { BlamSound blamSound = SoundConverter.GetXMA(BlamCache, BlamSoundGestalt, sound, relativePitchRangeIndex, i, xmaData); string permutationName = $"{baseName}_{relativePitchRangeIndex}_{i}"; var fileName = $"{directory}\\{permutationName}.xma"; using (EndianWriter output = new EndianWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { XMAFile XMAfile = new XMAFile(blamSound); XMAfile.Write(output); } } } }
public static BlamSound ConvertGen3Sound(CacheFile cache, SoundCacheFileGestalt soundGestalt, Sound sound, int pitchRangeIndex, int permutationIndex, byte[] data) { ClearFiles(); BlamSound blamSound = GetXMA(cache, soundGestalt, sound, pitchRangeIndex, permutationIndex, data); if (blamSound.Compression == Compression.UnknownWTF) { throw new Exception("Unknown compression"); } var loop = sound.Flags.HasFlag(Sound.FlagsValue.LoopingSound); var channelCount = Encoding.GetChannelCount(blamSound.Encoding); var sampleRate = blamSound.SampleRate.GetSampleRateHz(); WriteXMAFile(blamSound); if (channelCount > 2) { // channelCount is 4 or 6, ignore looping ConvertToWAV(XMAFile, false); byte[] originalWAVdata = File.ReadAllBytes(WAVFile); byte[] truncatedWAVdata = TruncateWAVFile(originalWAVdata, sampleRate, channelCount, 0x4E); blamSound.UpdateFormat(Compression.PCM, truncatedWAVdata); WriteWAVFile(blamSound); ConvertToMP3(WAVFile); LoadMP3Data(blamSound); } else if (!loop) { // not looping stereo or mono ConvertToWAV(XMAFile, true); blamSound.UpdateFormat(Compression.PCM, LoadWAVData(WAVFile, -1, false)); WriteWAVFile(blamSound); ConvertToMP3(WAVFile); LoadMP3Data(blamSound); } else { ConvertToWAV(XMAFile, true); blamSound.UpdateFormat(Compression.PCM, LoadWAVData(WAVFile, -1, false)); WriteWAVFile(blamSound); ConvertWAVToMP3Looping(WAVFile); blamSound.UpdateFormat(Compression.MP3, File.ReadAllBytes(MP3File)); } ClearFiles(); return(blamSound); }
public void ExtractXMA(string directory) { if (BlamSoundGestalt == null) { using (var stream = Cache.OpenCacheRead()) BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(Cache, stream); } var resourceDefinition = Cache.ResourceCache.GetSoundResourceDefinition(Sound.Resource); var xmaFileSize = BlamSoundGestalt.GetFileSize(Sound.SoundReference.PitchRangeIndex, Sound.SoundReference.PitchRangeCount); if (xmaFileSize < 0) { return; } var xmaData = resourceDefinition.Data.Data; if (xmaData == null) { Console.WriteLine($"ERROR: Failed to find sound data!"); return; } var parts = Tag.Name.Split('\\'); string baseName = parts[parts.Length - 1]; 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++) { BlamSound blamSound = SoundConverter.GetXMA(Cache, BlamSoundGestalt, Sound, relativePitchRangeIndex, i, xmaData); string permutationName = $"{baseName}_{relativePitchRangeIndex}_{i}"; var fileName = $"{directory}\\{permutationName}.xma"; using (EndianWriter output = new EndianWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { XMAFile XMAfile = new XMAFile(blamSound); XMAfile.Write(output); } } } }
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); } } } }
public static BlamSound GetXMA(CacheFile cache, SoundCacheFileGestalt soundGestalt, Sound sound, int pitchRangeIndex, int permutationIndex, byte[] data) { if (cache.ResourceLayoutTable == null || cache.ResourceGestalt == null) { cache.LoadResourceTags(); } int pitchRangeGestaltIndex = sound.SoundReference.PitchRangeIndex + pitchRangeIndex; int permutationGestaltIndex = soundGestalt.PitchRanges[pitchRangeGestaltIndex].FirstPermutationIndex + permutationIndex; var permutationSize = soundGestalt.GetPermutationSize(permutationGestaltIndex); var permutationOffset = soundGestalt.GetPermutationOffset(permutationGestaltIndex); byte[] permutationData = new byte[permutationSize]; Array.Copy(data, permutationOffset, permutationData, 0, permutationSize); return(new BlamSound(sound, permutationGestaltIndex, permutationData, cache.Version, soundGestalt)); }
private SoundLooping ConvertSoundLooping(SoundLooping soundLooping) { if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } soundLooping.Unused = null; soundLooping.SoundClass = ((int)soundLooping.SoundClass < 50) ? soundLooping.SoundClass : (soundLooping.SoundClass + 1); if (soundLooping.SoundClass == SoundLooping.SoundClassValue.FirstPersonInside) { soundLooping.SoundClass = SoundLooping.SoundClassValue.InsideSurroundTail; } if (soundLooping.SoundClass == SoundLooping.SoundClassValue.FirstPersonOutside) { soundLooping.SoundClass = SoundLooping.SoundClassValue.OutsideSurroundTail; } /* unsuccessful hacks of death and suffering * foreach (var track in soundLooping.Tracks) * { * track.FadeInDuration *= 2f; * track.Unknown1 *= 2f; * track.FadeOutDuration *= 2f; * track.AlternateCrossfadeDuration *= 2f; * track.Unknown5 *= 2; * track.AlternateFadeOutDuration *= 2f; * track.Unknown6 *= 2f; * } * * foreach (var detailSound in soundLooping.DetailSounds) * detailSound.RandomPeriodBounds = new Bounds<float>( * detailSound.RandomPeriodBounds.Lower * 2f, * detailSound.RandomPeriodBounds.Upper * 2f);*/ return(soundLooping); }
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()); } }
public static SoundCacheFileGestalt LoadSoundGestalt(GameCache cache, Stream cacheStream) { CachedTag blamTag = null; foreach (var tag in cache.TagCache.TagTable) { if (tag.Group.Tag == "ugh!") { blamTag = tag; break; } } if (blamTag == null) { return(null); } SoundCacheFileGestalt result = cache.Deserialize <SoundCacheFileGestalt>(cacheStream, blamTag); return(result); }
private SoundLooping ConvertSoundLooping(SoundLooping soundLooping) { if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } soundLooping.Unused = null; soundLooping.SoundClass = ((int)soundLooping.SoundClass < 50) ? soundLooping.SoundClass : (soundLooping.SoundClass + 1); if (soundLooping.SoundClass == SoundLooping.SoundClassValue.FirstPersonInside) { soundLooping.SoundClass = SoundLooping.SoundClassValue.InsideSurroundTail; } if (soundLooping.SoundClass == SoundLooping.SoundClassValue.FirstPersonOutside) { soundLooping.SoundClass = SoundLooping.SoundClassValue.OutsideSurroundTail; } // // Fixes for looping sound (temporary and hacky) // if (soundLooping.SoundClass == SoundLooping.SoundClassValue.VehicleEngine || soundLooping.SoundClass == SoundLooping.SoundClassValue.VehicleEngineLod || soundLooping.SoundClass == SoundLooping.SoundClassValue.Music) { soundLooping.Unknown4 = 1; } return(soundLooping); }
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) { 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); }
public BlamSound(Sound sound, int permutationGestaltIndex, byte[] data, CacheVersion version, SoundCacheFileGestalt soundGestalt = null) { switch (version) { case CacheVersion.Halo3Retail: case CacheVersion.Halo3ODST: case CacheVersion.HaloReach: InitGen3Sound(sound, soundGestalt, permutationGestaltIndex, data); break; default: throw new Exception($"Unsupported cache version {version}"); } }