diff --git a/.gitmodules b/.gitmodules index b1c3fc8431..c335c16833 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "LegendaryExplorer/submodules/WwiseTools"] path = LegendaryExplorer/submodules/WwiseTools url = https://github.com/ME3Tweaks/WwiseTools.git -[submodule "LegendaryExplorer/submodules/WwiseParser"] - path = LegendaryExplorer/submodules/WwiseParser - url = https://github.com/ME3Tweaks/WwiseParser.git [submodule "LegendaryExplorer/submodules/Wwiser.NET"] path = LegendaryExplorer/submodules/Wwiser.NET url = https://github.com/ME3Tweaks/Wwiser.NET diff --git a/LegendaryExplorer/LegendaryExplorer.sln b/LegendaryExplorer/LegendaryExplorer.sln index c5e760157b..188f49bd87 100644 --- a/LegendaryExplorer/LegendaryExplorer.sln +++ b/LegendaryExplorer/LegendaryExplorer.sln @@ -36,8 +36,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaveFormRendererLib", "Wave EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WwiseTools_DotNet6", "submodules\WwiseTools\WwiseTools_DotNet6\WwiseTools_DotNet6.csproj", "{BF251A45-7189-4E2C-AA08-0308F0E309A7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WwiseParserLib", "submodules\WwiseParser\WwiseParserLib\WwiseParserLib.csproj", "{DA7BCC8E-034B-4AA6-BBB4-BAA6CB4F6FB2}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "submodules", "submodules", "{947C0083-A3FF-4CEC-912C-BB65A11C6EEC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wwiser.NET", "Wwiser.NET", "{1F845EFA-0145-46AD-86FE-84C7DAAA5842}" diff --git a/LegendaryExplorer/LegendaryExplorer/LegendaryExplorer.csproj b/LegendaryExplorer/LegendaryExplorer/LegendaryExplorer.csproj index 407c41ac20..ec95f2ee4d 100644 --- a/LegendaryExplorer/LegendaryExplorer/LegendaryExplorer.csproj +++ b/LegendaryExplorer/LegendaryExplorer/LegendaryExplorer.csproj @@ -105,7 +105,6 @@ - diff --git a/LegendaryExplorer/LegendaryExplorer/SharedUI/Converters/SoundplorerConverters.cs b/LegendaryExplorer/LegendaryExplorer/SharedUI/Converters/SoundplorerConverters.cs index 8b8521ae1a..a68763f7f7 100644 --- a/LegendaryExplorer/LegendaryExplorer/SharedUI/Converters/SoundplorerConverters.cs +++ b/LegendaryExplorer/LegendaryExplorer/SharedUI/Converters/SoundplorerConverters.cs @@ -2,8 +2,10 @@ using System.Globalization; using System.Windows; using System.Windows.Data; +using ME3Tweaks.Wwiser.Model.Hierarchy; +using ME3Tweaks.Wwiser.Model.Hierarchy.Enums; using AudioStreamHelper = LegendaryExplorer.UnrealExtensions.AudioStreamHelper; -using HIRCDisplayObject = LegendaryExplorer.UserControls.ExportLoaderControls.HIRCDisplayObject; +using HIRCDisplayObject = LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel.HIRCDisplayObject; namespace LegendaryExplorer.SharedUI.Converters { @@ -36,14 +38,11 @@ public class HIRCMediaFetchTypeConverter : IValueConverter // parameter is allowed class type for visibility public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - uint i = (uint)value; - return i switch + if (value is HircItemContainer { Type.Value: HircType.Sound, Item: Sound snd}) { - 0 => $"Embedded", - 1 => $"Streamed", - 2 => $"Streamed with prefetch", - _ => $"Unknown playback fetch type: {value}" - }; + return Enum.GetName(typeof(StreamType.StreamTypeInner), snd.BankSourceData.StreamType.Value); + } + return "No media"; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -55,10 +54,14 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu [ValueConversion(typeof(byte), typeof(string))] public class HIRCObjectTypeConverter : IValueConverter { - // parameter is allowed class type for visibility public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return AudioStreamHelper.GetHircObjTypeString((byte)value); + if (value is HircItemContainer { Type: { } hircType }) + { + return Enum.GetName(typeof(HircType), hircType.Value); + } + + return ""; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -70,14 +73,11 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu [ValueConversion(typeof(int), typeof(Visibility))] public class HIRCObjectTypeVisibilityConverter : IValueConverter { - // parameter is allowed class type for visibility public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (parameter != null && value != null) + if (parameter is HircType parameterType && value is HIRCDisplayObject hdo) { - int iparameter = int.Parse((string)parameter); - HIRCDisplayObject ho = (HIRCDisplayObject)value; - return iparameter == ho.ObjType ? Visibility.Visible : Visibility.Collapsed; + return parameterType == hdo.Item.Type.Value ? Visibility.Visible : Visibility.Collapsed; } return Visibility.Collapsed; } diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/AssetDatabase/AssetDatabaseWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/AssetDatabase/AssetDatabaseWindow.xaml index 1236968f38..bc87febb9c 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/AssetDatabase/AssetDatabaseWindow.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/AssetDatabase/AssetDatabaseWindow.xaml @@ -14,6 +14,7 @@ xmlns:exportLoaderControls="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls" xmlns:assetDbFilters="clr-namespace:LegendaryExplorer.Tools.AssetDatabase.Filters" xmlns:materialEditor="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.MaterialEditor" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" Loaded="AssetDB_Loaded" Closing="AssetDB_Closing" AllowDrop="True" @@ -1074,7 +1075,7 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml index 2e47d3818d..1965d6cefe 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml @@ -14,6 +14,7 @@ xmlns:exportLoaderControls="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls" xmlns:dialogueEditor="clr-namespace:LegendaryExplorer.DialogueEditor" xmlns:sharedUi="clr-namespace:LegendaryExplorer.SharedUI" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" mc:Ignorable="d" Loaded="DialogueEditorWPF_Loaded" Closing="DialogueEditorWPF_Closing" @@ -1395,11 +1396,11 @@ Female - + Male - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageDumper/TreeNode.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageDumper/TreeNode.cs index 5ddc18b074..f7606a8efe 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageDumper/TreeNode.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageDumper/TreeNode.cs @@ -12,7 +12,7 @@ namespace LegendaryExplorer.Tools.PackageDumper { public class TreeNode { - public BinaryInterpreterWPF.NodeType Tag { get; set; } + public NodeType Tag { get; set; } public string Text { get; set; } public string Name { get; set; } @@ -80,25 +80,25 @@ public void PrintPretty(string indent, StreamWriter str, bool last) indent += "| "; } str.Write(Text); - if (Children.Count > 1000 && Tag == BinaryInterpreterWPF.NodeType.ArrayProperty) + if (Children.Count > 1000 && Tag == NodeType.ArrayProperty) { str.Write($" > 1000, ({Children.Count}) suppressed."); return; } - if (Tag == BinaryInterpreterWPF.NodeType.ArrayProperty && (Text.Contains("LookupTable") || Text.Contains("CompressedTrackOffsets"))) + if (Tag == NodeType.ArrayProperty && (Text.Contains("LookupTable") || Text.Contains("CompressedTrackOffsets"))) { str.Write(" - suppressed by data dumper."); return; } for (int i = 0; i < Children.Count; i++) { - if (Children[i].Tag == BinaryInterpreterWPF.NodeType.None) + if (Children[i].Tag == NodeType.None) { continue; } str.Write("\n"); - Children[i].PrintPretty(indent, str, i == Children.Count - 1 || (i == Children.Count - 2 && Children[Children.Count - 1].Tag == BinaryInterpreterWPF.NodeType.None)); + Children[i].PrintPretty(indent, str, i == Children.Count - 1 || (i == Children.Count - 2 && Children[Children.Count - 1].Tag == NodeType.None)); } } } diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsH.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsH.cs index 04130c69f0..38139967b1 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsH.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsH.cs @@ -372,5 +372,46 @@ public static void ReplaceAudioFromWwiseEncodedFile(string filePath = null, Expo } } } + + public static void DumpAllWwiseBanks(PackageEditorWindow pe) + { + var games = new[] { MEGame.ME2, MEGame.ME3, MEGame.LE2, MEGame.LE3 }; + var outputFolder = Path.Combine(AppDirectories.ObjectDatabasesFolder, "WwiseBankExport"); + Task.Run(() => + { + pe.SetBusy($"Exporting Wwise Banks"); + foreach (var game in games) + { + var outputGameFolder = Path.Combine(outputFolder, game.ToString()); + Directory.CreateDirectory(outputGameFolder); + var found = new HashSet(); + var allPackages = MELoadedFiles.GetFilesLoadedInGame(game).ToList(); + int numDone = 0; + foreach (var f in allPackages) + { + pe.BusyText = $"Processing file {++numDone}/{allPackages.Count} in {game}"; + using var package = MEPackageHandler.OpenMEPackage(f.Value); + + foreach (var exp in package.Exports.Where(x => x.ClassName == "WwiseBank" && !x.IsDefaultObject)) + { + if (found.Contains(exp.MemoryFullPath)) continue; + found.Add(exp.MemoryFullPath); + var fileName = exp.MemoryFullPath + ".bnk"; + + var data = new MemoryStream(exp.GetBinaryData()); + var binSkip = game.IsGame3() ? 0x10 : 0x18; + data.Skip(binSkip); + + using FileStream fs = new FileStream(Path.Combine(outputGameFolder, fileName), FileMode.Create); + data.CopyToEx(fs, (int)data.Length - binSkip); + } + } + } + }).ContinueWithOnUIThread(_ => + { + Process.Start(outputFolder); + pe.EndBusy(); + }); + } } } \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs index aed27e4199..3e2613109b 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs @@ -20,6 +20,7 @@ using System.Numerics; using System.Reactive; using System.Windows; +using ME3Tweaks.Wwiser.Model.Action; using static LegendaryExplorer.Misc.ExperimentsTools.PackageAutomations; using static LegendaryExplorer.Misc.ExperimentsTools.SequenceAutomations; using static LegendaryExplorer.Misc.ExperimentsTools.SharedMethods; @@ -1934,30 +1935,32 @@ public static void UpdateAudioIDs_EXPERIMENTAL(ExportEntry wwiseBankEntry, strin (uint oldBankID, uint newBankID) = UpdateID_EXPERIMENTAL(wwiseBankEntry, null, newWwiseBankName); - WwiseBankParsed wwiseBank = wwiseBankEntry.GetBinaryData(); + var wwiserBinary = wwiseBankEntry.GetBinaryData(); + var wwiseBank = wwiserBinary.Bank; // Update the bank id - wwiseBank.ID = newBankID; + wwiseBank.BKHD.SoundBankId = newBankID; idPairs.Add(oldBankID, newBankID); // Update referenced banks kvp that reference the old bank name - IEnumerable> updatedBanks = wwiseBank.ReferencedBanks - .Select(referencedBank => + if (wwiseBank.STID != null) + foreach (var refBank in wwiseBank.STID.BankIdToFilename) { - if (referencedBank.Value.Equals(oldBankName, StringComparison.OrdinalIgnoreCase)) + if (refBank.FileName.Equals(oldBankName, StringComparison.OrdinalIgnoreCase)) { - return new KeyValuePair(newBankID, newWwiseBankName); + refBank.BankId = newBankID; } - return referencedBank; - }); - wwiseBank.ReferencedBanks = new(updatedBanks); + } // Gather all the IDs we don't know about yet - foreach (WwiseBankParsed.HIRCObject hirc in wwiseBank.HIRCObjects.Values) + if (wwiseBank.HIRC != null) { - if (hirc.ID != 0 && !idPairs.ContainsKey(hirc.ID)) + foreach (var hirc in wwiseBank.HIRC.Items) { - idPairs.Add(hirc.ID, GenerateRandomID(random)); + if (hirc.Item.Id != 0 && !idPairs.ContainsKey(hirc.Item.Id)) + { + idPairs.Add(hirc.Item.Id, GenerateRandomID(random)); + } } } @@ -2001,6 +2004,8 @@ public static void UpdateAudioIDs_EXPERIMENTAL(ExportEntry wwiseBankEntry, strin // } // } //} + + wwiseBankEntry.WriteBinary(wwiserBinary); string bankBinaryAsString = Convert.ToHexString(wwiseBankEntry.GetBinaryData()); @@ -2031,21 +2036,21 @@ public static void UpdateAudioIDs_LEGACY(ExportEntry wwiseBankEntry, string newW (uint oldBankID, uint newBankID) = UpdateID_LEGACY(wwiseBankEntry, newWwiseBankName); - var wwiseBank = wwiseBankEntry.GetBinaryData(); + var wwiseBankBin = wwiseBankEntry.GetBinaryData(); + var wwiseBank = wwiseBankBin.Bank; + // Update the bank id - wwiseBank.ID = newBankID; + wwiseBank.BKHD.SoundBankId = newBankID; // Update referenced banks kvp that reference the old bank name - IEnumerable> updatedBanks = wwiseBank.ReferencedBanks - .Select(referencedBank => + if (wwiseBank.STID != null) + foreach (var refBank in wwiseBank.STID.BankIdToFilename) { - if (referencedBank.Value.Equals(oldBankName, StringComparison.OrdinalIgnoreCase)) + if (refBank.FileName.Equals(oldBankName, StringComparison.OrdinalIgnoreCase)) { - return new KeyValuePair(newBankID, newWwiseBankName); + refBank.BankId = newBankID; } - return referencedBank; - }); - wwiseBank.ReferencedBanks = new(updatedBanks); + } // DISABLED: Update references to old wwiseEvents' hashes, which are the ID of Event HIRCs. // DISABLED: Update references to old wwiseStreams' hashes, which are in the unknown bytes of Sound HIRCs. @@ -2053,40 +2058,40 @@ public static void UpdateAudioIDs_LEGACY(ExportEntry wwiseBankEntry, string newW // but we check in all of them just in case. byte[] bankIDArr = BitConverter.GetBytes(oldBankID); byte[] newBankIDArr = BitConverter.GetBytes(newBankID); - foreach (WwiseBankParsed.HIRCObject hirc in wwiseBank.HIRCObjects.Values) - { - //if (hirc.Type == HIRCType.Event) // References a WwiseEvent - //{ - // if (wwiseEventIDs.TryGetValue(hirc.ID, out uint newEventID)) - // { - // hirc.ID = newEventID; - // } - //} - //else if (hirc.Type == HIRCType.SoundSXFSoundVoice) // References a WwiseStream - //{ - // // 4 bytes ID is located at the start after 14 bytes - // Span streamIDSpan = hirc.unparsed.AsSpan(5..9); - // uint streamIDUInt = BitConverter.ToUInt32(streamIDSpan); - // if (wwiseStreamIDs.TryGetValue(streamIDUInt, out uint newStreamIDUInt)) - // { - // byte[] newStreamIDArr = BitConverter.GetBytes(newStreamIDUInt); - // newStreamIDArr.CopyTo(streamIDSpan); - // } - //} - - // Check for bank ID in all HIRCs, even though I'm almost certain it only appears - // in Event Actions - if (hirc.unparsed != null && hirc.unparsed.Length >= 4) // Only replace if not null and at least width of hash - { - Span bankIDSpan = hirc.unparsed.AsSpan(^4..); - if (bankIDSpan.SequenceEqual(bankIDArr)) + if(wwiseBank.HIRC != null) + foreach (var hircC in wwiseBank.HIRC.Items) + { + //if (hirc.Type == HIRCType.Event) // References a WwiseEvent + //{ + // if (wwiseEventIDs.TryGetValue(hirc.ID, out uint newEventID)) + // { + // hirc.ID = newEventID; + // } + //} + //else if (hirc.Type == HIRCType.SoundSXFSoundVoice) // References a WwiseStream + //{ + // // 4 bytes ID is located at the start after 14 bytes + // Span streamIDSpan = hirc.unparsed.AsSpan(5..9); + // uint streamIDUInt = BitConverter.ToUInt32(streamIDSpan); + // if (wwiseStreamIDs.TryGetValue(streamIDUInt, out uint newStreamIDUInt)) + // { + // byte[] newStreamIDArr = BitConverter.GetBytes(newStreamIDUInt); + // newStreamIDArr.CopyTo(streamIDSpan); + // } + //} + + // Check for bank ID in all HIRCs, even though I'm almost certain it only appears + // in Event Actions - Updated 6/19/25 by HenBagle to just update Play action? + if (hircC.Item is ME3Tweaks.Wwiser.Model.Hierarchy.Action action) { - newBankIDArr.CopyTo(bankIDSpan); + if(action.BankData.BankId == oldBankID) + { + action.BankData.BankId = newBankID; + } } } - } - wwiseBankEntry.WriteBinary(wwiseBank); + wwiseBankEntry.WriteBinary(wwiseBankBin); } /// diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml index 3614c09477..cf266e139f 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml @@ -23,6 +23,7 @@ xmlns:materialEditor="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.MaterialEditor" mc:Ignorable="d" xmlns:global="clr-namespace:LegendaryExplorer" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" Icon="pack://application:,,,/Tools/Icons/16x16/PackageEditor_Icon_16.ico" Title="Package Editor" Height="720" Width="1100" DragOver="Window_DragOver" @@ -564,7 +565,7 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs index e3421d97b4..93662c7507 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs @@ -49,6 +49,7 @@ using LegendaryExplorer.Tools.AssetViewer; using LegendaryExplorer.GameInterop; using LegendaryExplorer.Tools.ObjectReferenceViewer; +using LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel; namespace LegendaryExplorer.Tools.PackageEditor { diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerEntries.cs b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerEntries.cs index 1396f7be06..930cb86735 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerEntries.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerEntries.cs @@ -416,8 +416,9 @@ public void LoadData() } case "WwiseBank": { - var bank = Export.GetBinaryData(); - SubText = $"{bank.EmbeddedFiles.Count} embedded WEM{(bank.EmbeddedFiles.Count != 1 ? "s" : "")}"; + var bank = Export.GetBinaryData().Bank; + var embeddedFileCount = bank.EmbeddedFiles.Count; + SubText = $"{embeddedFileCount} embedded WEM{(embeddedFileCount != 1 ? "s" : "")}"; NeedsLoading = false; Icon = EFontAwesomeIcon.Solid_University; break; diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml index cd6e3e68eb..9ab3e15dd9 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml @@ -13,6 +13,7 @@ xmlns:bases="clr-namespace:LegendaryExplorer.SharedUI.Bases" xmlns:converters="clr-namespace:LegendaryExplorer.SharedUI.Converters" xmlns:misc="clr-namespace:LegendaryExplorer.Misc" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" Closing="Soundplorer_Closing" Drop="Window_Drop" AllowDrop="True" @@ -207,7 +208,7 @@ BorderThickness="3,0" BorderBrush="Transparent" HorizontalAlignment="Stretch" /> - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs index 416aff7798..fcd3fd37e0 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs @@ -32,7 +32,9 @@ using LegendaryExplorerCore.Gammtek.Extensions.Collections.Generic; using LegendaryExplorerCore.Packages.CloningImportingAndRelinking; using LegendaryExplorerCore.Sound.ISACT; +using ME3Tweaks.Wwiser; using AudioStreamHelper = LegendaryExplorer.UnrealExtensions.AudioStreamHelper; +using WwiseBank = LegendaryExplorerCore.Unreal.BinaryConverters.WwiseBank; using WwiseStream = LegendaryExplorerCore.Unreal.BinaryConverters.WwiseStream; namespace LegendaryExplorer.Tools.Soundplorer @@ -501,39 +503,37 @@ private void ExtractBankToWav(SoundplorerExport spExport, string location = null { if (spExport != null && spExport.Export.ClassName == "WwiseBank") { - var bank = spExport.Export.GetBinaryData(); - if (bank.EmbeddedFiles.Count > 0) + var bank = spExport.Export.GetBinaryData().Bank; + if (bank.EmbeddedFiles.Count == 0) return; + + if (location == null) { - if (location == null) + var dlg = new CommonOpenFileDialog("Select output folder") { - var dlg = new CommonOpenFileDialog("Select output folder") - { - IsFolderPicker = true - }; + IsFolderPicker = true + }; - if (dlg.ShowDialog(this) != CommonFileDialogResult.Ok) - { - return; - } - location = dlg.FileName; + if (dlg.ShowDialog(this) != CommonFileDialogResult.Ok) + { + return; } + location = dlg.FileName; + } - foreach ((uint wemID, byte[] wemData) in bank.EmbeddedFiles) + foreach (var file in bank.EmbeddedFiles) + { + string wemHeader = System.Text.Encoding.ASCII.GetString(file.Data[..4]); + string wemName = $"{spExport.Export.ObjectName}_0x{file.Id:X8}"; + if (wemHeader is "RIFF" or "RIFX") { - string wemHeader = "" + (char)wemData[0] + (char)wemData[1] + (char)wemData[2] + (char)wemData[3]; - string wemName = $"{spExport.Export.ObjectName}_0x{wemID:X8}"; - if (wemHeader == "RIFF" || wemHeader == "RIFX") + var wemFile = new EmbeddedWEMFile(file.Data, wemName, spExport.Export); //will correct truncated stuff + var waveStream = soundPanel.GetPCMStream(forcedWemFile: wemFile); + if (waveStream is { Length: > 0 }) { - var wem = new EmbeddedWEMFile(wemData, wemName, spExport.Export); //will correct truncated stuff - Stream waveStream = soundPanel.GetPCMStream(forcedWemFile: wem); - if (waveStream != null && waveStream.Length > 0) - { - string outputname = wemName + ".wav"; - string outpath = Path.Combine(location, outputname); - waveStream.SeekBegin(); - using var fileStream = File.Create(outpath); - waveStream.CopyTo(fileStream); - } + var outputPath = Path.Combine(location, $"{wemName}.wav"); + waveStream.SeekBegin(); + using var fileStream = File.Create(outputPath); + waveStream.CopyTo(fileStream); } } } @@ -1037,22 +1037,21 @@ private void DebugWriteBankToFileRebuild_Clicked(object sender, RoutedEventArgs { if (spExport.Export.ClassName == "WwiseBank") { - var bank = spExport.Export.GetBinaryData(); + var bank = spExport.Export.GetBinaryData().Bank; if (bank.EmbeddedFiles.Count > 0) { int i = 0; var AllWems = new List(); - foreach ((uint wemID, byte[] wemData) in bank.EmbeddedFiles) + foreach (var file in bank.EmbeddedFiles) { - string wemId = wemID.ToString("X8"); + string wemId = file.Id.ToString("X8"); string wemName = "Embedded WEM 0x" + wemId;// + "(" + singleWemMetadata.Item1 + ")"; - var wem = new EmbeddedWEMFile(wemData, $"{i}: {wemName}", spExport.Export, wemID); + var wem = new EmbeddedWEMFile(file.Data, $"{i}: {wemName}", spExport.Export, file.Id); AllWems.Add(wem); i++; } - bank.EmbeddedFiles.Empty(AllWems.Count); - bank.EmbeddedFiles.AddRange(AllWems.Select(wem => new KeyValuePair(wem.Id, wem.HasBeenFixed ? wem.OriginalWemData : wem.WemData))); + //bank.EmbeddedFiles = AllWems.Select(wem => new EmbeddedFile{ Id = wem.Id, Data = wem.HasBeenFixed ? wem.WemData : wem.OriginalWemData }).ToList(); ExportBank(spExport); } } diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml index b56367cd06..96ffb06e15 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml @@ -15,6 +15,7 @@ xmlns:settings="clr-namespace:LegendaryExplorer.Misc.AppSettings" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:packages="clr-namespace:LegendaryExplorerCore.Packages;assembly=LegendaryExplorerCore" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" mc:Ignorable="d" Loaded="WwiseEditorWPF_OnLoaded" Closing="WwiseEditorWPF_OnClosing" @@ -91,7 +92,7 @@ - diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml.cs b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml.cs index a9e7556179..7e7d3e1f78 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseEditorWindow.xaml.cs @@ -24,9 +24,12 @@ using Newtonsoft.Json; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Misc; +using ME3Tweaks.Wwiser.Model.Hierarchy; +using ME3Tweaks.Wwiser.Model.Hierarchy.Enums; using Piccolo; using Piccolo.Event; using Piccolo.Nodes; +using Action = System.Action; using Brushes = System.Drawing.Brushes; using Color = System.Drawing.Color; using Image = System.Drawing.Image; @@ -132,7 +135,7 @@ private set } } - private WwiseBankParsed CurrentWwiseBank; + private WwiseBank CurrentWwiseBank; public ICommand OpenCommand { get; set; } public ICommand SaveCommand { get; set; } @@ -260,7 +263,7 @@ public void LoadBank(ExportEntry export, bool fromFile = false) graphEditor.Enabled = false; graphEditor.UseWaitCursor = true; - CurrentWwiseBank = export.GetBinaryData(); + CurrentWwiseBank = export.GetBinaryData(); SetupJSON(export); Properties_InterpreterWPF.LoadExport(export); binaryInterpreter.LoadExport(export); @@ -306,21 +309,24 @@ private void GenerateGraph() } } - private void GetObjects(WwiseBankParsed bank) + private void GetObjects(WwiseBank bank) { var newObjs = new List(); - foreach ((uint id, WwiseBankParsed.HIRCObject hircObject) in CurrentWwiseBank.HIRCObjects) + CurrentObjects.Clear(); + if (bank.Bank.HIRC is null) return; + foreach (var hircItem in bank.Bank.HIRC.Items) { - newObjs.Add(hircObject switch + newObjs.Add(hircItem.Type.Value switch { - WwiseBankParsed.Event evt => new WEvent(evt, 0, 0, graphEditor), - WwiseBankParsed.EventAction evtAct => new WEventAction(evtAct, 0, 0, graphEditor), - WwiseBankParsed.SoundSFXVoice sfxvoice => new WSoundSFXVoice(sfxvoice, 0, 0, graphEditor), - _ => new WGeneric(hircObject, 0, 0, graphEditor) + HircType.Event => new WEvent(hircItem.Item as Event, 0, 0, graphEditor), + HircType.Action => new WEventAction(hircItem.Item as ME3Tweaks.Wwiser.Model.Hierarchy.Action, 0, 0, graphEditor), + HircType.RandomSequenceContainer => new WRandomSequenceAction(hircItem.Item as RandSeqContainer, 0, 0, graphEditor), + HircType.Sound => new WSoundSFXVoice(hircItem.Item as Sound, 0, 0, graphEditor), + _ => new WGeneric(hircItem.Item, 0, 0, graphEditor) }); } - CurrentObjects.ReplaceAll(newObjs); + CurrentObjects.AddRange(newObjs); } public void Layout() @@ -370,9 +376,9 @@ public void Layout() } case WSoundSFXVoice wSound: { - if (!referencedExports.TryGetValue(wSound.SoundSFXVoice.AudioID, out List wExports)) + if (!referencedExports.TryGetValue(wSound.SoundItem.BankSourceData.MediaInformation.SourceId, out List wExports)) { - if (!wwiseStreams.TryGetValue(wSound.SoundSFXVoice.AudioID, out ExportEntry wwiseSoundExport)) + if (!wwiseStreams.TryGetValue(wSound.SoundItem.BankSourceData.MediaInformation.SourceId, out ExportEntry wwiseSoundExport)) { continue; } @@ -380,7 +386,7 @@ public void Layout() wExports = new List(); var wExp = new WExport(wwiseSoundExport, 0, 0, graphEditor); wExports.Add(wExp); - referencedExports.AddToListAt(wSound.SoundSFXVoice.AudioID, wExp); + referencedExports.AddToListAt(wSound.SoundItem.BankSourceData.MediaInformation.SourceId, wExp); graphEditor.AddNode(wExp); } obj.Varlinks[0].Links.Clear(); diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseIO.cs b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseIO.cs index 3537e85772..6a62d0f11e 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseIO.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseIO.cs @@ -2,17 +2,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Compression; using System.Linq; -using System.Text; -using System.Threading.Tasks; using LegendaryExplorer.Audio; using LegendaryExplorer.UnrealExtensions; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.BinaryConverters; -using Newtonsoft.Json.Linq; using WwiseTools.Objects; using WwiseTools.Src.Models.SoundBank; using WwiseTools.Utils; @@ -48,11 +44,11 @@ public static async void ExportBankToProject(ExportEntry export, string projectO var soundbank = await WwiseUtility.Instance.CreateObjectAtPathAsync(export.ObjectName, WwiseObject.ObjectType.SoundBank, "\\Soundbanks\\Default Work Unit"); // Now we inspect the WwiseBank - var bank = ObjectBinary.From(export); + var bank = ObjectBinary.From(export); List wems = new List(); - foreach ((uint wemID, byte[] wemData) in bank.EmbeddedFiles) + foreach (var ef in bank.Bank.EmbeddedFiles) { - var wem = new EmbeddedWEMFile(wemData, "", export, wemID); + var wem = new EmbeddedWEMFile(ef.Data, "", export, ef.Id); wems.Add(wem); } diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseObjects.cs b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseObjects.cs index 7c0afc9790..94565c06ee 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseObjects.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/WwiseEditor/WwiseObjects.cs @@ -9,9 +9,14 @@ using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.BinaryConverters; using LegendaryExplorerCore.Helpers; +using ME3Tweaks.Wwiser.Model.Hierarchy; +using ME3Tweaks.Wwiser.Model.Hierarchy.Enums; using Piccolo; using Piccolo.Event; -using Piccolo.Nodes; +using Piccolo.Nodes; +using Action = ME3Tweaks.Wwiser.Model.Hierarchy.Action; +using Event = ME3Tweaks.Wwiser.Model.Hierarchy.Event; +using Sound = ME3Tweaks.Wwiser.Model.Hierarchy.Sound; using SText = LegendaryExplorer.Tools.SequenceObjects.SText; using WwiseStreamHelper = LegendaryExplorer.UnrealExtensions.AudioStreamHelper; @@ -59,17 +64,17 @@ public abstract class WwiseHircObjNode : PNode, IDisposable //public float Height { get { return shape.Height; } } public virtual bool IsSelected { get; set; } - protected WwiseBankParsed.HIRCObject hircObject; + protected HircItem hircItem; protected Pen outlinePen; protected SText comment; - public virtual uint ID => hircObject?.ID ?? 0; + public virtual uint ID => hircItem?.Id ?? 0; public string Comment => comment.Text; - protected WwiseHircObjNode(WwiseBankParsed.HIRCObject hircObj, WwiseGraphEditor grapheditor) + protected WwiseHircObjNode(HircItem item, WwiseGraphEditor grapheditor) { - hircObject = hircObj; + hircItem = item; g = grapheditor; comment = new SText(GetComment(), commentColor, false) { @@ -94,32 +99,17 @@ public static string GetIDString(uint id) id = id.Swap(); } - return $"{id:X8}"; + return $"{id}"; } - protected Color GetColor(HIRCType t) + protected Color GetColor(HircType t) { return t switch { - HIRCType.SoundSXFSoundVoice => intColor, - HIRCType.EventAction => interpDataColor, - HIRCType.Event => boolColor, - HIRCType.ActorMixer => stringColor, - //HIRCType.Settings => expr, - //HIRCType.RandomOrSequenceContainer => expr, - //HIRCType.SwitchContainer => expr, - //HIRCType.AudioBus => expr, - //HIRCType.BlendContainer => expr, - //HIRCType.MusicSegment => expr, - //HIRCType.MusicTrack => expr, - //HIRCType.MusicSwitchContainer => expr, - //HIRCType.MusicPlaylistContainer => expr, - //HIRCType.Attenuation => expr, - //HIRCType.DialogueEvent => expr, - //HIRCType.MotionBus => expr, - //HIRCType.MotionFX => expr, - //HIRCType.Effect => expr, - //HIRCType.AuxiliaryBus => expr, + HircType.Sound => intColor, + HircType.Action => interpDataColor, + HircType.Event => boolColor, + HircType.ActorMixer => stringColor, _ => Color.Black }; } @@ -348,9 +338,9 @@ public override void Dispose() public sealed class WEvent : WwiseHircObjNode { - public WwiseBankParsed.Event Event => (WwiseBankParsed.Event)hircObject; + public Event Event => (Event)hircItem; - public WEvent(WwiseBankParsed.Event hircEvent, float x, float y, WwiseGraphEditor grapheditor) + public WEvent(Event hircEvent, float x, float y, WwiseGraphEditor grapheditor) : base(hircEvent, grapheditor) { outlinePen = new Pen(EventColor); @@ -455,12 +445,12 @@ public override bool IsSelected protected override void GetLinks() { - if (Event.EventActions.Any()) + if (Event.ActionIds.Any()) { var l = new OutputLink { Desc = "Event Actions", - Links = Event.EventActions.Clone(), + Links = Event.ActionIds.Clone(), Edges = new List(), node = CreateActionLinkBox() }; @@ -497,8 +487,8 @@ public class WGeneric : WwiseHircObjNode protected InputDragHandler inputDragHandler = new (); - public WGeneric(WwiseBankParsed.HIRCObject hircO, float x, float y, WwiseGraphEditor grapheditor) - : base(hircO, grapheditor) + public WGeneric(HircItem item, float x, float y, WwiseGraphEditor grapheditor) + : base(item, grapheditor) { originalX = x; originalY = y; @@ -621,10 +611,7 @@ public override void Layout(float x, float y) SetOffset(x, y); } - protected virtual string GetTitle() - { - return WwiseStreamHelper.GetHircObjTypeString(hircObject.Type); - } + protected virtual string GetTitle() => hircItem.HircType.ToString(); public class InputDragHandler : PDragEventHandler { @@ -676,26 +663,65 @@ public override void Dispose() public sealed class WEventAction : WGeneric { - public WwiseBankParsed.EventAction EventAction => (WwiseBankParsed.EventAction)hircObject; + public Action EventAction => (Action)hircItem; - public WEventAction(WwiseBankParsed.EventAction evtAct, float x, float y, WwiseGraphEditor grapheditor) : base(evtAct, x, y, grapheditor) + public WEventAction(Action evtAct, float x, float y, WwiseGraphEditor grapheditor) : base(evtAct, x, y, grapheditor) { GetLinks(); } protected override string GetTitle() { - return $"{base.GetTitle()}: {WwiseStreamHelper.GetEventActionTypeString(EventAction.ActionType)}"; + return $"{base.GetTitle()}: {EventAction.Type.Value.ToString()}"; } protected override void GetLinks() { - if (EventAction.ReferencedObjectID != 0) + if (EventAction.TargetId != 0) { var l = new OutputLink { Desc = "Referenced object", - Links = new List{EventAction.ReferencedObjectID}, + Links = new List{EventAction.TargetId}, + Edges = new List(), + node = CreateActionLinkBox() + }; + l.node.Brush = outputBrush; + l.node.Pickable = false; + // PPath dragger = CreateActionLinkBox(); + // dragger.Brush = mostlyTransparentBrush; + // dragger.X = l.node.X; + // dragger.Y = l.node.Y; + // dragger.AddInputEventListener(outputDragHandler); + //l.node.AddChild(dragger); + Outlinks.Add(l); + } + } + } + + public sealed class WRandomSequenceAction : WGeneric + { + public RandSeqContainer RandSeqAction => (RandSeqContainer)hircItem; + + public WRandomSequenceAction(RandSeqContainer evtAct, float x, float y, WwiseGraphEditor grapheditor) : base(evtAct, x, y, grapheditor) + { + GetLinks(); + } + + protected override string GetTitle() + { + return $"{RandSeqAction.Mode.ToString()} Container"; + } + + protected override void GetLinks() + { + for(var i = 0; i < RandSeqAction.Playlist.Items.Count; i++) + { + var item = RandSeqAction.Playlist.Items[i]; + var l = new OutputLink + { + Desc = $"Item {i}, Weight {item.Weight}", + Links = new List{item.Id}, Edges = new List(), node = CreateActionLinkBox() }; @@ -714,26 +740,26 @@ protected override void GetLinks() public sealed class WSoundSFXVoice : WGeneric { - public WwiseBankParsed.SoundSFXVoice SoundSFXVoice => (WwiseBankParsed.SoundSFXVoice)hircObject; + public Sound SoundItem => (Sound)hircItem; - public WSoundSFXVoice(WwiseBankParsed.SoundSFXVoice ssfxv, float x, float y, WwiseGraphEditor grapheditor) : base(ssfxv, x, y, grapheditor) + public WSoundSFXVoice(Sound ssfxv, float x, float y, WwiseGraphEditor grapheditor) : base(ssfxv, x, y, grapheditor) { GetLinks(); } protected override string GetTitle() { - return $"{base.GetTitle()}: {SoundSFXVoice.State}, {SoundSFXVoice.SoundType}"; + return $"{base.GetTitle()}: {SoundItem.BankSourceData.StreamType.Value}, {SoundItem.BankSourceData.MediaInformation.Flags}"; } protected override void GetLinks() { - if (SoundSFXVoice.AudioID != 0) + if (SoundItem.BankSourceData.MediaInformation.SourceId != 0 && SoundItem.BankSourceData.StreamType.Value != StreamType.StreamTypeInner.DataBnk) { var l = new VarLink { Desc = "Referenced object", - Links = new List { SoundSFXVoice.AudioID }, + Links = new List { SoundItem.BankSourceData.MediaInformation.SourceId }, Edges = new List(), node = CreateVarLinkBox() }; diff --git a/LegendaryExplorer/LegendaryExplorer/UnrealExtensions/AudioStreamHelper.cs b/LegendaryExplorer/LegendaryExplorer/UnrealExtensions/AudioStreamHelper.cs index d0dbba3f3e..c859af76d4 100644 --- a/LegendaryExplorer/LegendaryExplorer/UnrealExtensions/AudioStreamHelper.cs +++ b/LegendaryExplorer/LegendaryExplorer/UnrealExtensions/AudioStreamHelper.cs @@ -318,40 +318,5 @@ public static MemoryStream GetWaveStreamFromISBEntry(ISACTListBankChunk bankEntr return null; //other codecs currently unsupported } } - - public static string GetHircObjTypeString(byte b) => GetHircObjTypeString((HIRCType)b); - - public static string GetHircObjTypeString(HIRCType ht) => - ht switch - { - HIRCType.SoundSXFSoundVoice => "Sound SFX/Sound Voice", - HIRCType.EventAction => "Event Action", - HIRCType.Event => "Event", - HIRCType.RandomOrSequenceContainer => "Random Container or Sequence Container", - HIRCType.ActorMixer => "Actor-Mixer", - HIRCType.MusicSegment => "Music Segment", - HIRCType.MusicTrack => "Music Track", - HIRCType.MusicSwitchContainer => "Music Switch Container", - HIRCType.MusicPlaylistContainer => "Music Playlist Container", - HIRCType.Attenuation => "Attenuation", - HIRCType.Effect => "Effect", - HIRCType.AuxiliaryBus => "Auxiliary Bus", - HIRCType.Settings => "Settings", - HIRCType.SwitchContainer => "Switch Container", - HIRCType.AudioBus => "Audio Bus", - HIRCType.BlendContainer => "Blend Container", - HIRCType.DialogueEvent => "Dialogue Event", - HIRCType.MotionBus => "Motion Bus", - HIRCType.MotionFX => "Motion FX", - _ => "UNKNOWN HIRCOBJECT TYPE!" - }; - - public static string GetEventActionTypeString(WwiseBankParsed.EventActionType actionType) => - actionType switch - { - WwiseBankParsed.EventActionType.Play => "Play", - WwiseBankParsed.EventActionType.Stop => "Stop", - _ => "Unknown Action" - }; } } \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs new file mode 100644 index 0000000000..6415cfe107 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs @@ -0,0 +1,359 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LegendaryExplorer.Misc; +using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorerCore.Gammtek.IO; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal; +using Newtonsoft.Json; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; + +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public partial class BinaryInterpreterWPF +{ + private List StartStaticMeshCollectionActorScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + //get a list of staticmesh stuff from the props. + var smacitems = new List(); + var props = CurrentLoadedExport.GetProperty>("StaticMeshComponents"); + + foreach (var prop in props) + { + if (prop.Value > 0) + { + smacitems.Add(Pcc.GetUExport(prop.Value)); + } + else + { + smacitems.Add(null); + } + } + + //find start of class binary (end of props) + int start = binarystart; + + //Lets make sure this binary is divisible by 64. + if ((data.Length - start) % 64 != 0) + { + subnodes.Add(new BinInterpNode + { + Tag = NodeType.Unknown, + Header = $"{start:X4} Binary data is not divisible by 64 ({data.Length - start})! SMCA binary data should be a length divisible by 64.", + Offset = start + }); + return subnodes; + } + + int smcaindex = 0; + while (start < data.Length && smcaindex < smacitems.Count) + { + BinInterpNode smcanode = new BinInterpNode + { + Tag = NodeType.Unknown + }; + ExportEntry associatedData = smacitems[smcaindex]; + string staticmesh = ""; + string objtext = "Null - unused data"; + if (associatedData != null) + { + objtext = $"[Export {associatedData.UIndex}] {associatedData.ObjectName.Instanced}"; + + //find associated static mesh value for display. + var smc_data = associatedData.DataReadOnly; + int staticmeshstart = 0x4; + bool found = false; + while (staticmeshstart < smc_data.Length && smc_data.Length - 8 >= staticmeshstart) + { + ulong nameindex = EndianReader.ToUInt64(smc_data, staticmeshstart, Pcc.Endian); + if (nameindex < (ulong)CurrentLoadedExport.FileRef.Names.Count && CurrentLoadedExport.FileRef.Names[(int)nameindex] == "StaticMesh") + { + //found it + found = true; + break; + } + else + { + staticmeshstart += 1; + } + } + + if (found) + { + int staticmeshexp = EndianReader.ToInt32(smc_data, staticmeshstart + 0x18, Pcc.Endian); + if (staticmeshexp > 0 && staticmeshexp < CurrentLoadedExport.FileRef.ExportCount) + { + staticmesh = Pcc.GetEntry(staticmeshexp).ObjectName.Instanced; + } + } + } + + smcanode.Header = $"{start:X4} [{smcaindex}] {objtext} {staticmesh}"; + smcanode.Offset = start; + subnodes.Add(smcanode); + + //Read nodes + for (int i = 0; i < 16; i++) + { + float smcadata = BitConverter.ToSingle(data, start); + BinInterpNode node = new BinInterpNode + { + Tag = NodeType.StructLeafFloat, + Header = start.ToString("X4") + }; + + //TODO: Figure out what the rest of these mean + string label = i.ToString(); + switch (i) + { + case 0: + label = "X1:"; + break; + case 1: + label = "X2: X-scaling-Axis: "; + break; + case 2: + label = "X3:"; + break; + case 3: + label = "XT:"; + break; + case 4: + label = "Y1: Y-scaling axis"; + break; + case 5: + label = "Y2:"; + break; + case 6: + label = "Y3:"; + break; + case 7: + label = "YT:"; + break; + case 8: + label = "Z1:"; + break; + case 9: + label = "Z2:"; + break; + case 10: + label = "Z3: Z-scaling axis"; + break; + case 11: + label = "ZT:"; + break; + case 12: + label = "LocX:"; + break; + case 13: + label = "LocY:"; + break; + case 14: + label = "LocZ:"; + break; + case 15: + label = "CameraCollisionDistanceScalar:"; + break; + } + + node.Header += $" {label} {smcadata}"; + + //TODO: Lookup staticmeshcomponent so we can see what this actually is without changing to the export + + node.Offset = start; + smcanode.Items.Add(node); + start += 4; + } + + smcaindex++; + } + //topLevelTree.ItemsSource = subnodes; + binarystart = start; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartTerrainScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var materialMapFile = Path.Combine(AppDirectories.ObjectDatabasesFolder, $"{CurrentLoadedExport.Game}MaterialMap.json"); + Dictionary materialGuidMap = null; + if (File.Exists(materialMapFile)) + { + materialGuidMap = JsonConvert.DeserializeObject>(File.ReadAllText(materialMapFile)); + } + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + subnodes.Add(MakeArrayNode(bin, "Heights", i => MakeUInt16Node(bin, $"{i}"))); + subnodes.Add(MakeArrayNode(bin, "InfoData", i => new BinInterpNode(bin.Position, $"{i}: {(EInfoFlags)bin.ReadByte()}"))); + subnodes.Add(MakeArrayNode(bin, "AlphaMaps", i => MakeArrayNode(bin, $"{i}: Data", j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadByte()}")))); + subnodes.Add(MakeArrayNode(bin, "WeightedTextureMaps", i => MakeEntryNode(bin, $"{i}", Pcc))); + for (int k = Pcc.Game is MEGame.ME1 or MEGame.UDK ? 1 : 2; k > 0; k--) + { + subnodes.Add(MakeArrayNode(bin, "CachedTerrainMaterials", i => + { + var node = MakeMaterialResourceNode(bin, $"{i}", materialGuidMap); + + node.Items.Add(MakeEntryNode(bin, "Terrain", Pcc)); + node.Items.Add(new BinInterpNode(bin.Position, "Mask") + { + IsExpanded = true, + Items = + { + MakeInt32Node(bin, "NumBits"), + new BinInterpNode(bin.Position, $"BitMask: {Convert.ToString(bin.ReadInt64(), 2).PadLeft(64, '0')}") + } + }); + node.Items.Add(MakeArrayNode(bin, "MaterialIds", j => MakeMaterialGuidNode(bin, $"{j}", materialGuidMap), true)); + if (Pcc.Game >= MEGame.ME3) + { + node.Items.Add(MakeGuidNode(bin, "LightingGuid")); + } + + if (Pcc.Game == MEGame.UDK) + { + node.Items.Add(MakeBoolIntNode(bin, "bEnableSpecular")); + } + return node; + })); + } + if (Pcc.Game != MEGame.ME1 && Pcc.Game != MEGame.UDK) + { + subnodes.Add(MakeArrayNode(bin, "CachedDisplacements", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadByte()}"))); + subnodes.Add(MakeFloatNode(bin, "MaxCollisionDisplacement")); + } + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + [Flags] + private enum EInfoFlags : byte + { + TID_Visibility_Off = 1, + TID_OrientationFlip = 2, + TID_Unreachable = 4, + TID_Locked = 8, + } + + private List StartStaticLightCollectionActorScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + //get a list of lightcomponents from the props. + var slcaitems = new List(); + var props = CurrentLoadedExport.GetProperty>("LightComponents"); + + foreach (var prop in props) + { + if (prop.Value > 0) + { + slcaitems.Add(CurrentLoadedExport.FileRef.GetEntry(prop.Value) as ExportEntry); + } + else + { + slcaitems.Add(null); + } + } + + //find start of class binary (end of props) + int start = binarystart; + + //Lets make sure this binary is divisible by 64. + if ((data.Length - start) % 64 != 0) + { + subnodes.Add(new BinInterpNode + { + Tag = NodeType.Unknown, + Header = $"{start:X4} Binary data is not divisible by 64 ({data.Length - start})! SLCA binary data should be a length divisible by 64.", + Offset = start + }); + return subnodes; + } + + int slcaindex = 0; + while (start < data.Length && slcaindex < slcaitems.Count) + { + BinInterpNode slcanode = new BinInterpNode + { + Tag = NodeType.Unknown + }; + ExportEntry assossiateddata = slcaitems[slcaindex]; + string objtext = "Null - unused data"; + if (assossiateddata != null) + { + objtext = $"[Export {assossiateddata.UIndex}] {assossiateddata.ObjectName.Instanced}"; + } + + slcanode.Header = $"{start:X4} [{slcaindex}] {objtext}"; + slcanode.Offset = start; + subnodes.Add(slcanode); + + //Read nodes + for (int i = 0; i < 16; i++) + { + float slcadata = BitConverter.ToSingle(data, start); + BinInterpNode node = new BinInterpNode + { + Tag = NodeType.StructLeafFloat, + Header = start.ToString("X4") + }; + + //TODO: Figure out what the rest of these mean + string label = i.ToString(); + switch (i) + { + case 1: + label = "ScalingXorY1:"; + break; + case 12: + label = "LocX:"; + break; + case 13: + label = "LocY:"; + break; + case 14: + label = "LocZ:"; + break; + case 15: + label = "CameraLayerDistance?:"; + break; + } + + node.Header += $" {label} {slcadata}"; + + node.Offset = start; + slcanode.Items.Add(node); + start += 4; + } + + slcaindex++; + } + + binarystart = start; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/AudioScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/AudioScans.cs index be2be4626c..50e7f04419 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/AudioScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/AudioScans.cs @@ -8,6 +8,7 @@ using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Sound.ISACT; using LegendaryExplorerCore.Unreal; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -129,7 +130,7 @@ private List StartSoundCueScan(byte[] data, ref int binarystart) IsExpanded = true, Items = { - MakeEntryNode(bin, "SoundNode"), + MakeEntryNode(bin, "SoundNode", Pcc), MakeInt32Node(bin, "NodePosX"), MakeInt32Node(bin, "NodePosY") } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs index a94599acfc..8bc5ef156c 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs @@ -42,7 +42,7 @@ public List Items public int Offset { get; set; } = -1; public int Length { get; set; } - public BinaryInterpreterWPF.NodeType Tag { get; set; } + public NodeType Tag { get; set; } public ArrayPropertyChildAddAlgorithm ArrayAddAlgorithm; protected bool _isExpanded; @@ -69,7 +69,7 @@ public void RemoveNullNodes() } } - public BinInterpNode(long pos, string text, BinaryInterpreterWPF.NodeType nodeType = BinaryInterpreterWPF.NodeType.Unknown) : this() + public BinInterpNode(long pos, string text, NodeType nodeType = NodeType.Unknown) : this() { Header = pos >= 0 ? $"0x{pos:X8}: {text}" : text; if (pos >= 0) @@ -88,7 +88,7 @@ public int GetPos() public int GetObjectRefValue(ExportEntry export) { if (UIndexValue != 0) return UIndexValue; //cached - if (Tag is BinaryInterpreterWPF.NodeType.ArrayLeafObject or BinaryInterpreterWPF.NodeType.ObjectProperty or BinaryInterpreterWPF.NodeType.StructLeafObject) + if (Tag is NodeType.ArrayLeafObject or NodeType.ObjectProperty or NodeType.StructLeafObject) { UIndexValue = EndianReader.ToInt32(export.DataReadOnly, GetPos(), export.FileRef.Endian); } @@ -234,4 +234,16 @@ public override bool IsExpanded } } } + + public class BinInterpNodeOffsetReference : BinInterpNode + { + public int OffsetTarget { get; set; } = -1; + + public BinInterpNodeOffsetReference(long pos, string text, + NodeType nodeType = NodeType.ReferenceToOffset) : base(pos, text, + nodeType) + { + + } + } } \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs index 8079968a75..e841aeae65 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs @@ -1,240 +1,21 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; -using System.Text; -using LegendaryExplorer.Misc; using LegendaryExplorer.SharedUI.Interfaces; using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Gammtek.Extensions; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; -using LegendaryExplorerCore.Unreal.BinaryConverters; using LegendaryExplorerCore.Helpers; -using LegendaryExplorerCore.Unreal.Classes; -using static LegendaryExplorer.Tools.TlkManagerNS.TLKManagerWPF; -using Newtonsoft.Json; +using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls { public partial class BinaryInterpreterWPF { - private BinInterpNode MakeStringNode(EndianReader bin, string nodeName) - { - int pos = (int)bin.Position; - int strLen = bin.ReadInt32(); - string str; - if (Pcc.Game is MEGame.ME3 or MEGame.LE3) - { - strLen *= -2; - str = bin.BaseStream.ReadStringUnicodeNull(strLen); - } - else - { - str = bin.BaseStream.ReadStringLatin1Null(strLen); - } - return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; - } - - private BinInterpNode MakeStringUTF8Node(EndianReader bin, string nodeName) - { - int pos = (int)bin.Position; - int strLen = bin.ReadInt32(); - string str = bin.BaseStream.ReadStringUtf8(strLen); - return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; - } - - private BinInterpNode ReadMaterialUniformExpression(EndianReader bin, string prefix = "") - { - NameReference expressionType = bin.ReadNameReference(Pcc); - var node = new BinInterpNode(bin.Position - 8, $"{prefix}{(string.IsNullOrEmpty(prefix) ? "" : ": ")}{expressionType.Instanced}"); - - switch (expressionType.Name) - { - case "FMaterialUniformExpressionAbs": - case "FMaterialUniformExpressionCeil": - case "FMaterialUniformExpressionFloor": - case "FMaterialUniformExpressionFrac": - case "FMaterialUniformExpressionPeriodic": - case "FMaterialUniformExpressionSquareRoot": - node.Items.Add(ReadMaterialUniformExpression(bin, "X")); - break; - case "FMaterialUniformExpressionAppendVector": - node.Items.Add(ReadMaterialUniformExpression(bin, "A")); - node.Items.Add(ReadMaterialUniformExpression(bin, "B")); - node.Items.Add(MakeUInt32Node(bin, "NumComponentsA:")); - break; - case "FMaterialUniformExpressionClamp": - node.Items.Add(ReadMaterialUniformExpression(bin, "Input")); - node.Items.Add(ReadMaterialUniformExpression(bin, "Min")); - node.Items.Add(ReadMaterialUniformExpression(bin, "Max")); - break; - case "FMaterialUniformExpressionConstant": - node.Items.Add(MakeFloatNode(bin, "R")); - node.Items.Add(MakeFloatNode(bin, "G")); - node.Items.Add(MakeFloatNode(bin, "B")); - node.Items.Add(MakeFloatNode(bin, "A")); - node.Items.Add(MakeByteNode(bin, "ValueType")); - break; - case "FMaterialUniformExpressionFmod": - case "FMaterialUniformExpressionMax": - case "FMaterialUniformExpressionMin": - node.Items.Add(ReadMaterialUniformExpression(bin, "A")); - node.Items.Add(ReadMaterialUniformExpression(bin, "B")); - break; - case "FMaterialUniformExpressionFoldedMath": - node.Items.Add(ReadMaterialUniformExpression(bin, "A")); - node.Items.Add(ReadMaterialUniformExpression(bin, "B")); - node.Items.Add(new BinInterpNode(bin.Position, $"Op: {(EFoldedMathOperation)bin.ReadByte()}")); - break; - case "FMaterialUniformExpressionRealTime": - //intentionally left blank. outputs current real-time, has no parameters - break; - case "FMaterialUniformExpressionScalarParameter": - node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); - node.Items.Add(MakeFloatNode(bin, "DefaultValue")); - break; - case "FMaterialUniformExpressionSine": - node.Items.Add(ReadMaterialUniformExpression(bin, "X")); - node.Items.Add(MakeBoolIntNode(bin, "bIsCosine")); - break; - case "FMaterialUniformExpressionTexture": - case "FMaterialUniformExpressionFlipBookTextureParameter": - if (Pcc.Game >= MEGame.ME3) - { - node.Items.Add(MakeInt32Node(bin, "TextureIndex")); - } - else - { - node.Items.Add(MakeEntryNode(bin, "TextureIndex")); - } - break; - case "FMaterialUniformExpressionFlipbookParameter": - node.Items.Add(MakeInt32Node(bin, "Index:")); - node.Items.Add(MakeEntryNode(bin, "TextureIndex")); - break; - case "FMaterialUniformExpressionTextureParameter": - node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); - node.Items.Add(MakeInt32Node(bin, "TextureIndex")); - break; - case "FMaterialUniformExpressionTime": - //intentionally left blank. outputs current scene time, has no parameters - break; - case "FMaterialUniformExpressionVectorParameter": - node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); - node.Items.Add(MakeFloatNode(bin, "Default R")); - node.Items.Add(MakeFloatNode(bin, "Default G")); - node.Items.Add(MakeFloatNode(bin, "Default B")); - node.Items.Add(MakeFloatNode(bin, "Default A")); - break; - case "FMaterialUniformExpressionFractionOfEffectEnabled": - //Not sure what it does, but it doesn't seem to have any parameters - break; - default: - throw new ArgumentException(expressionType.Instanced); - } - - return node; - } - - enum EFoldedMathOperation : byte - { - Add, - Sub, - Mul, - Div, - Dot - } - - private List StartStaticMeshComponentScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - bool bLoadVertexColorData; - uint numVertices; - - int lodDataCount = bin.ReadInt32(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"LODData count: {lodDataCount}")); - subnodes.AddRange(ReadList(lodDataCount, i => - { - BinInterpNode node = new BinInterpNode(bin.Position, $"LODData {i}") - { - IsExpanded = true - }; - node.Items.Add(new BinInterpNode(bin.Position, $"ShadowMaps ({bin.ReadInt32()})") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeEntryNode(bin, $"{j}")) - }); - node.Items.Add(new BinInterpNode(bin.Position, $"ShadowVertexBuffers ({bin.ReadInt32()})") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeEntryNode(bin, $"{j}")) - }); - node.Items.Add(MakeLightMapNode(bin)); - node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new List - { - new BinInterpNode(bin.Position, $"bLoadVertexColorData ({bLoadVertexColorData = bin.ReadBoolByte()})"), - ListInitHelper.ConditionalAdd(bLoadVertexColorData, () => new ITreeItem[] - { - new BinInterpNode(bin.Position, "OverrideVertexColors ") - { - Items = - { - MakeUInt32Node(bin, "Stride:"), - new BinInterpNode(bin.Position, $"NumVertices: {numVertices = bin.ReadUInt32()}"), - ListInitHelper.ConditionalAdd(numVertices > 0, () => new ITreeItem[] - { - MakeInt32Node(bin, "FColor size"), - new BinInterpNode(bin.Position, $"VertexData ({bin.ReadInt32()})") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeColorNode(bin, $"{j}")) - }, - }), - } - } - }) - })); - return node; - })); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartFluidSurfaceComponentScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - if (Pcc.Game == MEGame.ME3) - { - subnodes.Add(MakeLightMapNode(bin)); - } - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - private List StartForceFeedbackWaveformScan(byte[] data, ref int binarystart) { var subnodes = new List(); @@ -270,54 +51,32 @@ private List StartForceFeedbackWaveformScan(byte[] data, ref int bina return subnodes; } - private List StartTerrainComponentScan(byte[] data, ref int binarystart) + private List StartRB_BodySetupScan(byte[] data, ref int binarystart) { var subnodes = new List(); try { var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - bool bIsLeaf; - subnodes.Add(MakeArrayNode(bin, "CollisionVertices", i => MakeVectorNode(bin, $"{i}"))); - subnodes.Add(new BinInterpNode(bin.Position, "BVTree") + + int preCachedPhysDataCount; + int cachedConvexElementsCount; + subnodes.Add(new BinInterpNode(bin.Position, $"PreCachedPhysData ({preCachedPhysDataCount = bin.ReadInt32()})") { - IsExpanded = true, - Items = + Items = ReadList(preCachedPhysDataCount, i => new BinInterpNode(bin.Position, $"{i} CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") { - MakeArrayNode(bin, "Nodes", i => new BinInterpNode(bin.Position, $"{i}") + Items = ReadList(cachedConvexElementsCount, j => { - Items = + int size; + var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") { - MakeBoxNode(bin, "BoundingVolume"), - new BinInterpNode(bin.Position, $"bIsLeaf: {bIsLeaf = bin.ReadBoolInt()}"), - ListInitHelper.ConditionalAdd(bIsLeaf, () => new ITreeItem[] - { - MakeUInt16Node(bin, "XPos"), - MakeUInt16Node(bin, "YPos"), - MakeUInt16Node(bin, "XSize"), - MakeUInt16Node(bin, "YSize"), - }, () => new ITreeItem[] - { - MakeUInt16Node(bin, "NodeIndex[0]"), - MakeUInt16Node(bin, "NodeIndex[1]"), - MakeUInt16Node(bin, "NodeIndex[2]"), - MakeUInt16Node(bin, "NodeIndex[3]"), - }), - MakeFloatNodeConditional(bin, "Unknown float", CurrentLoadedExport.Game != MEGame.UDK), - } + Length = size + 8 + }; + bin.Skip(size); + return item; }) - } + }) }); - subnodes.Add(MakeArrayNode(bin, "PatchBounds", i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - MakeFloatNode(bin, "MinHeight"), - MakeFloatNode(bin, "MaxHeight"), - MakeFloatNode(bin, "MaxDisplacement"), - } - })); - subnodes.Add(MakeLightMapNode(bin)); binarystart = (int)bin.Position; } @@ -329,60 +88,21 @@ private List StartTerrainComponentScan(byte[] data, ref int binarysta return subnodes; } - private List StartTerrainScan(byte[] data, ref int binarystart) + private List StartDominantLightScan() { var subnodes = new List(); try { - var materialMapFile = Path.Combine(AppDirectories.ObjectDatabasesFolder, $"{CurrentLoadedExport.Game}MaterialMap.json"); - Dictionary materialGuidMap = null; - if (File.Exists(materialMapFile)) - { - materialGuidMap = JsonConvert.DeserializeObject>(File.ReadAllText(materialMapFile)); - } - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); + var bin = new EndianReader(CurrentLoadedExport.GetReadOnlyDataStream()) { Endian = CurrentLoadedExport.FileRef.Endian }; - subnodes.Add(MakeArrayNode(bin, "Heights", i => MakeUInt16Node(bin, $"{i}"))); - subnodes.Add(MakeArrayNode(bin, "InfoData", i => new BinInterpNode(bin.Position, $"{i}: {(EInfoFlags)bin.ReadByte()}"))); - subnodes.Add(MakeArrayNode(bin, "AlphaMaps", i => MakeArrayNode(bin, $"{i}: Data", j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadByte()}")))); - subnodes.Add(MakeArrayNode(bin, "WeightedTextureMaps", i => MakeEntryNode(bin, $"{i}"))); - for (int k = Pcc.Game is MEGame.ME1 or MEGame.UDK ? 1 : 2; k > 0; k--) + if (Pcc.Game >= MEGame.ME3) { - subnodes.Add(MakeArrayNode(bin, "CachedTerrainMaterials", i => + int count; + subnodes.Add(new BinInterpNode(bin.Position, $"DominantLightShadowMap ({count = bin.ReadInt32()})") { - var node = MakeMaterialResourceNode(bin, $"{i}", materialGuidMap); - - node.Items.Add(MakeEntryNode(bin, "Terrain")); - node.Items.Add(new BinInterpNode(bin.Position, "Mask") - { - IsExpanded = true, - Items = - { - MakeInt32Node(bin, "NumBits"), - new BinInterpNode(bin.Position, $"BitMask: {Convert.ToString(bin.ReadInt64(), 2).PadLeft(64, '0')}") - } - }); - node.Items.Add(MakeArrayNode(bin, "MaterialIds", j => MakeMaterialGuidNode(bin, $"{j}", materialGuidMap), true)); - if (Pcc.Game >= MEGame.ME3) - { - node.Items.Add(MakeGuidNode(bin, "LightingGuid")); - } - - if (Pcc.Game == MEGame.UDK) - { - node.Items.Add(MakeBoolIntNode(bin, "bEnableSpecular")); - } - return node; - })); - } - if (Pcc.Game != MEGame.ME1 && Pcc.Game != MEGame.UDK) - { - subnodes.Add(MakeArrayNode(bin, "CachedDisplacements", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadByte()}"))); - subnodes.Add(MakeFloatNode(bin, "MaxCollisionDisplacement")); + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUInt16()}")) + }); } - - binarystart = (int)bin.Position; } catch (Exception ex) { @@ -392,142 +112,7 @@ private List StartTerrainScan(byte[] data, ref int binarystart) return subnodes; } - [Flags] - enum EInfoFlags : byte - { - TID_Visibility_Off = 1, - TID_OrientationFlip = 2, - TID_Unreachable = 4, - TID_Locked = 8, - } - - private BinInterpNode MakeLightMapNode(EndianReader bin, List<(int, int)> lightmapChunksToRemove = null) - { - ELightMapType lightMapType; - int bulkSerializeElementCount; - int bulkSerializeDataSize; - - var retvalue = new BinInterpNode(bin.Position, "LightMap ") - { - IsExpanded = true, - Items = - { - new BinInterpNode(bin.Position, $"LightMapType: {lightMapType = (ELightMapType) bin.ReadInt32()}"), - ListInitHelper.ConditionalAdd(lightMapType != ELightMapType.LMT_None, () => - { - //chunk starts at 0 - postion of LM type - var chunk = ((int)bin.Position - 4,0); - var tree = new List - { - new BinInterpNode(bin.Position, $"LightGuids ({bin.ReadInt32()})") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadGuid()}")) - }, - ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_1D, () => new ITreeItem[] - { - MakeEntryNode(bin, "Owner"), - MakeUInt32Node(bin, "BulkDataFlags:"), - new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), - new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), - MakeInt32Node(bin, "BulkDataOffsetInFile"), - new BinInterpNode(bin.Position, $"DirectionalSamples: ({bulkSerializeElementCount})") - { - Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, - $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) - }) - }, - MakeVectorNode(bin, "ScaleVector 1"), - MakeVectorNode(bin, "ScaleVector 2"), - MakeVectorNode(bin, "ScaleVector 3"), - Pcc.Game < MEGame.ME3 ? MakeVectorNode(bin, "ScaleVector 4") : null, - MakeUInt32Node(bin, "BulkDataFlags:"), - new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), - new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), - MakeInt32Node(bin, "BulkDataOffsetInFile"), - new BinInterpNode(bin.Position, $"SimpleSamples: ({bulkSerializeElementCount})") - { - Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, - $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) - }) - }, - }.NonNull()), - ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_2D, () => new List - { - MakeEntryNode(bin, "Texture 1"), - MakeVectorNodeEditable(bin, "ScaleVector 1", true), - MakeEntryNode(bin, "Texture 2"), - MakeVectorNodeEditable(bin, "ScaleVector 2", true), - MakeEntryNode(bin, "Texture 3"), - MakeVectorNodeEditable(bin, "ScaleVector 3", true), - ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] - { - MakeEntryNode(bin, "Texture 4"), - MakeVectorNodeEditable(bin, "ScaleVector 4", true), - }), - MakeVector2DNodeEditable(bin, "CoordinateScale", true), - MakeVector2DNodeEditable(bin, "CoordinateBias", true) - }), - ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_3, () => new ITreeItem[] - { - MakeInt32Node(bin, "Unknown"), - MakeUInt32Node(bin, "BulkDataFlags:"), - new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), - new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), - MakeInt32Node(bin, "BulkDataOffsetInFile"), - new BinInterpNode(bin.Position, $"DirectionalSamples?: ({bulkSerializeElementCount})") - { - Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, - $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) - }) - }, - MakeVectorNode(bin, "ScaleVector?"), - MakeVectorNode(bin, "ScaleVector?") - }), - ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_4 || lightMapType == ELightMapType.LMT_6, () => new List - { - MakeEntryNode(bin, "Texture 1"), - new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), - MakeEntryNode(bin, "Texture 2"), - new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), - MakeEntryNode(bin, "Texture 3"), - new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), - new ListInitHelper.InitCollection(ReadList(4, j => MakeFloatNode(bin, "Unknown float"))), - }), - ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_5, () => new ITreeItem[] - { - MakeInt32Node(bin, "Unknown"), - MakeUInt32Node(bin, "BulkDataFlags:"), - new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), - new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), - MakeInt32Node(bin, "BulkDataOffsetInFile"), - new BinInterpNode(bin.Position, $"SimpleSamples?: ({bulkSerializeElementCount})") - { - Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, - $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) - }) - }, - MakeVectorNode(bin, "ScaleVector?") - }), - }; - chunk.Item2 = (int)bin.Position; - lightmapChunksToRemove?.Add(chunk); - return tree; - }) - } - }; - - return retvalue; - } - - private List StartBrushComponentScan(byte[] data, ref int binarystart) + private List StartPhysicsAssetInstanceScan(byte[] data, ref int binarystart) { var subnodes = new List(); try @@ -535,26 +120,19 @@ private List StartBrushComponentScan(byte[] data, ref int binarystart var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - int cachedConvexElementsCount; - subnodes.Add(new BinInterpNode(bin.Position, "CachedPhysBrushData") + int count; + subnodes.Add(new BinInterpNode(bin.Position, $"CollisionDisableTable ({count = bin.ReadInt32()})") { IsExpanded = true, - Items = + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}") { - new BinInterpNode(bin.Position, $"CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") + Items = { - Items = ReadList(cachedConvexElementsCount, j => - { - int size; - var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") - { - Length = size + 8 - }; - bin.Skip(size); - return item; - }) + MakeInt32Node(bin, "BodyAIndex"), + MakeInt32Node(bin, "BodyBIndex"), + MakeBoolIntNode(bin, "False") } - } + }) }); binarystart = (int)bin.Position; @@ -567,7 +145,7 @@ private List StartBrushComponentScan(byte[] data, ref int binarystart return subnodes; } - private List StartRB_BodySetupScan(byte[] data, ref int binarystart) + private List StartCookedBulkDataInfoContainerScan(byte[] data, ref int binarystart) { var subnodes = new List(); try @@ -575,22 +153,19 @@ private List StartRB_BodySetupScan(byte[] data, ref int binarystart) var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - int preCachedPhysDataCount; - int cachedConvexElementsCount; - subnodes.Add(new BinInterpNode(bin.Position, $"PreCachedPhysData ({preCachedPhysDataCount = bin.ReadInt32()})") + int count; + subnodes.Add(new BinInterpNode(bin.Position, $"Global Mip Data? ({count = bin.ReadInt32()})") { - Items = ReadList(preCachedPhysDataCount, i => new BinInterpNode(bin.Position, $"{i} CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") + IsExpanded = true, + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{bin.BaseStream.ReadStringLatin1Null(bin.ReadInt32())}") { - Items = ReadList(cachedConvexElementsCount, j => + Items = { - int size; - var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") - { - Length = size + 8 - }; - bin.Skip(size); - return item; - }) + new BinInterpNode(bin.Position, $"Storage Type: {(StorageTypes)bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }, + MakeInt32Node(bin, "Uncompressed Size"), + MakeInt32Node(bin, "Offset"), + MakeInt32Node(bin, "Compressed Size"), + } }) }); @@ -604,45 +179,38 @@ private List StartRB_BodySetupScan(byte[] data, ref int binarystart) return subnodes; } - private List StartModelComponentScan(byte[] data, ref int binarystart) + private List StartMorphTargetScan(byte[] data, ref int binarystart) { var subnodes = new List(); - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; try { - int count; + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - subnodes.Add(MakeEntryNode(bin, "Model")); - subnodes.Add(MakeInt32Node(bin, "ZoneIndex")); - subnodes.Add(new BinInterpNode(bin.Position, $"Elements ({count = bin.ReadInt32()})") + + subnodes.Add(MakeArrayNode(bin, "MorphLODModels", i => new BinInterpNode(bin.Position, $"{i}") { - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: FModelElement") + Items = { - Items = + MakeArrayNode(bin, "Vertices", j => new BinInterpNode(bin.Position, $"{j}") { - MakeLightMapNode(bin), - MakeEntryNode(bin, "Component"), - MakeEntryNode(bin, "Material"), - new BinInterpNode(bin.Position, $"Nodes ({count = bin.ReadInt32()})") - { - Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadUInt16()}")) - }, - new BinInterpNode(bin.Position, $"ShadowMaps ({count = bin.ReadInt32()})") - { - Items = ReadList(count, j => MakeEntryNode(bin, $"{j}")) - }, - new BinInterpNode(bin.Position, $"IrrelevantLights ({count = bin.ReadInt32()})") + Items = { - Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadGuid()}")) + MakeVectorNode(bin, "PositionDelta"), + MakePackedNormalNode(bin, "TangentZDelta"), + MakeUInt16Node(bin, "SourceIdx") } - } - }) - }); - subnodes.Add(new BinInterpNode(bin.Position, $"ComponentIndex: {bin.ReadUInt16()}")); - subnodes.Add(new BinInterpNode(bin.Position, $"Nodes ({count = bin.ReadInt32()})") + }), + MakeInt32Node(bin, "NumBaseMeshVerts") + } + })); + subnodes.Add(MakeArrayNode(bin, "BoneOffsets", i => new BinInterpNode(bin.Position, $"{i}") { - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUInt16()}")) - }); + Items = + { + MakeVectorNode(bin, "Offset"), + MakeNameNode(bin, "Bone", Pcc) + } + })); binarystart = (int)bin.Position; } @@ -654,272 +222,14 @@ private List StartModelComponentScan(byte[] data, ref int binarystart return subnodes; } - private List StartLightComponentScan(byte[] data, int binarystart) + private List StartShadowMap1DScan(byte[] data, int binarystart) { var subnodes = new List(); - if (Pcc.Game == MEGame.UDK) - { - return subnodes; - } try { var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - - int count; - foreach (string propName in new[] { "InclusionConvexVolumes", "ExclusionConvexVolumes" }) - { - subnodes.Add(new BinInterpNode(bin.Position, $"{propName} ({count = bin.ReadInt32()})") - { - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - new BinInterpNode(bin.Position, $"Planes ({count = bin.ReadInt32()})") - { - Items = ReadList(count, j => - new BinInterpNode(bin.Position, $"{j}: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()}, W: {bin.ReadSingle()})")) - }, - new BinInterpNode(bin.Position, $"PermutedPlanes ({count = bin.ReadInt32()})") - { - Items = ReadList(count, j => - new BinInterpNode(bin.Position, $"{j}: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()}, W: {bin.ReadSingle()})")) - } - } - }) - }); - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartDominantLightScan() - { - var subnodes = new List(); - try - { - var bin = new EndianReader(CurrentLoadedExport.GetReadOnlyDataStream()) { Endian = CurrentLoadedExport.FileRef.Endian }; - - if (Pcc.Game >= MEGame.ME3) - { - int count; - subnodes.Add(new BinInterpNode(bin.Position, $"DominantLightShadowMap ({count = bin.ReadInt32()})") - { - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUInt16()}")) - }); - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartBioPawnScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - int count; - subnodes.Add(new BinInterpNode(bin.Position, $"AnimationMap? ({count = bin.ReadInt32()})") - { - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc)}: {entryRefString(bin)}", NodeType.StructLeafObject) { Length = 4 }) - }); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartPhysicsAssetInstanceScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - int count; - subnodes.Add(new BinInterpNode(bin.Position, $"CollisionDisableTable ({count = bin.ReadInt32()})") - { - IsExpanded = true, - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - MakeInt32Node(bin, "BodyAIndex"), - MakeInt32Node(bin, "BodyBIndex"), - MakeBoolIntNode(bin, "False") - } - }) - }); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartCookedBulkDataInfoContainerScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - int count; - subnodes.Add(new BinInterpNode(bin.Position, $"Global Mip Data? ({count = bin.ReadInt32()})") - { - IsExpanded = true, - Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{bin.BaseStream.ReadStringLatin1Null(bin.ReadInt32())}") - { - Items = - { - new BinInterpNode(bin.Position, $"Storage Type: {(StorageTypes)bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }, - MakeInt32Node(bin, "Uncompressed Size"), - MakeInt32Node(bin, "Offset"), - MakeInt32Node(bin, "Compressed Size"), - } - }) - }); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartMorphTargetScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeArrayNode(bin, "MorphLODModels", i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - MakeArrayNode(bin, "Vertices", j => new BinInterpNode(bin.Position, $"{j}") - { - Items = - { - MakeVectorNode(bin, "PositionDelta"), - MakePackedNormalNode(bin, "TangentZDelta"), - MakeUInt16Node(bin, "SourceIdx") - } - }), - MakeInt32Node(bin, "NumBaseMeshVerts") - } - })); - subnodes.Add(MakeArrayNode(bin, "BoneOffsets", i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - MakeVectorNode(bin, "Offset"), - MakeNameNode(bin, "Bone") - } - })); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartSFXMorphFaceFrontEndDataSourceScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeArrayNode(bin, "DefaultSettingsNames", i => new BinInterpNode(bin.Position, $"{i}") - { - IsExpanded = true, - Items = - { - MakeStringNode(bin, "Name"), - MakeInt32Node(bin, "Index") - } - }, true)); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartBioCreatureSoundSetScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeArrayNode(bin, "UnkToCueMap?", i => new BinInterpNode(bin.Position, $"{i}") - { - IsExpanded = true, - Items = - { - MakeByteNode(bin, "Unknown byte"), - MakeInt32Node(bin, "index into m_aAllCues?") - } - }, true)); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartShadowMap1DScan(byte[] data, int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - if (Pcc.Game.IsGame3()) + if (Pcc.Game.IsGame3()) { subnodes.Add(new BinInterpNode(bin.Position, $"float size ({bin.ReadInt32()})")); } @@ -950,7 +260,7 @@ private List StartPolysScan(byte[] data, ref int binarystart) int polysCount = bin.ReadInt32(); subnodes.Add(new BinInterpNode(bin.Position - 4, $"Count: {polysCount}")); subnodes.Add(MakeInt32Node(bin, "Max")); - subnodes.Add(MakeEntryNode(bin, "Owner (self)")); + subnodes.Add(MakeEntryNode(bin, "Owner (self)", Pcc)); if (polysCount > 0) { subnodes.Add(new BinInterpNode(bin.Position, $"Elements ({polysCount})") @@ -969,9 +279,9 @@ private List StartPolysScan(byte[] data, ref int binarystart) new BinInterpNode(bin.Position, $"{j}: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()})")) }, MakeInt32Node(bin, "PolyFlags"), - MakeEntryNode(bin, "Actor"), + MakeEntryNode(bin, "Actor", Pcc), new BinInterpNode(bin.Position, $"ItemName: {bin.ReadNameReference(Pcc)}"), - MakeEntryNode(bin, "Material"), + MakeEntryNode(bin, "Material", Pcc), MakeInt32Node(bin, "iLink"), MakeInt32Node(bin, "iBrushPoly"), MakeFloatNode(bin, "ShadowMapScale"), @@ -987,7 +297,7 @@ private List StartPolysScan(byte[] data, ref int binarystart) MakeFloatNode(bin, "EmissiveBoost"), MakeFloatNode(bin, "DiffuseBoost"), MakeFloatNode(bin, "SpecularBoost"), - MakeNameNode(bin, "RulesetVariation") + MakeNameNode(bin, "RulesetVariation", Pcc) }), } }) @@ -1004,12 +314,6 @@ private List StartPolysScan(byte[] data, ref int binarystart) return subnodes; } - private string entryRefString(EndianReader bin) - { - int n = bin.ReadInt32(); - return $"#{n} {CurrentLoadedExport.FileRef.GetEntryString(n)}"; - } - private List StartModelScan(byte[] data, ref int binarystart) { var subnodes = new List(); @@ -1064,7 +368,7 @@ private List StartModelScan(byte[] data, ref int binarystart) }) }); - subnodes.Add(MakeEntryNode(bin, "Owner (self)")); + subnodes.Add(MakeEntryNode(bin, "Owner (self)", Pcc)); int surfsCount = bin.ReadInt32(); subnodes.Add(new BinInterpNode(bin.Position - 4, $"Surfaces ({surfsCount})") { @@ -1072,14 +376,14 @@ private List StartModelScan(byte[] data, ref int binarystart) { Items = new List { - MakeEntryNode(bin, "Material"), + MakeEntryNode(bin, "Material", Pcc), MakeInt32Node(bin, "PolyFlags"), MakeInt32Node(bin, "pBase"), MakeInt32Node(bin, "vNormal"), MakeInt32Node(bin, "vTextureU"), MakeInt32Node(bin, "vTextureV"), MakeInt32Node(bin, "iBrushPoly"), - MakeEntryNode(bin, "Actor"), + MakeEntryNode(bin, "Actor", Pcc), new BinInterpNode(bin.Position, $"Plane: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()}, W: {bin.ReadSingle()})"), MakeFloatNode(bin, "ShadowMapScale"), MakeInt32Node(bin, "LightingChannels(Bitfield)"), @@ -1113,7 +417,7 @@ private List StartModelScan(byte[] data, ref int binarystart) { Items = new List { - MakeEntryNode(bin, "ZoneActor"), + MakeEntryNode(bin, "ZoneActor", Pcc), MakeFloatNode(bin, "LastRenderTime"), new BinInterpNode(bin.Position, $"Connectivity: {Convert.ToString(bin.ReadInt64(), 2).PadLeft(64, '0')}"), new BinInterpNode(bin.Position, $"Visibility: {Convert.ToString(bin.ReadInt64(), 2).PadLeft(64, '0')}"), @@ -1121,7 +425,7 @@ private List StartModelScan(byte[] data, ref int binarystart) }) }); - subnodes.Add(MakeEntryNode(bin, "Polys")); + subnodes.Add(MakeEntryNode(bin, "Polys", Pcc)); subnodes.Add(MakeInt32Node(bin, "integer Size")); int leafHullsCount = bin.ReadInt32(); subnodes.Add(new BinInterpNode(bin.Position - 4, $"LeafHulls ({leafHullsCount})") @@ -1218,7 +522,7 @@ private List StartModelScan(byte[] data, ref int binarystart) return subnodes; } - private List StartDecalComponentScan(byte[] data, ref int binarystart) + private List StartWorldScan(byte[] data, ref int binarystart) { var subnodes = new List(); try @@ -1226,121 +530,14 @@ private List StartDecalComponentScan(byte[] data, ref int binarystart var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - int numStaticRecievers; - int count; - int fDecalVertexSize; - var item = new BinInterpNode(bin.Position, $"StaticReceivers: {numStaticRecievers = bin.ReadInt32()}") + subnodes.Add(MakeEntryNode(bin, "PersistentLevel", Pcc)); + if (Pcc.Game == MEGame.ME3 || Pcc.Game.IsLEGame()) { - IsExpanded = true - }; - item.Items = ReadList(numStaticRecievers, i => + subnodes.Add(MakeEntryNode(bin, "PersistentFaceFXAnimSet", Pcc)); + } + subnodes.AddRange(ReadList(4, i => new BinInterpNode(bin.Position, $"EditorView {i}") { - var node = new BinInterpNode(bin.Position, $"{i}"); - try - { - node.Items.Add(MakeEntryNode(bin, "Component")); - node.Items.Add(new BinInterpNode(bin.Position, $"FDecalVertex Size: {fDecalVertexSize = bin.ReadInt32()}")); - BinInterpNode interpNode = new BinInterpNode(bin.Position, $"Vertices ({count = bin.ReadInt32()})") - { - Length = 4 + fDecalVertexSize * count - }; - interpNode.Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}") - { - Length = fDecalVertexSize, - Items = - { - MakeVectorNode(bin, "Position"), - MakePackedNormalNode(bin, "TangentX"), - MakePackedNormalNode(bin, "TangentZ"), - ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] - { - MakeVector2DNode(bin, "LegacyProjectedUVs") - }), - MakeVector2DNode(bin, "LightMapCoordinate"), - ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] - { - MakeVector2DNode(bin, "LegacyNormalTransform[0]"), - MakeVector2DNode(bin, "LegacyNormalTransform[1]") - }), - } - }); - node.Items.Add(interpNode); - node.Items.Add(MakeInt32Node(bin, "unsigned short size")); - node.Items.Add(new BinInterpNode(bin.Position, $"Indices ({count = bin.ReadInt32()})") - { - Length = 4 + count * 2, - Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadUInt16()}") { Length = 2 }) - }); - node.Items.Add(MakeUInt32Node(bin, "NumTriangles")); - node.Items.Add(MakeLightMapNode(bin)); - if (Pcc.Game >= MEGame.ME3) - { - node.Items.Add(new BinInterpNode(bin.Position, $"ShadowMap1D ({count = bin.ReadInt32()})") - { - Length = 4 + count * 4, - Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {entryRefString(bin)}") { Length = 4 }) - }); - node.Items.Add(MakeInt32Node(bin, "Data")); - node.Items.Add(MakeInt32Node(bin, "InstanceIndex")); - } - } - catch (Exception e) - { - node.Items.Add(new BinInterpNode { Header = $"Error reading binary data: {e}" }); - } - return node; - }); - subnodes.Add(item); - - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private static List ReadList(int count, Func selector) - { - //sanity check. if this number is too small, feel free to increase - if (count > 5097152) - { - throw new Exception($"Is this actually a list? {count} seems like an incorrect count"); - } - var list = new List(); - try - { - for (int i = 0; i < count; i++) - { - list.Add(selector(i)); - } - } - catch (Exception ex) - { - new BinInterpNode { Header = $"Error reading binary data: {ex}" }; - } - - return list; - } - - private List StartWorldScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeEntryNode(bin, "PersistentLevel")); - if (Pcc.Game == MEGame.ME3 || Pcc.Game.IsLEGame()) - { - subnodes.Add(MakeEntryNode(bin, "PersistentFaceFXAnimSet")); - } - subnodes.AddRange(ReadList(4, i => new BinInterpNode(bin.Position, $"EditorView {i}") - { - Items = + Items = { MakeVectorNode(bin, "CamPosition"), new BinInterpNode(bin.Position, $"CamRotation: (Pitch: {bin.ReadInt32()}, Yaw: {bin.ReadInt32()}, Roll: {bin.ReadInt32()})"), @@ -1351,17 +548,17 @@ private List StartWorldScan(byte[] data, ref int binarystart) { subnodes.Add(MakeFloatNode(bin, "unkFloat")); } - subnodes.Add(MakeEntryNode(bin, "Null")); + subnodes.Add(MakeEntryNode(bin, "Null", Pcc)); if (Pcc.Game is MEGame.ME1 or MEGame.LE1) { - subnodes.Add(MakeEntryNode(bin, "DecalManager")); + subnodes.Add(MakeEntryNode(bin, "DecalManager", Pcc)); } int extraObjsCount; subnodes.Add(new BinInterpNode(bin.Position, $"ExtraReferencedObjects: {extraObjsCount = bin.ReadInt32()}") { ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, - Items = ReadList(extraObjsCount, i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}", NodeType.ArrayLeafObject)) + Items = ReadList(extraObjsCount, i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}", NodeType.ArrayLeafObject)) }); binarystart = (int)bin.Position; @@ -1387,10 +584,10 @@ private List StartStackScan(out int endPos) IsExpanded = true }; List items = item.Items; - items.Add(MakeEntryNode(bin, "Node", out int uIndex)); + items.Add(MakeEntryNode(bin, "Node", Pcc, out int uIndex)); if (Pcc.Game is not MEGame.UDK) { - items.Add(MakeEntryNode(bin, "StateNode")); + items.Add(MakeEntryNode(bin, "StateNode", Pcc)); } items.Add(new BinInterpNode(bin.Position, $"ProbeMask: {bin.ReadUInt64():X16}")); if (Pcc.Game >= MEGame.ME3 || Pcc.Platform is MEPackage.GamePlatform.PS3) @@ -1405,8 +602,8 @@ private List StartStackScan(out int endPos) { Items = { - MakeEntryNode(bin, "State"), - MakeEntryNode(bin, "Node"), + MakeEntryNode(bin, "State", Pcc), + MakeEntryNode(bin, "Node", Pcc), MakeInt32Node(bin, "Offset") } })); @@ -1434,13 +631,13 @@ private List StartMetaDataScan(byte[] data, ref int binarystart) bin.JumpTo(binarystart); subnodes.Add(MakeArrayNode(bin, "Object to Metadata Map", i => { - var node = Pcc.Game is MEGame.UDK ? MakeEntryNode(bin, "Object") : MakeStringNode(bin, "Object"); + var node = Pcc.Game is MEGame.UDK ? MakeEntryNode(bin, "Object", Pcc) : MakeStringNode(bin, "Object", Pcc.Game); node.IsExpanded = true; int count = bin.ReadInt32(); while (count-- > 0) { var metadataType = bin.ReadNameReference(Pcc); - node.Items.Add(MakeStringNode(bin, metadataType.Instanced)); + node.Items.Add(MakeStringNode(bin, metadataType.Instanced, Pcc.Game)); } return node; }, true)); @@ -1461,7 +658,7 @@ private List StartTextBufferScan(byte[] data, int binarystart) bin.JumpTo(binarystart); subnodes.Add(MakeInt32Node(bin, "Position")); subnodes.Add(MakeInt32Node(bin, "Top")); - subnodes.Add(MakeStringNode(bin, "Text")); + subnodes.Add(MakeStringNode(bin, "Text", Pcc.Game)); } catch (Exception ex) { @@ -1470,4093 +667,817 @@ private List StartTextBufferScan(byte[] data, int binarystart) return subnodes; } - private List StartBioTlkFileSetScan(byte[] data, ref int binarystart) + private List StartAnimSequenceScan(byte[] data, ref int binarystart) { var subnodes = new List(); - try - { - int offset = binarystart; - if (data.Length > binarystart) - { - int count = BitConverter.ToInt32(data, offset); - subnodes.Add(new BinInterpNode - { - Header = $"0x{offset:X4} Count: {count}", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - //offset += 4; - //offset += 8; //skip 8 - - for (int i = 0; i < count; i++) - { - int langRef = BitConverter.ToInt32(data, offset); - int langTlkCount = BitConverter.ToInt32(data, offset + 8); - var languageNode = new BinInterpNode - { - Header = $"0x{offset:X4} {CurrentLoadedExport.FileRef.GetNameEntry(langRef)} - {langTlkCount} entries", - Offset = offset, - Tag = NodeType.StructLeafName, - IsExpanded = true - }; - subnodes.Add(languageNode); - offset += 12; - for (int k = 0; k < langTlkCount; k++) - { - int tlkIndex = BitConverter.ToInt32(data, offset); //-1 in reader - languageNode.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X4} TLK #{k} export: {tlkIndex} {CurrentLoadedExport.FileRef.GetEntryString(tlkIndex)}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - } - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } + #region UDK AKF_PerTrackCompression - private List StartBioStateEventMapScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try + if (Pcc.Game == MEGame.UDK && CurrentLoadedExport.GetProperty("KeyEncodingFormat")?.Value.Name == "AKF_PerTrackCompression") { - int offset = binarystart; - - int eCount = BitConverter.ToInt32(data, offset); - var EventCountNode = new BinInterpNode + try { - Header = $"0x{offset:X4} State Event Count: {eCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(EventCountNode); + var TrackOffsets = CurrentLoadedExport.GetProperty>("CompressedTrackOffsets"); + var numFrames = CurrentLoadedExport.GetProperty("NumFrames")?.Value ?? 0; - for (int e = 0; e < eCount; e++) //EVENTS - { - int iEventID = BitConverter.ToInt32(data, offset); //EVENT ID - var EventIDs = new BinInterpNode - { - Header = $"0x{offset:X5} [{e}] State Transition ID: {iEventID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - EventCountNode.Items.Add(EventIDs); + List boneList = ((ExportEntry)CurrentLoadedExport.Parent).GetProperty>("TrackBoneNames").Select(np => $"{np}").ToList(); - int EventMapInstVer = BitConverter.ToInt32(data, offset); //Event Instance Version - EventIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {EventMapInstVer} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; + var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; + bin.JumpTo(binarystart); - int nTransitions = BitConverter.ToInt32(data, offset); //Count of State Events - var TransitionsIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Transitions: {nTransitions} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - EventIDs.Items.Add(TransitionsIDs); + int numTracks = bin.ReadInt32() * 2; + bin.Skip(-4); - for (int t = 0; t < nTransitions; t++) //TRANSITIONS + BinInterpNode rawAnimDataNode = MakeInt32Node(bin, "RawAnimationData: NumTracks"); + subnodes.Add(rawAnimDataNode); + for (int i = 0; i < numTracks; i++) { - int transTYPE = BitConverter.ToInt32(data, offset); //Get TYPE - if (transTYPE == 0) // TYPE 0 = BOOL STATE EVENT - { - offset += 8; - int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot - offset -= 8; - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Transition on Bool {tPlotID}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); - - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - tPlotID = BitConverter.ToInt32(data, offset); //Plot - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Plot ID: {tPlotID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tNewValue = BitConverter.ToInt32(data, offset); //NewValue - bool bNewValue = false; - if (tNewValue == 1) { bNewValue = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} New Value: {tNewValue} {bNewValue} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool - bool bUseParam = false; - if (tUseParam == 1) { bUseParam = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (transTYPE == 1) //TYPE 1 = CONSEQUENCE - { - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Consequence", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); - - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tConsequenceParam = BitConverter.ToInt32(data, offset); //Consequence parameter - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Consequence Parameter: {tConsequenceParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (transTYPE == 2) // TYPE 2 = FLOAT TRANSITION + int keySize = bin.ReadInt32(); + int numKeys = bin.ReadInt32(); + for (int j = 0; j < numKeys; j++) { - offset += 8; - int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot - offset -= 8; - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} transition on Float {tPlotID}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); - - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - if (Pcc.Game.IsGame2()) + if (keySize == 12) { - int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool - bool bIncrement = false; - if (tIncrement == 1) { bIncrement = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; + rawAnimDataNode.Items.Add(MakeVectorNode(bin, $"{boneList[i / 2]}, PosKey {j}")); } - - tPlotID = BitConverter.ToInt32(data, offset); //Plot - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Plot ID: {tPlotID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - float tNewValue = BitConverter.ToInt32(data, offset); //NewValue - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} New Value: {tNewValue} ", - Offset = offset, - Tag = NodeType.StructLeafFloat - }); - offset += 4; - - int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool - bool bUseParam = false; - if (tUseParam == 1) { bUseParam = true; } - nTransition.Items.Add(new BinInterpNode + else if (keySize == 16) { - Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - if (!Pcc.Game.IsGame2()) + rawAnimDataNode.Items.Add(MakeQuatNode(bin, $"{boneList[i / 2]}, RotKey {j}")); + } + else { - int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool - bool bIncrement = false; - if (tIncrement == 1) { bIncrement = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; + throw new NotImplementedException($"Unexpected key size: {keySize}"); } } - else if (transTYPE == 3) // TYPE 3 = FUNCTION - { - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Function", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); + } - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int PackageName = BitConverter.ToInt32(data, offset); //Package name - offset += 4; - int PackageIdx = BitConverter.ToInt32(data, offset); //Package name idx - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Package Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(PackageName), PackageIdx).Instanced}", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 4; - - int ClassName = BitConverter.ToInt32(data, offset); //Class name - offset += 4; - int ClassIdx = BitConverter.ToInt32(data, offset); //Class name idx - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Class Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(ClassName), ClassIdx).Instanced}", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 4; - - int FunctionName = BitConverter.ToInt32(data, offset); //Function name - offset += 4; - int FunctionIdx = BitConverter.ToInt32(data, offset); //Function name idx - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Function Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(FunctionName), FunctionIdx).Instanced}", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 4; - - int Parameter = BitConverter.ToInt32(data, offset); //Parameter - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Parameter: {Parameter} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (transTYPE == 4) // TYPE 4 = INT TRANSITION - { - offset += 8; - int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot - offset -= 8; - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} transition on INT {tPlotID}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); + subnodes.Add(MakeInt32Node(bin, "AnimBinary length")); + var startOff = bin.Position; + for (int i = 0; i < boneList.Count; i++) + { + var boneNode = new BinInterpNode(bin.Position, boneList[i]); + subnodes.Add(boneNode); - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - tPlotID = BitConverter.ToInt32(data, offset); //Plot - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Plot ID: {tPlotID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tNewValue = BitConverter.ToInt32(data, offset); //NewValue - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} New Value: {tNewValue} ", - Offset = offset, - Tag = NodeType.StructLeafFloat - }); - offset += 4; - - int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool - bool bUseParam = false; - if (tUseParam == 1) { bUseParam = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool - bool bIncrement = false; - if (tIncrement == 1) { bIncrement = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (transTYPE == 5) // TYPE 5 = LOCAL BOOL - { - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Local Bool", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 36; - TransitionsIDs.Items.Add(nTransition); - } - else if (transTYPE == 6) // TYPE 6 = LOCAL FLOAT - { - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Local Float", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 36; - TransitionsIDs.Items.Add(nTransition); - } - else if (transTYPE == 7) // TYPE 7 = LOCAL INT - { - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Local Int", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); + int posOff = TrackOffsets[i * 2]; - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tObjtag = BitConverter.ToInt32(data, offset); //Use Object tag?? - bool bObjtag = false; - if (tObjtag == 1) { bObjtag = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Object Tag: {tObjtag} {bObjtag} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int FunctionName = BitConverter.ToInt32(data, offset); //Function name - offset += 4; - int FunctionIdx = BitConverter.ToInt32(data, offset); //Function name - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Function Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(FunctionName), FunctionIdx).Instanced}", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 4; - - int TagName = BitConverter.ToInt32(data, offset); //Object name - offset += 4; - int TagIdx = BitConverter.ToInt32(data, offset); //Object idx - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Object Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(TagName), TagIdx).Instanced}", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 4; - - int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool - bool bUseParam = false; - if (tUseParam == 1) { bUseParam = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tNewValue = BitConverter.ToInt32(data, offset); //NewValue - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} New Value: {tNewValue} ", - Offset = offset, - Tag = NodeType.StructLeafFloat - }); - offset += 4; - } - else if (transTYPE == 8) // TYPE 8 = SUBSTATE + if (posOff >= 0) { - offset += 8; - int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot - offset -= 8; - var nTransition = new BinInterpNode - { - Header = $"0x{offset:X5} Type: {transTYPE} Substate Transition on Bool {tPlotID}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - TransitionsIDs.Items.Add(nTransition); + bin.JumpTo(startOff + posOff); + int header = bin.ReadInt32(); + int numKeys = header & 0x00FFFFFF; + int formatFlags = (header >> 24) & 0x0F; + AnimationCompressionFormat keyFormat = (AnimationCompressionFormat)((header >> 28) & 0x0F); - int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - tPlotID = BitConverter.ToInt32(data, offset); //Plot - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Plot ID: {tPlotID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tNewValue = BitConverter.ToInt32(data, offset); //NewState Bool - bool bNewValue = false; - if (tNewValue == 1) { bNewValue = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} New State: {tNewValue} {bNewValue}", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool - bool bUseParam = false; - if (tUseParam == 1) { bUseParam = true; } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tParentType = BitConverter.ToInt32(data, offset); //Parent OR type flag - bool bParentType = false; - string sParentType = "ALL of siblings TRUE => Parent TRUE"; - if (tParentType == 1) - { - bParentType = true; - sParentType = "ANY of siblings TRUE => Parent TRUE"; - } - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Parent OR type: {tParentType} {bParentType} {sParentType}", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int ParentIdx = BitConverter.ToInt32(data, offset); //Parent Bool - nTransition.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Parent Bool: {ParentIdx} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int sibCount = BitConverter.ToInt32(data, offset); //Sibling Substates - var SiblingIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Sibling Substates Count: {sibCount} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - nTransition.Items.Add(SiblingIDs); + boneNode.Items.Add(new BinInterpNode(bin.Position - 4, $"PosKey Header: {numKeys} keys, Compression: {keyFormat}, FormatFlags:{formatFlags:X}") { Length = 4 }); - for (int s = 0; s < sibCount; s++) //SIBLING SUBSTATE BOOLS + for (int j = 0; j < numKeys; j++) { - int nSibling = BitConverter.ToInt32(data, offset); - var nSiblings = new BinInterpNode + switch (keyFormat) { - Header = $"0x{offset:X5} Sibling: {s} Bool: {nSibling}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - SiblingIDs.Items.Add(nSiblings); - offset += 4; + case AnimationCompressionFormat.ACF_None: + case AnimationCompressionFormat.ACF_Float96NoW: + if ((formatFlags & 7) == 0) + { + boneNode.Items.Add(MakeVectorNode(bin, $"PosKey {j}")); + } + else + { + int binPosition = (int)bin.Position; + int keyLength = 4 * ((formatFlags & 1) + ((formatFlags >> 1) & 1) + ((formatFlags >> 2) & 1)); + float x = (formatFlags & 1) != 0 ? bin.ReadFloat() : 0, + y = (formatFlags & 2) != 0 ? bin.ReadFloat() : 0, + z = (formatFlags & 4) != 0 ? bin.ReadFloat() : 0; + boneNode.Items.Add(new BinInterpNode(binPosition, $"PosKey {j}: (X: {x}, Y: {y}, Z: {z})") + { + Length = keyLength + }); + } + break; + case AnimationCompressionFormat.ACF_Fixed48NoW: + case AnimationCompressionFormat.ACF_IntervalFixed32NoW: + case AnimationCompressionFormat.ACF_Fixed32NoW: + case AnimationCompressionFormat.ACF_Float32NoW: + case AnimationCompressionFormat.ACF_BioFixed48: + default: + throw new NotImplementedException($"{keyFormat} is not supported yet!"); + } } } - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioQuestMapScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - var game = CurrentLoadedExport.FileRef.Game; - try - { - int offset = binarystart; - - int qCount = BitConverter.ToInt32(data, offset); - var QuestNode = new BinInterpNode - { - Header = $"0x{offset:X4} Quest Count: {qCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(QuestNode); - - for (int i = 0; i < qCount; i++) //QUESTS - { - int iQuestID = BitConverter.ToInt32(data, offset); //QUEST ID - var QuestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Quest ID: {iQuestID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - QuestNode.Items.Add(QuestIDs); - - int instanceVersion = BitConverter.ToInt32(data, offset); //Unknown1 - QuestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} InstanceVersion: {instanceVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - int isMission = BitConverter.ToInt32(data, offset); //Unknown2 - bool isMissionB = isMission == 1; - QuestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} IsMission: {isMission} {isMissionB} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int gCount = BitConverter.ToInt32(data, offset); //Goal Count - var GoalsIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Goals: {gCount} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - QuestIDs.Items.Add(GoalsIDs); + int rotOff = TrackOffsets[i * 2 + 1]; - for (int g = 0; g < gCount; g++) //GOALS - { - //Add either state or Conditional as starting node - offset += 12; - int gConditional = BitConverter.ToInt32(data, offset); //Conditional - offset += 4; - int gState = BitConverter.ToInt32(data, offset); //State - offset -= 16; - int goalStart = gState; - string startType = "Bool"; - if (gState == -1) + if (rotOff >= 0) { - goalStart = gConditional; - startType = "Conditional"; - } - var nGoalIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Goal start plot/cnd: {goalStart} {startType}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - GoalsIDs.Items.Add(nGoalIDs); - - int iGoalInstVersion = BitConverter.ToInt32(data, offset); //Goal Instance Version - nGoalIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Goal Instance Version: {iGoalInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int gTitle = BitConverter.ToInt32(data, offset); //Goal Name - string gttlkLookup = GlobalFindStrRefbyID(gTitle, game, CurrentLoadedExport.FileRef); - nGoalIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Goal Name StrRef: {gTitle} {gttlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int gDescription = BitConverter.ToInt32(data, offset); //Goal Description - string gdtlkLookup = GlobalFindStrRefbyID(gDescription, game, CurrentLoadedExport.FileRef); - nGoalIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Goal Description StrRef: {gDescription} {gdtlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - gConditional = BitConverter.ToInt32(data, offset); //Conditional - nGoalIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Conditional: {gConditional} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - gState = BitConverter.ToInt32(data, offset); //State - nGoalIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Bool State: {gState} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - - int tCount = BitConverter.ToInt32(data, offset); //Task Count - var TaskIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Tasks Count: {tCount} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - QuestIDs.Items.Add(TaskIDs); - - for (int t = 0; t < tCount; t++) //TASKS - { - var nTaskIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Task: {t}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - TaskIDs.Items.Add(nTaskIDs); - - int iTaskInstVersion = BitConverter.ToInt32(data, offset); //Task Instance Version - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Task Instance Version: {iTaskInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int tFinish = BitConverter.ToInt32(data, offset); //Primary Codex - bool bFinish = tFinish == 1; - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Task Finishes Quest: {tFinish} {bFinish}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int tTitle = BitConverter.ToInt32(data, offset); //Task Name - string tttlkLookup = GlobalFindStrRefbyID(tTitle, game, CurrentLoadedExport.FileRef); - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Task Name StrRef: {tTitle} {tttlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int tDescription = BitConverter.ToInt32(data, offset); //Task Description - string tdtlkLookup = GlobalFindStrRefbyID(tDescription, game, CurrentLoadedExport.FileRef); - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Task Description StrRef: {tDescription} {tdtlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int piCount = BitConverter.ToInt32(data, offset); //Plot item Count - var PlotIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Plot Item Count: {piCount} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - nTaskIDs.Items.Add(PlotIDs); - - for (int pi = 0; pi < piCount; pi++) //TASK PLOT ITEMS - { - int iPlotItem = BitConverter.ToInt32(data, offset); //Plot item index - var nPlotItems = new BinInterpNode - { - Header = $"0x{offset:X5} Plot items: {pi} Index: {iPlotItem}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - PlotIDs.Items.Add(nPlotItems); - offset += 4; - } - - int planetName = BitConverter.ToInt32(data, offset); //Planet name - offset += 4; - int planetIdx = BitConverter.ToInt32(data, offset); //Name index - offset -= 4; - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Planet Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(planetName), planetIdx).Instanced} ", - Offset = offset, - Tag = NodeType.StructLeafName - }); - offset += 8; - - int wpStrLgth = BitConverter.ToInt32(data, offset); //String length for waypoint - offset += 4; - string wpRef = "No Waypoint data"; - if (wpStrLgth > 0) - { - //offset += 1; - MemoryStream ms = new MemoryStream(data); - ms.Position = offset; - wpRef = ms.ReadStringLatin1Null(wpStrLgth); - } - nTaskIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Waypoint ref: {wpRef} ", - Offset = offset, - Tag = NodeType.StructLeafStr - }); - offset += wpStrLgth; - } - - int pCount = BitConverter.ToInt32(data, offset); //Plot Item Count - var PlotItemIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Plot Items: {pCount} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - QuestIDs.Items.Add(PlotItemIDs); - - for (int p = 0; p < pCount; p++) //PLOT ITEM - { - //Add count starting node - var nPlotItemIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Plot Item: {p} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - PlotItemIDs.Items.Add(nPlotItemIDs); - - int iPlotInstVersion = BitConverter.ToInt32(data, offset); //Plot Item Instance Version - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Plot item Instance Version: {iPlotInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pTitle = BitConverter.ToInt32(data, offset); //Plot item Name - string pitlkLookup = GlobalFindStrRefbyID(pTitle, game, CurrentLoadedExport.FileRef); - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Goal Name StrRef: {pTitle} {pitlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int pIcon = BitConverter.ToInt32(data, offset); //Icon Index - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Icon Index: {pIcon} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pConditional = BitConverter.ToInt32(data, offset); //Conditional - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Conditional: {pConditional} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pState = BitConverter.ToInt32(data, offset); //Int - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Integer State: {pState} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pTarget = BitConverter.ToInt32(data, offset); //Target Index - nPlotItemIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Item Count Target: {pTarget} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - } - - int bsCount = BitConverter.ToInt32(data, offset); - var bsNode = new BinInterpNode - { - Header = $"0x{offset:X4} Bool Journal Events: {bsCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(bsNode); - - for (int b = 0; b < bsCount; b++) - { - int iBoolEvtID = BitConverter.ToInt32(data, offset); //BOOL STATE ID - var BoolEvtIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Bool Journal Event: {iBoolEvtID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - bsNode.Items.Add(BoolEvtIDs); - - int bsInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - var BoolQuestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {bsInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - BoolEvtIDs.Items.Add(BoolQuestIDs); - - int bqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count - var bqstNode = new BinInterpNode - { - Header = $"0x{offset:X4} Related Quests: {bqstCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - BoolQuestIDs.Items.Add(bqstNode); - - for (int bq = 0; bq < bqstCount; bq++) //Related Quests - { - offset += 16; - int bqQuest = BitConverter.ToInt32(data, offset); //Bool quest ID - var bquestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Related Quest: {bqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset -= 16; - bqstNode.Items.Add(bquestIDs); - - int bqInstVersion = BitConverter.ToInt32(data, offset); //Bool quest Instance Version - bquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {bqInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int bqTask = BitConverter.ToInt32(data, offset); //Bool quest Instance Version - bquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Related Task Link: {bqTask} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int bqConditional = BitConverter.ToInt32(data, offset); //Bool quest Conditional - bquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Conditional: {bqConditional} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int bqState = BitConverter.ToInt32(data, offset); //Bool quest State - bquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Bool State: {bqState} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - bqQuest = BitConverter.ToInt32(data, offset); //Bool quest ID - bquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Quest Link: {bqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - } - - int isCount = BitConverter.ToInt32(data, offset); - var isNode = new BinInterpNode - { - Header = $"0x{offset:X4} Int Journal Events: {isCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(isNode); - - for (int iEvt = 0; iEvt < isCount; iEvt++) //INTEGER STATE EVENTS - { - int iInttEvtID = BitConverter.ToInt32(data, offset); - var IntEvtIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Int Journal Event: {iInttEvtID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - isNode.Items.Add(IntEvtIDs); - - int isInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - var IntQuestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {isInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - IntEvtIDs.Items.Add(IntQuestIDs); - - int iqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count - var iqstNode = new BinInterpNode - { - Header = $"0x{offset:X4} Related Quests: {iqstCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - IntQuestIDs.Items.Add(iqstNode); - - for (int iq = 0; iq < iqstCount; iq++) //Related Quests - { - offset += 16; - int iqQuest = BitConverter.ToInt32(data, offset); //int quest ID - var iquestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Related Quest: {iqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset -= 16; - iqstNode.Items.Add(iquestIDs); - - int iqInstVersion = BitConverter.ToInt32(data, offset); //Int quest Instance Version - iquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {iqInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int iqTask = BitConverter.ToInt32(data, offset); //Int quest Instance Version - iquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Related Task Link: {iqTask} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int iqConditional = BitConverter.ToInt32(data, offset); //Int quest Conditional - iquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Conditional: {iqConditional} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int iqState = BitConverter.ToInt32(data, offset); //Int quest State - iquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Bool State: {iqState} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - iqQuest = BitConverter.ToInt32(data, offset); //Int quest ID - iquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Quest Link: {iqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - } - - int fsCount = BitConverter.ToInt32(data, offset); - var fsNode = new BinInterpNode - { - Header = $"0x{offset:X4} Float Journal Events: {fsCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(fsNode); - - for (int f = 0; f < fsCount; f++) //FLOAT STATE EVENTS - { - int iFloatEvtID = BitConverter.ToInt32(data, offset); //FLOAT STATE ID - var FloatEvtIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Float Journal Event: {iFloatEvtID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - fsNode.Items.Add(FloatEvtIDs); - - int fsInstVersion = BitConverter.ToInt32(data, offset); //Instance Version - var FloatQuestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {fsInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - FloatEvtIDs.Items.Add(FloatQuestIDs); - - int fqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count - var fqstNode = new BinInterpNode - { - Header = $"0x{offset:X4} Related Quests: {fqstCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - FloatQuestIDs.Items.Add(fqstNode); - - for (int fq = 0; fq < fqstCount; fq++) //Related Quests - { - offset += 16; - int fqQuest = BitConverter.ToInt32(data, offset); //float quest ID - var fquestIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Related Quest: {fqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset -= 16; - fqstNode.Items.Add(fquestIDs); - - int fqInstVersion = BitConverter.ToInt32(data, offset); //float quest Instance Version - fquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {fqInstVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int fqTask = BitConverter.ToInt32(data, offset); //Float quest Instance Version - fquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Related Task Link: {fqTask} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int fqConditional = BitConverter.ToInt32(data, offset); //Float quest Conditional - fquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Conditional: {fqConditional} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int fqState = BitConverter.ToInt32(data, offset); //Float quest State - fquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Bool State: {fqState} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - fqQuest = BitConverter.ToInt32(data, offset); //Float quest ID - fquestIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Quest Link: {fqQuest} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioCodexMapScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - var game = CurrentLoadedExport.FileRef.Game; - try - { - int offset = binarystart; - - int sCount = BitConverter.ToInt32(data, offset); - var SectionsNode = new BinInterpNode - { - Header = $"0x{offset:X4} Codex Section Count: {sCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(SectionsNode); - - for (int i = 0; i < sCount; i++) - { - int iSectionID = BitConverter.ToInt32(data, offset); //Section ID - var SectionIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Section ID: {iSectionID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - SectionsNode.Items.Add(SectionIDs); - - int instVersion = BitConverter.ToInt32(data, offset); //Instance Version - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {instVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int sTitle = BitConverter.ToInt32(data, offset); //Codex Title - string ttlkLookup = GlobalFindStrRefbyID(sTitle, game, CurrentLoadedExport.FileRef); - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Title StrRef: {sTitle} {ttlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int sDescription = BitConverter.ToInt32(data, offset); //Codex Description - string dtlkLookup = GlobalFindStrRefbyID(sDescription, game, CurrentLoadedExport.FileRef); - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Description StrRef: {sDescription} {dtlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int sTexture = BitConverter.ToInt32(data, offset); //Texture ID - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Texture ID: {sTexture} ", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int sPriority = BitConverter.ToInt32(data, offset); //Priority - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Priority: {sPriority} (5 is low, 1 is high)", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - if (instVersion >= 3) - { - int sndExport = BitConverter.ToInt32(data, offset); - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X8} Codex Sound: {sndExport} {CurrentLoadedExport.FileRef.GetEntryString(sndExport)}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - } - - int sPrimary = BitConverter.ToInt32(data, offset); //Primary Codex - bool bPrimary = false; - if (sPrimary == 1) { bPrimary = true; } - SectionIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Is Primary Codex: {sPrimary} {bPrimary}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - } - //START OF CODEX PAGES SECTION - int pCount = BitConverter.ToInt32(data, offset); - var PagesNode = new BinInterpNode - { - Header = $"0x{offset:X4} Codex Page Count: {pCount}", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - subnodes.Add(PagesNode); - - for (int i = 0; i < pCount; i++) - { - int iPageID = BitConverter.ToInt32(data, offset); //Page ID - var PageIDs = new BinInterpNode - { - Header = $"0x{offset:X5} Page Bool: {iPageID} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }; - offset += 4; - PagesNode.Items.Add(PageIDs); - - int instVersion = BitConverter.ToInt32(data, offset); //Instance Version - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Instance Version: {instVersion} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pTitle = BitConverter.ToInt32(data, offset); //Codex Title - string ttlkLookup = GlobalFindStrRefbyID(pTitle, game, CurrentLoadedExport.FileRef); - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Page Title StrRef: {pTitle} {ttlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pDescription = BitConverter.ToInt32(data, offset); //Codex Description - string dtlkLookup = GlobalFindStrRefbyID(pDescription, game, CurrentLoadedExport.FileRef); - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Page Description StrRef: {pDescription} {dtlkLookup}", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pTexture = BitConverter.ToInt32(data, offset); //Texture ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Texture ID: {pTexture} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pPriority = BitConverter.ToInt32(data, offset); //Priority - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Priority: {pPriority} (5 is low, 1 is high)", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - if (instVersion == 4) //ME3 use object reference found sound then section - { - int sndExport = BitConverter.ToInt32(data, offset); - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X8} Codex Sound: {sndExport} {CurrentLoadedExport.FileRef.GetEntryString(sndExport)}", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - - int pSection = BitConverter.ToInt32(data, offset); //Section ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Reference: {pSection} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (instVersion == 3 && Pcc.Game != MEGame.LE1) //ME2 use Section then no sound reference //LE1 uses something else... - { - int pSection = BitConverter.ToInt32(data, offset); //Section ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Reference: {pSection} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - } - else if (instVersion == 3 && Pcc.Game == MEGame.LE1) - { - int unkSection = BitConverter.ToInt32(data, offset); //Unknown ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Unknown Int: {unkSection} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int pSection = BitConverter.ToInt32(data, offset); //Section ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Reference: {pSection} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int sndStrLgth = BitConverter.ToInt32(data, offset); //String length for sound - string sndRef = "No sound data"; - if (sndStrLgth > 0) - { - MemoryStream ms = new MemoryStream(data); - ms.Position = offset + 4; - sndRef = ms.ReadStringLatin1Null(sndStrLgth); - } - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} SoundRef String: {sndRef} ", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += 4; - offset += sndStrLgth; - } - else //ME1 has different order (section ID then codex sound) and uses a string reference. - { - int pSection = BitConverter.ToInt32(data, offset); //Section ID - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} Section Reference: {pSection} ", - Offset = offset, - Tag = NodeType.StructLeafInt - }); - offset += 4; - - int sndStrLgth = BitConverter.ToInt32(data, offset); //String length for sound - offset += 4; - string sndRef = "No sound data"; - if (sndStrLgth > 0) - { - MemoryStream ms = new MemoryStream(data); - ms.Position = offset; - sndRef = ms.ReadStringLatin1Null(sndStrLgth); - } - PageIDs.Items.Add(new BinInterpNode - { - Header = $"0x{offset:X5} SoundRef String: {sndRef} ", - Offset = offset, - Tag = NodeType.StructLeafObject - }); - offset += sndStrLgth; - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartAnimSequenceScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - - #region UDK AKF_PerTrackCompression - - if (Pcc.Game == MEGame.UDK && CurrentLoadedExport.GetProperty("KeyEncodingFormat")?.Value.Name == "AKF_PerTrackCompression") - { - try - { - var TrackOffsets = CurrentLoadedExport.GetProperty>("CompressedTrackOffsets"); - var numFrames = CurrentLoadedExport.GetProperty("NumFrames")?.Value ?? 0; - - List boneList = ((ExportEntry)CurrentLoadedExport.Parent).GetProperty>("TrackBoneNames").Select(np => $"{np}").ToList(); - - var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; - bin.JumpTo(binarystart); - - int numTracks = bin.ReadInt32() * 2; - bin.Skip(-4); - - BinInterpNode rawAnimDataNode = MakeInt32Node(bin, "RawAnimationData: NumTracks"); - subnodes.Add(rawAnimDataNode); - for (int i = 0; i < numTracks; i++) - { - int keySize = bin.ReadInt32(); - int numKeys = bin.ReadInt32(); - for (int j = 0; j < numKeys; j++) - { - if (keySize == 12) - { - rawAnimDataNode.Items.Add(MakeVectorNode(bin, $"{boneList[i / 2]}, PosKey {j}")); - } - else if (keySize == 16) - { - rawAnimDataNode.Items.Add(MakeQuatNode(bin, $"{boneList[i / 2]}, RotKey {j}")); - } - else - { - throw new NotImplementedException($"Unexpected key size: {keySize}"); - } - } - } - - subnodes.Add(MakeInt32Node(bin, "AnimBinary length")); - var startOff = bin.Position; - for (int i = 0; i < boneList.Count; i++) - { - var boneNode = new BinInterpNode(bin.Position, boneList[i]); - subnodes.Add(boneNode); - - int posOff = TrackOffsets[i * 2]; - - if (posOff >= 0) - { - bin.JumpTo(startOff + posOff); - int header = bin.ReadInt32(); - int numKeys = header & 0x00FFFFFF; - int formatFlags = (header >> 24) & 0x0F; - AnimationCompressionFormat keyFormat = (AnimationCompressionFormat)((header >> 28) & 0x0F); - - boneNode.Items.Add(new BinInterpNode(bin.Position - 4, $"PosKey Header: {numKeys} keys, Compression: {keyFormat}, FormatFlags:{formatFlags:X}") { Length = 4 }); - - for (int j = 0; j < numKeys; j++) - { - switch (keyFormat) - { - case AnimationCompressionFormat.ACF_None: - case AnimationCompressionFormat.ACF_Float96NoW: - if ((formatFlags & 7) == 0) - { - boneNode.Items.Add(MakeVectorNode(bin, $"PosKey {j}")); - } - else - { - int binPosition = (int)bin.Position; - int keyLength = 4 * ((formatFlags & 1) + ((formatFlags >> 1) & 1) + ((formatFlags >> 2) & 1)); - float x = (formatFlags & 1) != 0 ? bin.ReadFloat() : 0, - y = (formatFlags & 2) != 0 ? bin.ReadFloat() : 0, - z = (formatFlags & 4) != 0 ? bin.ReadFloat() : 0; - boneNode.Items.Add(new BinInterpNode(binPosition, $"PosKey {j}: (X: {x}, Y: {y}, Z: {z})") - { - Length = keyLength - }); - } - break; - case AnimationCompressionFormat.ACF_Fixed48NoW: - case AnimationCompressionFormat.ACF_IntervalFixed32NoW: - case AnimationCompressionFormat.ACF_Fixed32NoW: - case AnimationCompressionFormat.ACF_Float32NoW: - case AnimationCompressionFormat.ACF_BioFixed48: - default: - throw new NotImplementedException($"{keyFormat} is not supported yet!"); - } - } - } - - int rotOff = TrackOffsets[i * 2 + 1]; - - if (rotOff >= 0) - { - bin.JumpTo(startOff + rotOff); - int header = bin.ReadInt32(); - int numKeys = header & 0x00FFFFFF; - int formatFlags = (header >> 24) & 0x0F; - AnimationCompressionFormat keyFormat = (AnimationCompressionFormat)((header >> 28) & 0x0F); - - boneNode.Items.Add(new BinInterpNode(bin.Position - 4, $"RotKey Header: {numKeys} keys, Compression: {keyFormat}, FormatFlags:{formatFlags:X}") { Length = 4 }); - switch (keyFormat) - { - case AnimationCompressionFormat.ACF_None: - { - for (int j = 0; j < numKeys; j++) - { - boneNode.Items.Add(MakeQuatNode(bin, $"RotKey {j}")); - } - break; - } - case AnimationCompressionFormat.ACF_Fixed48NoW: - { - const float scale = 32767.0f; - const ushort unkConst = 32767; - int keyLength = 2 * ((formatFlags & 1) + ((formatFlags >> 1) & 1) + ((formatFlags >> 2) & 1)); - for (int j = 0; j < numKeys; j++) - { - int binPosition = (int)bin.Position; - float x = (formatFlags & 1) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0, - y = (formatFlags & 2) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0, - z = (formatFlags & 4) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0; - boneNode.Items.Add(new BinInterpNode(binPosition, $"RotKey {j}: (X: {x}, Y: {y}, Z: {z}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") - { - Length = keyLength - }); - } - break; - } - case AnimationCompressionFormat.ACF_Float96NoW: - { - float x, y, z; - for (int j = 0; j < numKeys; j++) - { - boneNode.Items.Add(new BinInterpNode(bin.Position, $"RotKey {j}: (X: {x = bin.ReadFloat()}, Y: {y = bin.ReadFloat()}, Z: {z = bin.ReadFloat()}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") - { - Length = 12 - }); - } - break; - } - case AnimationCompressionFormat.ACF_IntervalFixed32NoW: - case AnimationCompressionFormat.ACF_Fixed32NoW: - case AnimationCompressionFormat.ACF_Float32NoW: - case AnimationCompressionFormat.ACF_BioFixed48: - default: - throw new NotImplementedException($"{keyFormat} is not supported yet!"); - } - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - #endregion - - try - { - var TrackOffsets = CurrentLoadedExport.GetProperty>("CompressedTrackOffsets"); - var numFrames = CurrentLoadedExport.GetProperty("NumFrames")?.Value ?? 0; - - List boneList; - if (Pcc.Game == MEGame.UDK) - { - boneList = ((ExportEntry)CurrentLoadedExport.Parent)?.GetProperty>("TrackBoneNames")?.Select(np => $"{np}").ToList(); - } - else - { - var animsetData = CurrentLoadedExport.GetProperty("m_pBioAnimSetData"); - //In ME2, BioAnimSetData can sometimes be in a different package. - boneList = animsetData != null && Pcc.IsUExport(animsetData.Value) - ? Pcc.GetUExport(animsetData.Value).GetProperty>("TrackBoneNames")?.Select(np => $"{np}").ToList() - : null; - } - - boneList ??= Enumerable.Repeat("???", TrackOffsets.Count / 4).ToList(); - Enum.TryParse(CurrentLoadedExport.GetProperty("KeyEncodingFormat")?.Value.Name, out AnimationKeyFormat keyEncoding); - Enum.TryParse(CurrentLoadedExport.GetProperty("RotationCompressionFormat")?.Value.Name, out AnimationCompressionFormat rotCompression); - Enum.TryParse(CurrentLoadedExport.GetProperty("TranslationCompressionFormat")?.Value.Name, out AnimationCompressionFormat posCompression); - - var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; - bin.JumpTo(binarystart); - if (Pcc.Game is MEGame.ME2 or MEGame.LE2 && Pcc.Platform != MEPackage.GamePlatform.PS3) - { - bin.Skip(12); - subnodes.Add(MakeInt32Node(bin, "AnimBinary Offset")); - } - else if (Pcc.Game == MEGame.UDK) - { - int numTracks = bin.ReadInt32() * 2; - bin.Skip(-4); - - BinInterpNode rawAnimDataNode = MakeInt32Node(bin, "RawAnimationData: NumTracks"); - subnodes.Add(rawAnimDataNode); - for (int i = 0; i < numTracks; i++) - { - int keySize = bin.ReadInt32(); - int numKeys = bin.ReadInt32(); - for (int j = 0; j < numKeys; j++) - { - if (keySize == 12) - { - rawAnimDataNode.Items.Add(MakeVectorNode(bin, $"{boneList[i / 2]}, PosKey {j}")); - } - else if (keySize == 16) - { - rawAnimDataNode.Items.Add(MakeQuatNode(bin, $"{boneList[i / 2]}, RotKey {j}")); - } - else - { - throw new NotImplementedException($"Unexpected key size: {keySize}"); - } - } - } - } - - subnodes.Add(MakeInt32Node(bin, "AnimBinary length")); - var startOffset = bin.Position; - for (int i = 0; i < boneList.Count; i++) - { - var boneNode = new BinInterpNode(bin.Position, boneList[i]); - subnodes.Add(boneNode); - - int posOff = TrackOffsets[i * 4]; - int posKeys = TrackOffsets[i * 4 + 1]; - int rotOff = TrackOffsets[i * 4 + 2]; - int rotKeys = TrackOffsets[i * 4 + 3]; - - if (posKeys > 0) - { - bin.JumpTo(startOffset + posOff); - - AnimationCompressionFormat compressionFormat = posCompression; - - if (posKeys == 1) - { - compressionFormat = AnimationCompressionFormat.ACF_None; - } - for (int j = 0; j < posKeys; j++) - { - BinInterpNode posKeyNode; - switch (compressionFormat) - { - case AnimationCompressionFormat.ACF_None: - case AnimationCompressionFormat.ACF_Float96NoW: - posKeyNode = MakeVectorNode(bin, $"PosKey {j}"); - break; - case AnimationCompressionFormat.ACF_IntervalFixed32NoW: - case AnimationCompressionFormat.ACF_Fixed48NoW: - case AnimationCompressionFormat.ACF_Fixed32NoW: - case AnimationCompressionFormat.ACF_Float32NoW: - case AnimationCompressionFormat.ACF_BioFixed48: - default: - throw new NotImplementedException($"Translation keys in format {compressionFormat} cannot be read!"); - } - boneNode.Items.Add(posKeyNode); - } - - readTrackTable(posKeys); - } - - if (rotKeys > 0) - { - bin.JumpTo(startOffset + rotOff); - - AnimationCompressionFormat compressionFormat = rotCompression; - - if (rotKeys == 1) - { - compressionFormat = AnimationCompressionFormat.ACF_Float96NoW; - } - else if (Pcc.Game != MEGame.UDK) - { - boneNode.Items.Add(MakeVectorNode(bin, "Mins")); - boneNode.Items.Add(MakeVectorNode(bin, "Ranges")); - } - - for (int j = 0; j < rotKeys; j++) - { - BinInterpNode rotKeyNode; - switch (compressionFormat) - { - case AnimationCompressionFormat.ACF_None: - rotKeyNode = MakeQuatNode(bin, $"RotKey {j}"); - break; - case AnimationCompressionFormat.ACF_Float96NoW: - { - float x, y, z; - rotKeyNode = new BinInterpNode(bin.Position, $"RotKey {j}: (X: {x = bin.ReadFloat()}, Y: {y = bin.ReadFloat()}, Z: {z = bin.ReadFloat()}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") - { - Length = 12 - }; - break; - } - case AnimationCompressionFormat.ACF_BioFixed48: - { - var pos = bin.Position; - ushort a = bin.ReadUInt16(); - ushort b = bin.ReadUInt16(); - ushort c = bin.ReadUInt16(); - var rot = AnimSequence.DecompressBioFixed48(a, b, c); - rotKeyNode = new BinInterpNode(pos, $"RotKey {j}: (X: {rot.X}, Y: {rot.Y}, Z: {rot.Z}, W: {rot.W})") - { - Length = 6 - }; - break; - } - case AnimationCompressionFormat.ACF_Fixed48NoW: - { - var pos = bin.Position; - var a = bin.ReadUInt16(); - var b = bin.ReadUInt16(); - var c = bin.ReadUInt16(); - var rot = AnimSequence.DecompressFixed48NoW(a, b, c); - rotKeyNode = new BinInterpNode(pos, $"RotKey {j}: (X: {rot.X}, Y: {rot.Y}, Z: {rot.Z}, W: {rot.W})") - { - Length = 6 - }; - break; - } - case AnimationCompressionFormat.ACF_IntervalFixed32NoW: - case AnimationCompressionFormat.ACF_Fixed32NoW: - case AnimationCompressionFormat.ACF_Float32NoW: - default: - throw new NotImplementedException($"Rotation keys in format {compressionFormat} cannot be read!"); - } - boneNode.Items.Add(rotKeyNode); - } - - readTrackTable(rotKeys); - } - - void readTrackTable(int numKeys) - { - if (keyEncoding == AnimationKeyFormat.AKF_VariableKeyLerp && numKeys > 1) - { - bin.JumpTo((bin.Position - startOffset).Align(4) + startOffset); - var trackTable = new BinInterpNode(bin.Position, "TrackTable"); - boneNode.Items.Add(trackTable); - - for (int j = 0; j < numKeys; j++) - { - trackTable.Items.Add(numFrames > 0xFF ? MakeUInt16Node(bin, $"{j}") : MakeByteNode(bin, $"{j}")); - } - } - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - /// - /// Reads the common header for FaceFX archives. - /// - private (int version, MEGame game) ReadFaceFXHeader(EndianReader bin, List subnodes) - { - var archiveSize = bin.ReadInt32(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Archive size: {archiveSize} ({FileSize.FormatSize(archiveSize)})")); - subnodes.Add(new BinInterpNode(bin.Position, $"Magic: {bin.ReadInt32():X8}") { Length = 4 }); - int versionID = bin.ReadInt32(); //1710 = ME1, 1610 = ME2, 1731 = ME3. - var game = versionID == 1710 ? MEGame.ME1 : - versionID == 1610 ? MEGame.ME2 : - versionID == 1731 ? MEGame.ME3 : - MEGame.Unknown; - var vIdStr = versionID.ToString(); - var vers = new Version(vIdStr[0] - '0', vIdStr[1] - '0', vIdStr[2] - '0', vIdStr[3] - '0'); //Mega hack - subnodes.Add(new BinInterpNode(bin.Position - 4, $"SDK Version: {versionID} ({vers})") { Length = 4 }); - if (versionID == 1731) - { - subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); - } - - subnodes.Add(new BinInterpNode(bin.Position, $"Licensee: {bin.ReadFaceFXString(game)}")); - subnodes.Add(new BinInterpNode(bin.Position, $"Project: {bin.ReadFaceFXString(game)}")); - - var licenseeVersion = bin.ReadInt32(); - vIdStr = licenseeVersion.ToString(); - vers = new Version(vIdStr[0] - '0', vIdStr[1] - '0', vIdStr[2] - '0', vIdStr[3] - '0'); //Mega hack - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Licensee version: {vIdStr} ({vers})") { Length = 4 }); - - return (versionID, game); - } - - private List StartFaceFXAnimSetScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - ReadFaceFXHeader(bin, subnodes); - - if (Pcc.Game == MEGame.ME2) - { - subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); - } - else - { - subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - - if (Pcc.Game != MEGame.ME2) - { - int hNodeCount = bin.ReadInt32(); - var hNodes = new List(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Nodes: {hNodeCount} items") - { - Items = hNodes - }); - for (int i = 0; i < hNodeCount; i++) - { - var hNodeNodes = new List(); - hNodes.Add(new BinInterpNode(bin.Position, $"{i}") - { - Items = hNodeNodes - }); - hNodeNodes.Add(MakeInt32Node(bin, "Unknown")); - var nNameCount = bin.ReadInt32(); - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name Count: {nNameCount}") { Length = 4 }); - for (int n = 0; n < nNameCount; n++) - { - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - } - } - - int nameCount = bin.ReadInt32(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Names: {nameCount} items") - { - //ME2 different to ME3/1 - Items = ReadList(nameCount, i => new BinInterpNode(bin.Skip(Pcc.Game != MEGame.ME2 ? 0 : 4).Position, $"{i}: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")) - }); - - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - if (Pcc.Game == MEGame.ME2) - { - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - } - - int lineCount = bin.ReadInt32(); - var lines = new List(); - - subnodes.Add(new BinInterpNode(bin.Position - 4, $"FaceFXLines: {lineCount} items") - { - Items = lines - }); - for (int i = 0; i < lineCount; i++) - { - var nodes = new List(); - lines.Add(new BinInterpNode(bin.Position, $"{i}") - { - Items = nodes - }); - if (Pcc.Game == MEGame.ME2) - { - nodes.Add(MakeInt32Node(bin, "Unknown")); - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - nodes.Add(MakeInt32Node(bin, "Name")); - if (Pcc.Game == MEGame.ME2) - { - nodes.Add(MakeInt32Node(bin, "Unknown")); - } - int animationCount = bin.ReadInt32(); - var anims = new List(); - nodes.Add(new BinInterpNode(bin.Position - 4, $"Animations: {animationCount} items") - { - Items = anims - }); - for (int j = 0; j < animationCount; j++) - { - var animNodes = new List(); - anims.Add(new BinInterpNode(bin.Position, $"{j}") - { - Items = animNodes - }); - if (Pcc.Game == MEGame.ME2) - { - animNodes.Add(MakeInt32Node(bin, "Unknown")); - animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - animNodes.Add(MakeInt32Node(bin, "Name")); - animNodes.Add(MakeInt32Node(bin, "Unknown")); - if (Pcc.Game == MEGame.ME2) - { - animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - } - - int pointsCount = bin.ReadInt32(); - nodes.Add(new BinInterpNode(bin.Position - 4, $"Points: {pointsCount} items") - { - Items = ReadList(pointsCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = new List - { - new BinInterpNode(bin.Position, $"Time: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"Weight: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"InTangent: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"LeaveTangent: {bin.ReadFloat()}") {Length = 4} - } - }) - }); - - if (pointsCount > 0) - { - if (Pcc.Game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - nodes.Add(new BinInterpNode(bin.Position, $"NumKeys: {bin.ReadInt32()} items") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{bin.ReadInt32()} keys")) - }); - } - nodes.Add(new BinInterpNode(bin.Position, $"Fade In Time: {bin.ReadFloat()}") { Length = 4 }); - nodes.Add(new BinInterpNode(bin.Position, $"Fade Out Time: {bin.ReadFloat()}") { Length = 4 }); - nodes.Add(MakeInt32Node(bin, "Unknown")); - if (Pcc.Game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - nodes.Add(new BinInterpNode(bin.Position, $"Path: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - if (Pcc.Game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - nodes.Add(new BinInterpNode(bin.Position, $"ID: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - nodes.Add(MakeInt32Node(bin, "index")); - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartFaceFXAssetScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - var (version, game) = ReadFaceFXHeader(bin, subnodes); - - if (game == MEGame.ME2) - { - subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); - } - else - { - subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - //Node Table - if (game != MEGame.ME2) - { - int hNodeCount = bin.ReadInt32(); - var hNodes = new List(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Nodes: {hNodeCount} items") - { - Items = hNodes - }); - for (int i = 0; i < hNodeCount; i++) - { - var hNodeNodes = new List(); - hNodes.Add(new BinInterpNode(bin.Position, $"{i}") - { - Items = hNodeNodes - }); - hNodeNodes.Add(MakeInt32Node(bin, "Unknown")); - var nNameCount = bin.ReadInt32(); - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name Count: {nNameCount}") { Length = 4 }); - for (int n = 0; n < nNameCount; n++) - { - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - hNodeNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - } - } - - //Name Table - - var nameTable = new List(); - int nameCount = bin.ReadInt32(); - var nametablePos = bin.Position - 4; - var nametabObj = new List(); - - // Does this need byte aligned? ME2 seems like it does... - //if (game == MEGame.ME2) - //{ - // bin.ReadByte(); // Align to bytes? - //} - - for (int m = 0; m < nameCount; m++) - { - var pos = bin.Position; - var mName = bin.ReadFaceFXString(game, true); - nameTable.Add(mName); - nametabObj.Add(new BinInterpNode(pos, $"{m}: {mName}")); - //if (game != MEGame.ME2) - //{ - // bin.Skip(4); - //} - } - - subnodes.Add(new BinInterpNode(nametablePos, $"Names: {nameCount} items") - { - //ME1 and ME3 same, ME2 different - Items = nametabObj - }); - - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - - if (game == MEGame.ME2) - { - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt16Node(bin, "Unknown")); - subnodes.Add(MakeInt16Node(bin, "Unknown")); - subnodes.Add(MakeInt16Node(bin, "Unknown")); - } - - //LIST A - BONES - var bonesList = new List(); - var bonesCount = bin.ReadInt32(); - if (game == MEGame.ME2) - { - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt16Node(bin, "Unknown")); - } - subnodes.Add(new BinInterpNode(((game == MEGame.ME2) ? (bin.Position - 10) : (bin.Position - 4)), $"Bone Nodes: {bonesCount} items") - { - Items = bonesList - }); - - for (int a = 0; a < bonesCount; a++) //NOT EXACT?? - { - var boneNode = new List(); - bonesList.Add(new BinInterpNode(bin.Position, $"{nameTable[bin.ReadInt32()]}") - { - Items = boneNode - }); - boneNode.Add(MakeFloatNode(bin, "X")); - boneNode.Add(MakeFloatNode(bin, "Y")); - boneNode.Add(MakeFloatNode(bin, "Z")); - - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - if (game != MEGame.ME2) - { - boneNode.Add(MakeFloatNode(bin, "Unknown float")); - boneNode.Add(MakeFloatNode(bin, "Bone weight?")); - //while (true) - //{ - // var item = bin.ReadInt32(); - // if (item == 2147483647) - // { - // tableItems.Add(new BinInterpNode(bin.Position - 4, $"End Marker: FF FF FF 7F") { Length = 4 }); - // tableItems.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - // break; - // } - - // bin.Skip(-4); - // tableItems.Add(MakeFloatNode(bin, "Unknown float")); - - //} - //Name list to Bones and other facefx? - var unkNameList1 = new List(); - var countUk1 = bin.ReadInt32(); - boneNode.Add(new BinInterpNode(bin.Position - 4, $"Functions?: {countUk1} items") - { - Items = unkNameList1 - }); - for (int b = 0; b < countUk1; b++) - { - var unameVal = bin.ReadInt32(); - var unkNameList1items = new List(); - unkNameList1.Add(new BinInterpNode(bin.Position - 4, $"{b},{unameVal}") - { - Items = unkNameList1items - }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"{nameTable[bin.ReadInt32()]}")); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); - unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); - unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); - } - } - } - - //LIST B - COMBINER NODES - //FROM HERE ME3 ONLY WIP - - //I have literally no idea how this works in ME2 - - var combinerList = new List(); - var combinerListNames = new List(); - var combinerCount = bin.ReadInt32(); - - if (game == MEGame.ME2) - { - subnodes.Add(MakeInt32Node(bin, "Unknown")); - } - - subnodes.Add(new BinInterpNode(bin.Position - (game == MEGame.ME2 ? 8 : 4), $"Combiner nodes: {combinerCount} items") - { - Items = combinerList - }); - - for (int b = 0, i = 0; i < combinerCount; b++, i++) - { - var bLocation = bin.Position; - // There seem to be several types, known types are 0, 4, 6, 8. - int formatType; - if (game == MEGame.ME2) - { - formatType = bin.ReadInt16(); - } - else formatType = bin.ReadInt32(); - - var nameIdx = bin.ReadInt32(); - - var combinerNode = new List(); - combinerList.Add(new BinInterpNode(bin.Position - 4, $"{b}: {nameTable[nameIdx]} - {(FxNodeType)formatType}") - { - Items = combinerNode - }); - combinerListNames.Add(nameTable[nameIdx]); - - combinerNode.Add(new BinInterpNode(bin.Position - 8, $"Format: {formatType} - {(FxNodeType)formatType}")); - combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Table index: {nameIdx}")); - combinerNode.Add(new BinInterpNode(bin.Position, $"Minimum Value: {bin.ReadSingle()}") { Length = 4 }); - combinerNode.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - combinerNode.Add(new BinInterpNode(bin.Position, $"Maximum Value: {bin.ReadSingle()}") { Length = 4 }); - combinerNode.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); - var inputOp = bin.ReadInt32(); - combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Input Operation: {inputOp} - {(FxInputOperation)inputOp}")); - - // Parent links section - var parentLinks = new List(); //Name list to Bones and other facefx phenomes? - var parentLinksCount = bin.ReadInt32(); - combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Parent Links: {parentLinksCount} items") - { - Items = parentLinks - }); - for (int n2 = 0; n2 < parentLinksCount; n2++) - { - var combinerIdx = bin.ReadInt32(); - var linkedNode = combinerIdx < b ? combinerListNames[combinerIdx] : ""; - var parentLinkItems = new List(); - parentLinks.Add(new BinInterpNode(bin.Position - 4, $"Combiner Idx: {combinerIdx} {linkedNode}") - { - Items = parentLinkItems - }); - var linkFunction = bin.ReadInt32(); - parentLinkItems.Add(new BinInterpNode(bin.Position - 4, $"Link Function: {(FxLinkFunction)linkFunction}")); - var n3count = bin.ReadInt32(); - parentLinkItems.Add(new BinInterpNode(bin.Position - 4, $"Parameter Count: {n3count}")); - for (int n3 = 0; n3 < n3count; n3++) - { - parentLinkItems.Add(new BinInterpNode(bin.Position, $"Function Parameter {n3}: {bin.ReadSingle()}") { Length = 4 }); - } - } - - // Parameters section - int parameterCount = bin.ReadInt32(); - var fxaParameter = new List(parameterCount); - combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Parameters: {parameterCount} items") - { - Items = fxaParameter - }); - for (int fxaIndex = 0; fxaIndex < parameterCount; fxaIndex++) - { - int fxaIdxVal = bin.ReadInt32(); - var fxaInfoItem = new List(); - fxaParameter.Add(new BinInterpNode(bin.Position - 4, $"{nameTable[fxaIdxVal]} - {fxaIdxVal}") - { - Items = fxaInfoItem - }); - int parameterFmt = bin.ReadInt32(); - fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Name: {nameTable[fxaIdxVal]} ({fxaIdxVal})")); - fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Format: {(FxNodeParamFormat)parameterFmt} ({parameterFmt})") { Length = 4 }); - // Parameter format - 0 means first int is the param value, 3 means there is a string on the end that is the param value - - var firstUnkIntName = parameterFmt == 0 ? "Int Value" : "Unknown int"; - fxaInfoItem.Add(new BinInterpNode(bin.Position, $"{firstUnkIntName}: {bin.ReadInt32()}") { Length = 4 }); - fxaInfoItem.Add(new BinInterpNode(bin.Position, $"Float value?: {bin.ReadSingle()}") { Length = 4 }); - fxaInfoItem.Add(new BinInterpNode(bin.Position, $"Unknown int: {bin.ReadInt32()}") { Length = 4 }); - - if (parameterFmt == 3) - { - var unkStringLength = bin.ReadInt32(); - fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Value: {bin.BaseStream.ReadStringLatin1(unkStringLength)}")); - } - } - } - - // Fix names for bone node functions now that we've parsed combiner table - this is terrible code - foreach (var bone in bonesList) - { - var functions = (bone as BinInterpNode).Items[^1]; - if (functions is BinInterpNode functionNode && functionNode.Header.Contains("Function")) - { - foreach (var funcItem in functionNode.Items) - { - if (funcItem is BinInterpNode func) - { - var ints = func.Header.Split(',', ' ').Where(str => Int32.TryParse(str, out _)).Select(str => Convert.ToInt32(str)).ToArray(); - if (ints.Length != 2) break; - func.Header = $"{ints[0]}: Combiner Node {ints[1]} ({combinerListNames[ints[1]]})"; - } - } - } - } - - // Unknown Table C First 4 bytes - // Theory 1: This could refer to a number of "unique" entries, it seems to be a number of entries in the table that have only 1 string reference to the combiner. - // Subtracting: (entries in this table that have 2 or more strings) - (total amount of combiner entires) = seems to result in same number that exists in these first 4 bytes. - byte[] unkHeaderC = bin.ReadBytes(4); - var unkListC = new List(); - subnodes.Add(new BinInterpNode(bin.Position - 4, $"Unknown Table C - Combiner Mapping?") - { - Items = unkListC - }); - - for (int c = 0; c < combinerCount; c++) - { - // Table begins with an unknown ID - this ID seems to be from some kind of global table as same entries with same names use same IDs across different FaceFX files - // (not 100% sure as only smallish sample of about 20 files was checked) - int entryID = bin.ReadInt32(); - // Add tree item with entry ID as an idicator - var unkListCitems = new List(); - unkListC.Add(new BinInterpNode(bin.Position - 4, $"{c}: {entryID}") - { - Items = unkListCitems - }); - unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Unknown int: {entryID}") { Length = 4 }); - // String count: - int stringCount = bin.ReadInt32(); - unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"String count: {stringCount}") { Length = 4 }); - // Combiner entry name: - int stringLength = bin.ReadInt32(); - string stringText = bin.ReadEndianASCIIString(stringLength); - unkListCitems.Add(new BinInterpNode(bin.Position - stringLength - 4, $"Combiner String: {stringText}") { Length = stringLength + 4 }); - // Combiner entry ID: - int name = bin.ReadInt32(); - unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Combiner ID: {name} {combinerListNames[name]}") { Length = 4 }); - // Do the same for next strings if theres more than one. - for (int i = 1; i < stringCount; i++) - { - c++; - stringLength = bin.ReadInt32(); - stringText = bin.ReadEndianASCIIString(stringLength); - unkListCitems.Add(new BinInterpNode(bin.Position - stringLength - 4, $"Combiner String: {stringText}") { Length = stringLength + 4 }); - name = bin.ReadInt32(); - unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Combiner ID: {name} {combinerListNames[name]}") { Length = 4 }); - } - } - - if (game == MEGame.ME2) - { - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - } - - subnodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); - - int lineCount; - var lines = new List(); - subnodes.Add(new BinInterpNode(bin.Position, $"FaceFXLines: {lineCount = bin.ReadInt32()}") - { - Items = lines - }); - for (int i = 0; i < lineCount; i++) - { - var nodes = new List(); - lines.Add(new BinInterpNode(bin.Position, $"{i}") - { - Items = nodes - }); - nodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); - int animationCount = bin.ReadInt32(); - var anims = new List(); - nodes.Add(new BinInterpNode(bin.Position - 4, $"Animations: {animationCount} items") - { - Items = anims - }); - for (int j = 0; j < animationCount; j++) - { - var animNodes = new List(); - anims.Add(new BinInterpNode(bin.Position, $"{j}") - { - Items = animNodes - }); - animNodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); - animNodes.Add(MakeInt32Node(bin, "Unknown")); - if (game == MEGame.ME2) - { - animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - } - int pointsCount = bin.ReadInt32(); - nodes.Add(new BinInterpNode(bin.Position - 4, $"Points: {pointsCount} items") - { - Items = ReadList(pointsCount, j => new BinInterpNode(bin.Position, $"{j}") - { - Items = new List - { - new BinInterpNode(bin.Position, $"Time: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"Weight: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"InTangent: {bin.ReadFloat()}") {Length = 4}, - new BinInterpNode(bin.Position, $"LeaveTangent: {bin.ReadFloat()}") {Length = 4} - } - }) - }); - if (pointsCount > 0) - { - if (game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - - nodes.Add(new BinInterpNode(bin.Position, $"NumKeys: {bin.ReadInt32()} items") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{bin.ReadInt32()} keys")) - }); - } - - nodes.Add(new BinInterpNode(bin.Position, $"Fade In Time: {bin.ReadFloat()}") { Length = 4 }); - nodes.Add(new BinInterpNode(bin.Position, $"Fade Out Time: {bin.ReadFloat()}") { Length = 4 }); - nodes.Add(MakeInt32Node(bin, "Unknown")); - if (game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - - nodes.Add(new BinInterpNode(bin.Position, $"Path: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - if (game == MEGame.ME2) - { - nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); - } - - nodes.Add(new BinInterpNode(bin.Position, $"ID: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); - nodes.Add(MakeInt32Node(bin, "index")); - } - - subnodes.Add(MakeInt32Node(bin, "unknown")); - - subnodes.Add(MakeArrayNode(bin, "Unknown Table D - Mapping?", i => new BinInterpNode(bin.Position, $"{i}") - { - IsExpanded = true, - Items = - { - new BinInterpNode(bin.Position, $"Column Id?: {bin.ReadInt32()}") {Length = 4}, - new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") {Length = 4}, - MakeFloatNode(bin, "Unk Float") - } - })); - subnodes.Add(MakeArrayNode(bin, "Lip sync phoneme list:", i => new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 })); - subnodes.Add(MakeInt32Node(bin, "Unknown")); - if (game is MEGame.LE1 or MEGame.LE2) - { - subnodes.Add(MakeArrayNode(bin, "Unknown Ints", i => new BinInterpNode(bin.Position, $"Unknown: {nameTable[bin.ReadInt32()]}") { Length = 4 })); - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioGestureRuntimeDataScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - subnodes.Add(MakeArrayNode(bin, "m_mapAnimSetOwners", i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc)} => {bin.ReadNameReference(Pcc)}") - { - Length = 16 - })); - - int count; - if (CurrentLoadedExport.Game.IsGame1()) - { - var propDataNode = new BinInterpNode(bin.Position, $"m_mapCharTypeOverrides ({count = bin.ReadInt32()} items)"); - subnodes.Add(propDataNode); - for (int i = 0; i < count; i++) - { - propDataNode.Items.Add(new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) - { - Length = 8, - IsExpanded = true, - Items = - { - MakeNameNode(bin, "nm_Female"), - MakeNameNode(bin, "nm_Asari"), - MakeNameNode(bin, "nm_Turian"), - MakeNameNode(bin, "nm_Salarian"), - MakeNameNode(bin, "nm_Quarian"), - MakeNameNode(bin, "nm_Other"), - MakeNameNode(bin, "nm_Krogan"), - MakeNameNode(bin, "nm_Geth"), - MakeNameNode(bin, "nm_Other_Artificial") - } - }); - } - } - else - { - var propDataNode = new BinInterpNode(bin.Position, $"m_mapMeshProps ({count = bin.ReadInt32()} items)"); - subnodes.Add(propDataNode); - for (int i = 0; i < count; i++) - { - BinInterpNode node = new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) - { - Length = 8 - }; - propDataNode.Items.Add(node); - node.Items.Add(MakeNameNode(bin, "nmPropName")); - node.Items.Add(MakeStringNode(bin, "sMesh")); - node.Items.Add(MakeNameNode(bin, "nmAttachTo")); - node.Items.Add(MakeVectorNode(bin, "vOffsetLocation")); - node.Items.Add(MakeRotatorNode(bin, "rOffsetRotation")); - node.Items.Add(MakeVectorNode(bin, "vOffsetScale")); - int count2; - var propActionsNode = new BinInterpNode(bin.Position, $"mapActions ({count2 = bin.ReadInt32()} items)") - { - IsExpanded = true - }; - node.Items.Add(propActionsNode); - for (int j = 0; j < count2; j++) - { - BinInterpNode node2 = new BinInterpNode(bin.Position, $"{j}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) - { - Length = 8 - }; - propActionsNode.Items.Add(node2); - node2.Items.Add(MakeNameNode(bin, "nmActionName")); - if (CurrentLoadedExport.Game.IsGame2()) - { - node2.Items.Add(MakeStringNode(bin, "sEffect")); - } - - node2.Items.Add(MakeBoolIntNode(bin, "bActivate")); - node2.Items.Add(MakeNameNode(bin, "nmAttachTo")); - node2.Items.Add(MakeVectorNode(bin, "vOffsetLocation")); - node2.Items.Add(MakeRotatorNode(bin, "rOffsetRotation")); - node2.Items.Add(MakeVectorNode(bin, "vOffsetScale")); - if (CurrentLoadedExport.Game.IsGame3()) - { - node2.Items.Add(MakeStringNode(bin, "sParticleSys")); - node2.Items.Add(MakeStringNode(bin, "sClientEffect")); - node2.Items.Add(MakeBoolIntNode(bin, "bCooldown")); - node2.Items.Add(new BinInterpNode(bin.Position, "tSpawnParams") - { - Length = 0x38, - IsExpanded = true, - Items = - { - MakeVectorNode(bin, "vHitLocation"), - MakeVectorNode(bin, "vHitNormal"), - MakeNameNode(bin, "nmHitBone"), - MakeVectorNode(bin, "vRayDir"), - MakeVectorNode(bin, "vSpawnValue") - } - }); - } - } - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - private List StartObjectRedirectorScan(byte[] data, ref int binaryStart) - { - var subnodes = new List(); - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.Skip(binaryStart); - subnodes.Add(MakeEntryNode(bin, "Redirect references to this export to")); - return subnodes; - } - - private List Scan_Bio2DA(byte[] data) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(data) { Endian = Pcc.Endian }; - bin.JumpTo(CurrentLoadedExport.propsEnd()); - - bool isIndexed = !bin.ReadBoolInt(); - bin.Skip(-4); - if (isIndexed) - { - subnodes.Add(MakeUInt32Node(bin, "Zero")); - } - - int cellCount; - subnodes.Add(new BinInterpNode(bin.Position, $"Populated Cell Count: {cellCount = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }); - - for (int i = 0; i < cellCount; i++) - { - Bio2DACell.Bio2DADataType type; - subnodes.Add(new BinInterpNode(bin.Position, $"Cell {(isIndexed ? bin.ReadInt32() : i)}", NodeType.StructLeafInt) - { - Items = - { - new BinInterpNode(bin.Position, $"Type: {type = (Bio2DACell.Bio2DADataType)bin.ReadByte()}") { Length = 1 }, - type switch - { - Bio2DACell.Bio2DADataType.TYPE_INT => MakeInt32Node(bin, "Value"), - Bio2DACell.Bio2DADataType.TYPE_NAME => MakeNameNode(bin, "Value"), - Bio2DACell.Bio2DADataType.TYPE_FLOAT => MakeFloatNode(bin, "Value"), - Bio2DACell.Bio2DADataType.TYPE_NULL => new BinInterpNode("Value: NULL"), - _ => throw new ArgumentOutOfRangeException() - } - } - }); - } - - if (!isIndexed) - { - subnodes.Add(MakeUInt32Node(bin, "Zero")); - } - - int columnCount; - subnodes.Add(new BinInterpNode(bin.Position, $"Column Count: {columnCount = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }); - - for (int i = 0; i < columnCount; i++) - { - subnodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.ReadNameReference(Pcc)}, Index: {bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 12 }); - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioSquadCombatScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - subnodes.Add(MakeArrayNode(bin, "Count", i => - { - string entry = null; - if (Pcc.Game.IsLEGame()) - { - entry = Pcc.GetEntryString(bin.ReadInt32()); - } - - var guid = bin.ReadGuid(); - int num = bin.ReadInt32(); - - return new BinInterpNode(bin.Position, $"{guid}: {num} {entry}"); - })); - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioDynamicAnimSetScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - - try - { - int binarypos = binarystart; - int count = EndianReader.ToInt32(data, binarypos, CurrentLoadedExport.FileRef.Endian); - subnodes.Add(new BinInterpNode - { - Header = $"0x{binarypos:X4} Count: {count}" - }); - binarypos += 4; //+ int - for (int i = 0; i < count; i++) - { - int nameIndex = EndianReader.ToInt32(data, binarypos, CurrentLoadedExport.FileRef.Endian); - int nameIndexNum = EndianReader.ToInt32(data, binarypos + 4, CurrentLoadedExport.FileRef.Endian); - int shouldBe1 = EndianReader.ToInt32(data, binarypos + 8, CurrentLoadedExport.FileRef.Endian); - - var name = CurrentLoadedExport.FileRef.GetNameEntry(nameIndex); - string nodeValue = $"{new NameReference(name, nameIndexNum).Instanced}"; - if (shouldBe1 != 1) - { - //ERROR - nodeValue += " - Not followed by 1 (integer)!"; - } - - subnodes.Add(new BinInterpNode - { - Header = $"0x{binarypos:X4} Name: {nodeValue}", - Tag = NodeType.StructLeafName, - Offset = binarypos, - }); - subnodes.Add(new BinInterpNode - { - Header = $"0x{(binarypos + 8):X4} Unknown 1: {shouldBe1}", - Tag = NodeType.StructLeafInt, - Offset = (binarypos + 8), - }); - binarypos += 12; - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartBioStageScan(byte[] data, ref int binarystart) - { - /* - * Length (int) - Name: m_aCameraList - int unknown 0 - Count + int unknown - [Camera name - unreal property data]*/ - var subnodes = new List(); - //if ((CurrentLoadedExport.Header[0x1f] & 0x2) != 0) - { - int pos = binarystart; - if (data.Length > binarystart) - { - int length = BitConverter.ToInt32(data, binarystart); - subnodes.Add(new BinInterpNode - { - Header = $"{binarystart:X4} Length: {length}", - Offset = pos - }); - pos += 4; - if (length != 0) - { - int nameindex = BitConverter.ToInt32(data, pos); - int num = BitConverter.ToInt32(data, pos + 4); - - var name = new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameindex), num); - subnodes.Add(new BinInterpNode - { - Header = $"{(pos - binarystart):X4} Array Name: {name.Instanced}", - Offset = pos, - Tag = NodeType.StructLeafName - }); - - pos += 8; - int shouldbezero = BitConverter.ToInt32(data, pos); - if (shouldbezero != 0) - { - Debug.WriteLine($"NOT ZERO FOUND: {pos}"); - } - pos += 4; - - int count = BitConverter.ToInt32(data, pos); - subnodes.Add(new BinInterpNode - { - Header = $"{(pos - binarystart):X4} Count: {count}", - Offset = pos - }); - pos += 4; - - shouldbezero = BitConverter.ToInt32(data, pos); - if (shouldbezero != 0) - { - Debug.WriteLine($"NOT ZERO FOUND: {pos}"); - } - pos += 4; - try - { - var stream = new MemoryStream(data); - for (int i = 0; i < count; i++) - { - nameindex = BitConverter.ToInt32(data, pos); - num = BitConverter.ToInt32(data, pos + 4); - BinInterpNode parentnode = new BinInterpNode - { - Header = $"{(pos - binarystart):X4} Camera {i + 1}: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameindex), num).Instanced}", - Tag = NodeType.StructLeafName, - Offset = pos - }; - subnodes.Add(parentnode); - pos += 8; - stream.Seek(pos, SeekOrigin.Begin); - var props = PropertyCollection.ReadProps(CurrentLoadedExport, stream, "BioStageCamera", includeNoneProperty: true); - - UPropertyTreeViewEntry topLevelTree = new UPropertyTreeViewEntry(); //not used, just for holding and building data. - foreach (Property prop in props) - { - InterpreterExportLoader.GenerateUPropertyTreeForProperty(prop, topLevelTree, CurrentLoadedExport); - } - subnodes.AddRange(topLevelTree.ChildrenProperties); - - //finish writing function here - pos = (int)stream.Position; - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - } - } - } - return subnodes; - } - - private List StartGuidCacheScan(byte[] data, ref int binarystart) - { - /* - * - * count +4 - * nameentry +8 - * guid +16 - * - */ - var subnodes = new List(); - - try - { - int pos = binarystart; - int count = BitConverter.ToInt32(data, pos); - subnodes.Add(new BinInterpNode - { - Header = $"{(pos - binarystart):X4} count: {count}", - Offset = pos, - }); - pos += 4; - for (int i = 0; i < count && pos < data.Length; i++) - { - int nameRef = BitConverter.ToInt32(data, pos); - int nameIdx = BitConverter.ToInt32(data, pos + 4); - Guid guid = new Guid(data.Skip(pos + 8).Take(16).ToArray()); - subnodes.Add(new BinInterpNode - { - Header = $"{(pos - binarystart):X4} {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameRef), nameIdx).Instanced}: {{{guid}}}", - Offset = pos, - - Tag = NodeType.StructLeafName - }); - //Debug.WriteLine($"{pos:X4} {new NameReference(CurrentLoadedExport.FileRef.getNameEntry(nameRef), nameIdx).Instanced}: {{{guid}}}"); - pos += 24; - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private List StartLevelScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeEntryNode(bin, "Self")); - int actorsCount; - BinInterpNode levelActorsNode; - subnodes.Add(levelActorsNode = new BinInterpNode(bin.Position, $"Level Actors: ({actorsCount = bin.ReadInt32()})", NodeType.StructLeafInt) - { - ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, - IsExpanded = true - }); - levelActorsNode.Items = ReadList(actorsCount, i => new BinInterpNode(bin.Position, $"{i}: {entryRefString(bin)}", NodeType.ArrayLeafObject) - { - ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, - Parent = levelActorsNode, - }); - - subnodes.Add(new BinInterpNode(bin.Position, "URL") - { - Items = - { - MakeStringNode(bin, "Protocol"), - MakeStringNode(bin, "Host"), - MakeStringNode(bin, "Map"), - MakeStringNode(bin, "Portal"), - new BinInterpNode(bin.Position, $"Op: ({bin.ReadInt32()} items)") - { - Items = ReadList(bin.Skip(-4).ReadInt32(), i => MakeStringNode(bin, $"{i}")) - }, - MakeInt32Node(bin, "Port"), - new BinInterpNode(bin.Position, $"Valid: {bin.ReadInt32()}") - } - }); - subnodes.Add(MakeEntryNode(bin, "Model")); - int modelcomponentsCount; - subnodes.Add(new BinInterpNode(bin.Position, $"ModelComponents: ({modelcomponentsCount = bin.ReadInt32()})") - { - Items = ReadList(modelcomponentsCount, i => MakeEntryNode(bin, $"{i}")) - }); - int sequencesCount; - subnodes.Add(new BinInterpNode(bin.Position, $"GameSequences: ({sequencesCount = bin.ReadInt32()})") - { - Items = ReadList(sequencesCount, i => MakeEntryNode(bin, $"{i}")) - }); - int texToInstCount; - int streamableTexInstCount; - subnodes.Add(new BinInterpNode(bin.Position, $"TextureToInstancesMap: ({texToInstCount = bin.ReadInt32()})") - { - Items = ReadList(texToInstCount, i => - new BinInterpNode(bin.Position, $"{entryRefString(bin)}: ({streamableTexInstCount = bin.ReadInt32()} StreamableTextureInstances)") - { - Items = ReadList(streamableTexInstCount, j => new BinInterpNode(bin.Position, $"{j}") - { - IsExpanded = true, - Items = - { - new BinInterpNode(bin.Position, "BoundingSphere") - { - IsExpanded = true, - Items = - { - MakeVectorNode(bin, "Center"), - new BinInterpNode(bin.Position, $"Radius: {bin.ReadSingle()}") - } - }, - new BinInterpNode(bin.Position, $"TexelFactor: {bin.ReadSingle()}") - } - }) - }) - }); - if (Pcc.Game == MEGame.UDK) - { - subnodes.Add(MakeArrayNode(bin, "MeshesComponentsWithDynamicLighting?", - i => new BinInterpNode(bin.Position, $"{i}: {entryRefString(bin)}, {bin.ReadInt32()}"))); - } - - if (Pcc.Game >= MEGame.ME3) - { - int apexSize; - subnodes.Add(new BinInterpNode(bin.Position, $"APEX Size: {apexSize = bin.ReadInt32()}")); - //should always be zero, but just in case... - if (apexSize > 0) - { - subnodes.Add(new BinInterpNode(bin.Position, $"APEX mesh?: {apexSize} bytes") { Length = apexSize }); - bin.Skip(apexSize); - } - } - - int cachedPhysBSPDataSize; - subnodes.Add(MakeInt32Node(bin, "size of byte")); - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysBSPData Size: {cachedPhysBSPDataSize = bin.ReadInt32()}")); - if (cachedPhysBSPDataSize > 0) - { - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysBSPData: {cachedPhysBSPDataSize} bytes") { Length = cachedPhysBSPDataSize }); - bin.Skip(cachedPhysBSPDataSize); - } - - int cachedPhysSMDataMapCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysSMDataMap: ({cachedPhysSMDataMapCount = bin.ReadInt32()})") - { - Items = ReadList(cachedPhysSMDataMapCount, i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}") - { - Items = - { - MakeVectorNode(bin, "Scale3D"), - new BinInterpNode(bin.Position, $"CachedDataIndex: {bin.ReadInt32()}") - } - }) - }); - - int cachedPhysSMDataStoreCount; - int cachedConvexElementsCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysSMDataStore: ({cachedPhysSMDataStoreCount = bin.ReadInt32()})") - { - Items = ReadList(cachedPhysSMDataStoreCount, i => new BinInterpNode(bin.Position, $"{i}: CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") - { - Items = ReadList(cachedConvexElementsCount, j => - { - int size; - var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") - { - Length = size + 8 - }; - bin.Skip(size); - return item; - }) - }) - }); - - int cachedPhysPerTriSMDataMapCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysPerTriSMDataMap: ({cachedPhysPerTriSMDataMapCount = bin.ReadInt32()})") - { - Items = ReadList(cachedPhysPerTriSMDataMapCount, i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}") - { - Items = - { - MakeVectorNode(bin, "Scale3D"), - new BinInterpNode(bin.Position, $"CachedDataIndex: {bin.ReadInt32()}") - } - }) - }); - - int cachedPhysPerTriSMDataStoreCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysPerTriSMDataStore: ({cachedPhysPerTriSMDataStoreCount = bin.ReadInt32()})") - { - Items = ReadList(cachedPhysPerTriSMDataStoreCount, j => - { - int size; - var item = new BinInterpNode(bin.Position, $"{j}: CachedPerTriData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") - { - Length = size + 8 - }; - bin.Skip(size); - return item; - }) - }); - - subnodes.Add(MakeInt32Node(bin, "CachedPhysBSPDataVersion")); - subnodes.Add(MakeInt32Node(bin, "CachedPhysSMDataVersion")); - - int forceStreamTexturesCount; - subnodes.Add(new BinInterpNode(bin.Position, $"ForceStreamTextures: ({forceStreamTexturesCount = bin.ReadInt32()})") - { - Items = ReadList(forceStreamTexturesCount, i => MakeBoolIntNode(bin, $"Texture: {entryRefString(bin)} | ForceStream")) - }); + bin.JumpTo(startOff + rotOff); + int header = bin.ReadInt32(); + int numKeys = header & 0x00FFFFFF; + int formatFlags = (header >> 24) & 0x0F; + AnimationCompressionFormat keyFormat = (AnimationCompressionFormat)((header >> 28) & 0x0F); - if (Pcc.Game == MEGame.UDK) - { - subnodes.Add(new BinInterpNode(bin.Position, "CachedPhysConvexBSPData") - { - Items = - { - new BinInterpNode(bin.Position, $"CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") + boneNode.Items.Add(new BinInterpNode(bin.Position - 4, $"RotKey Header: {numKeys} keys, Compression: {keyFormat}, FormatFlags:{formatFlags:X}") { Length = 4 }); + switch (keyFormat) { - Items = ReadList(cachedConvexElementsCount, j => - { - int size; - var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") + case AnimationCompressionFormat.ACF_None: { - Length = size + 8 - }; - bin.Skip(size); - return item; - }) - } - } - }); - subnodes.Add(MakeInt32Node(bin, "CachedPhysConvexBSPVersion")); - } - - subnodes.Add(MakeEntryNode(bin, "NavListStart")); - subnodes.Add(MakeEntryNode(bin, "NavListEnd")); - subnodes.Add(MakeEntryNode(bin, "CoverListStart")); - subnodes.Add(MakeEntryNode(bin, "CoverListEnd")); - if (Pcc.Game >= MEGame.ME3) - { - subnodes.Add(MakeEntryNode(bin, "PylonListStart")); - subnodes.Add(MakeEntryNode(bin, "PylonListEnd")); - } - if (Pcc.Game is MEGame.ME3 or MEGame.LE3 or MEGame.UDK) - { - int guidToIntMapCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelCoverGuidRefs: ({guidToIntMapCount = bin.ReadInt32()})") - { - Items = ReadList(guidToIntMapCount, i => MakeInt32Node(bin, $"{bin.ReadGuid()}")) - }); - - int coverListCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CoverLinkRefs: ({coverListCount = bin.ReadInt32()})") - { - Items = ReadList(coverListCount, i => MakeEntryNode(bin, $"{i}")) - }); - - int intToByteMapCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CoverIndexPairs: ({intToByteMapCount = bin.ReadInt32()})") - { - Items = ReadList(intToByteMapCount, i => new BinInterpNode(bin.Position, $"[{i}] {bin.ReadInt32()}: {bin.ReadByte()}")) - }); - - if (Pcc.Game != MEGame.UDK) - { - // BioWare only - - int guidToIntMap2Count; - subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelNavGuidRefs: ({guidToIntMap2Count = bin.ReadInt32()})") - { - Items = ReadList(guidToIntMap2Count, i => MakeInt32Node(bin, $"{bin.ReadGuid()}")) - }); - - int navListCount; - subnodes.Add(new BinInterpNode(bin.Position, $"NavRefs: ({navListCount = bin.ReadInt32()})") - { - Items = ReadList(navListCount, i => MakeEntryNode(bin, $"{i}")) - }); - - int numbersCount; - subnodes.Add(new BinInterpNode(bin.Position, - $"NavRefIndices: ({numbersCount = bin.ReadInt32()})") - { - Items = ReadList(numbersCount, i => MakeInt32Node(bin, $"{i}")) - }); - } - } - - int crossLevelActorsCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelActors?: ({crossLevelActorsCount = bin.ReadInt32()})") - { - Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}")) - }); - - if (Pcc.Game is MEGame.ME1 or MEGame.LE1) - { - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?")); - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?")); - } - - if (Pcc.Game >= MEGame.ME3) - { - bool bInitialized; - int samplesCount; - subnodes.Add(new BinInterpNode(bin.Position, "PrecomputedLightVolume") - { - Items = - { - new BinInterpNode(bin.Position, $"bInitialized: ({bInitialized = bin.ReadBoolInt()})"), - ListInitHelper.ConditionalAdd(bInitialized, () => new ITreeItem[] - { - MakeBoxNode(bin, "Bounds"), - MakeFloatNode(bin, "SampleSpacing"), - new BinInterpNode(bin.Position, $"Samples ({samplesCount = bin.ReadInt32()})") - { - Items = ReadList(samplesCount, i => new BinInterpNode(bin.Position, $"{i}") + for (int j = 0; j < numKeys; j++) + { + boneNode.Items.Add(MakeQuatNode(bin, $"RotKey {j}")); + } + break; + } + case AnimationCompressionFormat.ACF_Fixed48NoW: { - Items = + const float scale = 32767.0f; + const ushort unkConst = 32767; + int keyLength = 2 * ((formatFlags & 1) + ((formatFlags >> 1) & 1) + ((formatFlags >> 2) & 1)); + for (int j = 0; j < numKeys; j++) { - MakeVectorNode(bin, "Position"), - MakeFloatNode(bin, "Radius"), - ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] + int binPosition = (int)bin.Position; + float x = (formatFlags & 1) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0, + y = (formatFlags & 2) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0, + z = (formatFlags & 4) != 0 ? (bin.ReadUInt16() - unkConst) / scale : 0; + boneNode.Items.Add(new BinInterpNode(binPosition, $"RotKey {j}: (X: {x}, Y: {y}, Z: {z}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") { - MakeByteNode(bin, "IndirectDirectionTheta"), - MakeByteNode(bin, "IndirectDirectionPhi"), - MakeByteNode(bin, "EnvironmentDirectionTheta"), - MakeByteNode(bin, "EnvironmentDirectionPhi"), - MakeColorNode(bin, "IndirectRadiance"), - MakeColorNode(bin, "EnvironmentRadiance"), - MakeColorNode(bin, "AmbientRadiance"), - MakeByteNode(bin, "bShadowedFromDominantLights"), - }, () => new [] + Length = keyLength + }); + } + break; + } + case AnimationCompressionFormat.ACF_Float96NoW: + { + float x, y, z; + for (int j = 0; j < numKeys; j++) + { + boneNode.Items.Add(new BinInterpNode(bin.Position, $"RotKey {j}: (X: {x = bin.ReadFloat()}, Y: {y = bin.ReadFloat()}, Z: {z = bin.ReadFloat()}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") { - //SirCxyrtyx: This is a color, but is serialized as an FQuantizedSHVectorRGB, a vector of colored, quantized spherical harmonic coefficients. - //Conversion to ARGB is possible, but devilishly tricky. Let me know if this is something that's actually needed - new BinInterpNode(bin.Position, $"Ambient Radiance? : {bin.ReadToBuffer(39)}"){ Length = 39} - }) + Length = 12 + }); } - }) - } - }) + break; + } + case AnimationCompressionFormat.ACF_IntervalFixed32NoW: + case AnimationCompressionFormat.ACF_Fixed32NoW: + case AnimationCompressionFormat.ACF_Float32NoW: + case AnimationCompressionFormat.ACF_BioFixed48: + default: + throw new NotImplementedException($"{keyFormat} is not supported yet!"); + } } - }); + } } - if (Pcc.Game == MEGame.UDK) + catch (Exception ex) { - BinInterpNode item = new BinInterpNode(bin.Position, "PrecomputedVisibilityHandler") - { - IsExpanded = true - }; - subnodes.Add(item); - item.Items.Add(MakeVector2DNode(bin, "PrecomputedVisibilityCellBucketOriginXY")); - item.Items.Add(MakeFloatNode(bin, "PrecomputedVisibilityCellSizeXY")); - item.Items.Add(MakeFloatNode(bin, "PrecomputedVisibilityCellSizeZ")); - item.Items.Add(MakeInt32Node(bin, "PrecomputedVisibilityCellBucketSizeXY")); - item.Items.Add(MakeInt32Node(bin, "PrecomputedVisibilityNumCellBuckets")); - - item = new BinInterpNode(bin.Position, "PrecomputedVolumeDistanceField") - { - IsExpanded = true - }; - - subnodes.Add(item); - item.Items.Add(MakeFloatNode(bin, "VolumeMaxDistance")); - item.Items.Add(MakeBoxNode(bin, "VolumeBox")); - item.Items.Add(MakeInt32Node(bin, "VolumeSizeX")); - item.Items.Add(MakeInt32Node(bin, "VolumeSizeY")); - item.Items.Add(MakeInt32Node(bin, "VolumeSizeZ")); - item.Items.Add(MakeArrayNode(bin, "Data", x=> MakeColorNode(bin, $"Color[{x}]"))); - item.Items.Add(MakeInt32Node(bin, "UDKUnknown")); - + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } + return subnodes; + } - binarystart = (int)bin.Position; - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; - - private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name, out bool boolVal) - { - return new BinInterpNode(bin.Position, $"{name}: {boolVal = bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; - } - - private static BinInterpNode MakeBoolByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolByte()}") { Length = 1 }; - - private static BinInterpNode MakeFloatNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; - - private static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string name, bool create) - { - if (create) - { - return new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; - } - return null; - } - - private static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; - - private static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; - - private static BinInterpNode MakeInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; - - private static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; - - private static BinInterpNode MakeInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; - - private static BinInterpNode MakeInt32Node(EndianReader bin, string name, out int val) - { - return new BinInterpNode(bin.Position, $"{name}: {val = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; - } - - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; - - private static BinInterpNode MakeInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; - - private static BinInterpNode MakeByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; - - private static BinInterpNode MakeSByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; - - private BinInterpNode MakeNameNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadNameReference(Pcc).Instanced}", NodeType.StructLeafName) { Length = 8 }; - - private BinInterpNode MakeNameNode(EndianReader bin, string name, out NameReference nameRef) => - new BinInterpNode(bin.Position, $"{name}: {nameRef = bin.ReadNameReference(Pcc).Instanced}", NodeType.StructLeafName) { Length = 8 }; - - private BinInterpNode MakeEntryNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {entryRefString(bin)}", NodeType.StructLeafObject) { Length = 4 }; - private BinInterpNode MakeEntryNode(EndianReader bin, string name, out int uIndex) - { - long binPosition = bin.Position; - uIndex = bin.ReadInt32(); - string refString = $"#{uIndex} {CurrentLoadedExport.FileRef.GetEntryString(uIndex)}"; - return new BinInterpNode(binPosition, $"{name}: {refString}", NodeType.StructLeafObject) { Length = 4 }; - } + #endregion - private static BinInterpNode MakePackedNormalNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadByte() / 127.5f - 1}, Y: {bin.ReadByte() / 127.5f - 1}, Z: {bin.ReadByte() / 127.5f - 1}, W: {bin.ReadByte() / 127.5f - 1})") + try { - Length = 4 - }; - - private static BinInterpNode MakeVectorNodeEditable(EndianReader bin, string name, bool expanded = false) - { - var node = new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()})") { Length = 12 }; - bin.Position -= 12; - node.Items.Add(MakeFloatNode(bin, "X")); - node.Items.Add(MakeFloatNode(bin, "Y")); - node.Items.Add(MakeFloatNode(bin, "Z")); - node.IsExpanded = expanded; - return node; - } - - private static BinInterpNode MakeVector2DNodeEditable(EndianReader bin, string name, bool expanded = false) - { - var node = new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; - bin.Position -= 8; - node.Items.Add(MakeFloatNode(bin, "X")); - node.Items.Add(MakeFloatNode(bin, "Y")); - node.IsExpanded = expanded; - return node; - } - - private static BinInterpNode MakeVectorNode(EndianReader bin, string name) - { - var node = new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()})") { Length = 12 }; - bin.Position -= 12; - node.Items.Add(MakeFloatNode(bin, "X")); - node.Items.Add(MakeFloatNode(bin, "Y")); - node.Items.Add(MakeFloatNode(bin, "Z")); - return node; - } - - private static BinInterpNode MakeQuatNode(EndianReader bin, string name) - { - var node = new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()}, W: {bin.ReadFloat()})") { Length = 16 }; - bin.Position -= 16; - node.Items.Add(MakeFloatNode(bin, "X")); - node.Items.Add(MakeFloatNode(bin, "Y")); - node.Items.Add(MakeFloatNode(bin, "Z")); - node.Items.Add(MakeFloatNode(bin, "W")); - return node; - } - - private static BinInterpNode MakeRotatorNode(EndianReader bin, string name) - { - var node = new BinInterpNode(bin.Position, $"{name}: (Pitch: {bin.ReadInt32()}, Yaw: {bin.ReadInt32()}, Roll: {bin.ReadInt32()})") { Length = 12 }; - bin.Position -= 12; - node.Items.Add(MakeInt32Node(bin, "Pitch")); - node.Items.Add(MakeInt32Node(bin, "Yaw")); - node.Items.Add(MakeInt32Node(bin, "Roll")); - return node; - } + var TrackOffsets = CurrentLoadedExport.GetProperty>("CompressedTrackOffsets"); + var numFrames = CurrentLoadedExport.GetProperty("NumFrames")?.Value ?? 0; - private static BinInterpNode MakeBoxNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, name) - { - IsExpanded = true, - Items = + List boneList; + if (Pcc.Game == MEGame.UDK) { - MakeVectorNode(bin, "Min"), - MakeVectorNode(bin, "Max"), - new BinInterpNode(bin.Position, $"IsValid: {bin.ReadBoolByte()}") - }, - Length = 25 - }; - - private static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; + boneList = ((ExportEntry)CurrentLoadedExport.Parent)?.GetProperty>("TrackBoneNames")?.Select(np => $"{np}").ToList(); + } + else + { + var animsetData = CurrentLoadedExport.GetProperty("m_pBioAnimSetData"); + //In ME2, BioAnimSetData can sometimes be in a different package. + boneList = animsetData != null && Pcc.IsUExport(animsetData.Value) + ? Pcc.GetUExport(animsetData.Value).GetProperty>("TrackBoneNames")?.Select(np => $"{np}").ToList() + : null; + } - private static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat16()}, Y: {bin.ReadFloat16()})") { Length = 4 }; + boneList ??= Enumerable.Repeat("???", TrackOffsets.Count / 4).ToList(); + Enum.TryParse(CurrentLoadedExport.GetProperty("KeyEncodingFormat")?.Value.Name, out AnimationKeyFormat keyEncoding); + Enum.TryParse(CurrentLoadedExport.GetProperty("RotationCompressionFormat")?.Value.Name, out AnimationCompressionFormat rotCompression); + Enum.TryParse(CurrentLoadedExport.GetProperty("TranslationCompressionFormat")?.Value.Name, out AnimationCompressionFormat posCompression); - private static BinInterpNode MakeColorNode(EndianReader bin, string name) - { - return new BinInterpNode(bin.Position, $"{name}") - { - Length = 4, - Items = + var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; + bin.JumpTo(binarystart); + if (Pcc.Game is MEGame.ME2 or MEGame.LE2 && Pcc.Platform != MEPackage.GamePlatform.PS3) { - new BinInterpNode(bin.Position, $"B: {bin.ReadByte()}"), - new BinInterpNode(bin.Position, $"G: {bin.ReadByte()}"), - new BinInterpNode(bin.Position, $"R: {bin.ReadByte()}"), - new BinInterpNode(bin.Position, $"A: {bin.ReadByte()}"), + bin.Skip(12); + subnodes.Add(MakeInt32Node(bin, "AnimBinary Offset")); } - }; - } - - private static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) - { - return new BinInterpNode(bin.Position, $"{name}") - { - Items = + else if (Pcc.Game == MEGame.UDK) { - MakeVectorNode(bin, "Origin"), - MakeVectorNode(bin, "BoxExtent"), - MakeFloatNode(bin, "SphereRadius") + int numTracks = bin.ReadInt32() * 2; + bin.Skip(-4); + + BinInterpNode rawAnimDataNode = MakeInt32Node(bin, "RawAnimationData: NumTracks"); + subnodes.Add(rawAnimDataNode); + for (int i = 0; i < numTracks; i++) + { + int keySize = bin.ReadInt32(); + int numKeys = bin.ReadInt32(); + for (int j = 0; j < numKeys; j++) + { + if (keySize == 12) + { + rawAnimDataNode.Items.Add(MakeVectorNode(bin, $"{boneList[i / 2]}, PosKey {j}")); + } + else if (keySize == 16) + { + rawAnimDataNode.Items.Add(MakeQuatNode(bin, $"{boneList[i / 2]}, RotKey {j}")); + } + else + { + throw new NotImplementedException($"Unexpected key size: {keySize}"); + } + } + } } - }; - } - private static BinInterpNode MakeMaterialGuidNode(EndianReader bin, string name, Dictionary materialGuidMap = null) - { - var guid = bin.ReadGuid(); - var node = new BinInterpNode(bin.Position - 16, $"{name}: {guid}") { Length = 16 }; + subnodes.Add(MakeInt32Node(bin, "AnimBinary length")); + var startOffset = bin.Position; + for (int i = 0; i < boneList.Count; i++) + { + var boneNode = new BinInterpNode(bin.Position, boneList[i]); + subnodes.Add(boneNode); -#if DEBUG - if (materialGuidMap != null && materialGuidMap.TryGetValue(guid, out var matName)) - { - node.Header += " " + matName; - } -#endif + int posOff = TrackOffsets[i * 4]; + int posKeys = TrackOffsets[i * 4 + 1]; + int rotOff = TrackOffsets[i * 4 + 2]; + int rotKeys = TrackOffsets[i * 4 + 3]; - node.Tag = NodeType.Guid; - return node; - } + if (posKeys > 0) + { + bin.JumpTo(startOffset + posOff); - private static BinInterpNode MakeGuidNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadGuid()}", NodeType.Guid) { Length = 16 }; + AnimationCompressionFormat compressionFormat = posCompression; - private static BinInterpNode MakeArrayNode(EndianReader bin, string name, Func selector, bool IsExpanded = false, - BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) - { - int count; - return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadInt32()})") - { - IsExpanded = IsExpanded, - Items = ReadList(count, selector), - ArrayAddAlgorithm = arrayAddAlgo, - Length = 4 - }; - } + if (posKeys == 1) + { + compressionFormat = AnimationCompressionFormat.ACF_None; + } + for (int j = 0; j < posKeys; j++) + { + BinInterpNode posKeyNode; + switch (compressionFormat) + { + case AnimationCompressionFormat.ACF_None: + case AnimationCompressionFormat.ACF_Float96NoW: + posKeyNode = MakeVectorNode(bin, $"PosKey {j}"); + break; + case AnimationCompressionFormat.ACF_IntervalFixed32NoW: + case AnimationCompressionFormat.ACF_Fixed48NoW: + case AnimationCompressionFormat.ACF_Fixed32NoW: + case AnimationCompressionFormat.ACF_Float32NoW: + case AnimationCompressionFormat.ACF_BioFixed48: + default: + throw new NotImplementedException($"Translation keys in format {compressionFormat} cannot be read!"); + } + boneNode.Items.Add(posKeyNode); + } - private static BinInterpNode MakeArrayNodeInt16Count(EndianReader bin, string name, Func selector, bool IsExpanded = false, - BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) - { - int count; - return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadInt16()})") - { - IsExpanded = IsExpanded, - Items = ReadList(count, selector), - ArrayAddAlgorithm = arrayAddAlgo, - Length = 2 - }; - } + readTrackTable(posKeys); + } - private static BinInterpNode MakeArrayNodeByteCount(EndianReader bin, string name, Func selector, bool IsExpanded = false, - BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) - { - int count; - return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadByte()})") - { - IsExpanded = IsExpanded, - Items = ReadList(count, selector), - ArrayAddAlgorithm = arrayAddAlgo, - Length = 1 - }; - } + if (rotKeys > 0) + { + bin.JumpTo(startOffset + rotOff); - private static BinInterpNode MakeByteArrayNode(EndianReader bin, string name) - { - int pos = (int)bin.Position; - int count = bin.ReadInt32(); - bin.Skip(count); - return new BinInterpNode(pos, $"{name} ({count} bytes)"); - } + AnimationCompressionFormat compressionFormat = rotCompression; - private static BinInterpNode MakeArrayNode(int count, EndianReader bin, string name, Func selector, bool IsExpanded = false) - { - return new BinInterpNode(bin.Position, $"{name} ({count})") - { - IsExpanded = IsExpanded, - Items = ReadList(count, selector) - }; - } + if (rotKeys == 1) + { + compressionFormat = AnimationCompressionFormat.ACF_Float96NoW; + } + else if (Pcc.Game != MEGame.UDK) + { + boneNode.Items.Add(MakeVectorNode(bin, "Mins")); + boneNode.Items.Add(MakeVectorNode(bin, "Ranges")); + } + + for (int j = 0; j < rotKeys; j++) + { + BinInterpNode rotKeyNode; + switch (compressionFormat) + { + case AnimationCompressionFormat.ACF_None: + rotKeyNode = MakeQuatNode(bin, $"RotKey {j}"); + break; + case AnimationCompressionFormat.ACF_Float96NoW: + { + float x, y, z; + rotKeyNode = new BinInterpNode(bin.Position, $"RotKey {j}: (X: {x = bin.ReadFloat()}, Y: {y = bin.ReadFloat()}, Z: {z = bin.ReadFloat()}, W: {AnimSequence.ReconstructQuaternionComponent(x, y, z)})") + { + Length = 12 + }; + break; + } + case AnimationCompressionFormat.ACF_BioFixed48: + { + var pos = bin.Position; + ushort a = bin.ReadUInt16(); + ushort b = bin.ReadUInt16(); + ushort c = bin.ReadUInt16(); + var rot = AnimSequence.DecompressBioFixed48(a, b, c); + rotKeyNode = new BinInterpNode(pos, $"RotKey {j}: (X: {rot.X}, Y: {rot.Y}, Z: {rot.Z}, W: {rot.W})") + { + Length = 6 + }; + break; + } + case AnimationCompressionFormat.ACF_Fixed48NoW: + { + var pos = bin.Position; + var a = bin.ReadUInt16(); + var b = bin.ReadUInt16(); + var c = bin.ReadUInt16(); + var rot = AnimSequence.DecompressFixed48NoW(a, b, c); + rotKeyNode = new BinInterpNode(pos, $"RotKey {j}: (X: {rot.X}, Y: {rot.Y}, Z: {rot.Z}, W: {rot.W})") + { + Length = 6 + }; + break; + } + case AnimationCompressionFormat.ACF_IntervalFixed32NoW: + case AnimationCompressionFormat.ACF_Fixed32NoW: + case AnimationCompressionFormat.ACF_Float32NoW: + default: + throw new NotImplementedException($"Rotation keys in format {compressionFormat} cannot be read!"); + } + boneNode.Items.Add(rotKeyNode); + } + readTrackTable(rotKeys); + } + void readTrackTable(int numKeys) + { + if (keyEncoding == AnimationKeyFormat.AKF_VariableKeyLerp && numKeys > 1) + { + bin.JumpTo((bin.Position - startOffset).Align(4) + startOffset); + var trackTable = new BinInterpNode(bin.Position, "TrackTable"); + boneNode.Items.Add(trackTable); + for (int j = 0; j < numKeys; j++) + { + trackTable.Items.Add(numFrames > 0xFF ? MakeUInt16Node(bin, $"{j}") : MakeByteNode(bin, $"{j}")); + } + } + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } - [Flags] - public enum ECoordTransformUsage : uint + private List StartObjectRedirectorScan(byte[] data, ref int binaryStart) { - // no transforms used - UsedCoord_None = 0, - // local to world used - UsedCoord_World = 1 << 0, - // local to view used - UsedCoord_View = 1 << 1, - // local to local used - UsedCoord_Local = 1 << 2, - // World Position used - UsedCoord_WorldPos = 1 << 3 + var subnodes = new List(); + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.Skip(binaryStart); + subnodes.Add(MakeEntryNode(bin, "Redirect references to this export to", Pcc)); + return subnodes; } - private List StartPrefabInstanceScan(byte[] data, ref int binarystart) + private List StartGuidCacheScan(byte[] data, ref int binarystart) { /* - * count: 4 bytes - * Prefab ref : 4 bytes - * Level Object : 4 bytes - * 0: 4 bytes * + * count +4 + * nameentry +8 + * guid +16 + * */ var subnodes = new List(); - if (!CurrentLoadedExport.HasStack) - { - return subnodes; - } try { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - subnodes.Add(MakeArrayNode(bin, "ArchetypeToInstanceMap", i => new BinInterpNode(bin.Position, $"{i}") + int pos = binarystart; + int count = BitConverter.ToInt32(data, pos); + subnodes.Add(new BinInterpNode { - IsExpanded = true, - Items = - { - MakeEntryNode(bin, "Archetype"), - MakeEntryNode(bin, "Instance") - } - }, true)); - subnodes.Add(MakeArrayNode(bin, "PrefabInstance_ObjectMap", i => new BinInterpNode(bin.Position, $"{i}") + Header = $"{(pos - binarystart):X4} count: {count}", + Offset = pos, + }); + pos += 4; + for (int i = 0; i < count && pos < data.Length; i++) { - IsExpanded = true, - Items = + int nameRef = BitConverter.ToInt32(data, pos); + int nameIdx = BitConverter.ToInt32(data, pos + 4); + Guid guid = new Guid(data.Skip(pos + 8).Take(16).ToArray()); + subnodes.Add(new BinInterpNode { - MakeEntryNode(bin, "Object:"), - MakeInt32Node(bin, "int") - } - }, true)); + Header = $"{(pos - binarystart):X4} {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameRef), nameIdx).Instanced}: {{{guid}}}", + Offset = pos, + + Tag = NodeType.StructLeafName + }); + //Debug.WriteLine($"{pos:X4} {new NameReference(CurrentLoadedExport.FileRef.getNameEntry(nameRef), nameIdx).Instanced}: {{{guid}}}"); + pos += 24; + } } catch (Exception ex) { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); } return subnodes; } - private List StartSkeletalMeshScan(byte[] data, ref int binarystart) + private List StartLevelScan(byte[] data, ref int binarystart) { var subnodes = new List(); - var game = CurrentLoadedExport.FileRef.Game; try { - PackageCache cache = new PackageCache(); var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - subnodes.Add(MakeBoxSphereBoundsNode(bin, "Bounds")); - subnodes.Add(MakeArrayNode(bin, "Materials", i => + subnodes.Add(MakeEntryNode(bin, "Self", Pcc)); + int actorsCount; + BinInterpNode levelActorsNode; + subnodes.Add(levelActorsNode = new BinInterpNode(bin.Position, $"Level Actors: ({actorsCount = bin.ReadInt32()})", NodeType.StructLeafInt) + { + ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, + IsExpanded = true + }); + levelActorsNode.Items = ReadList(actorsCount, i => new BinInterpNode(bin.Position, $"{i}: {MakeEntryNodeString(bin, Pcc)}", NodeType.ArrayLeafObject) + { + ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, + Parent = levelActorsNode, + }); + + subnodes.Add(new BinInterpNode(bin.Position, "URL") { - var matNode = MakeEntryNode(bin, $"{i}"); - try + Items = { - var value = bin.Skip(-4).ReadInt32(); - if (value != 0 && Pcc.GetEntry(value) is ExportEntry matExport) + MakeStringNode(bin, "Protocol", Pcc.Game), + MakeStringNode(bin, "Host", Pcc.Game), + MakeStringNode(bin, "Map", Pcc.Game), + MakeStringNode(bin, "Portal", Pcc.Game), + new BinInterpNode(bin.Position, $"Op: ({bin.ReadInt32()} items)") { - foreach (IEntry texture in MaterialInstanceConstant.GetTextures(matExport, cache)) + Items = ReadList(bin.Skip(-4).ReadInt32(), i => MakeStringNode(bin, $"{i}", Pcc.Game)) + }, + MakeInt32Node(bin, "Port"), + new BinInterpNode(bin.Position, $"Valid: {bin.ReadInt32()}") + } + }); + subnodes.Add(MakeEntryNode(bin, "Model", Pcc)); + int modelcomponentsCount; + subnodes.Add(new BinInterpNode(bin.Position, $"ModelComponents: ({modelcomponentsCount = bin.ReadInt32()})") + { + Items = ReadList(modelcomponentsCount, i => MakeEntryNode(bin, $"{i}", Pcc)) + }); + int sequencesCount; + subnodes.Add(new BinInterpNode(bin.Position, $"GameSequences: ({sequencesCount = bin.ReadInt32()})") + { + Items = ReadList(sequencesCount, i => MakeEntryNode(bin, $"{i}", Pcc)) + }); + int texToInstCount; + int streamableTexInstCount; + subnodes.Add(new BinInterpNode(bin.Position, $"TextureToInstancesMap: ({texToInstCount = bin.ReadInt32()})") + { + Items = ReadList(texToInstCount, i => + new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}: ({streamableTexInstCount = bin.ReadInt32()} StreamableTextureInstances)") + { + Items = ReadList(streamableTexInstCount, j => new BinInterpNode(bin.Position, $"{j}") + { + IsExpanded = true, + Items = { - matNode.Items.Add(new BinInterpNode(-1, $"#{texture.UIndex} {texture.FileRef.GetEntryString(texture.UIndex)}", NodeType.StructLeafObject) { UIndexValue = texture.UIndex }); + new BinInterpNode(bin.Position, "BoundingSphere") + { + IsExpanded = true, + Items = + { + MakeVectorNode(bin, "Center"), + new BinInterpNode(bin.Position, $"Radius: {bin.ReadSingle()}") + } + }, + new BinInterpNode(bin.Position, $"TexelFactor: {bin.ReadSingle()}") } - } - } - catch + }) + }) + }); + if (Pcc.Game == MEGame.UDK) + { + subnodes.Add(MakeArrayNode(bin, "MeshesComponentsWithDynamicLighting?", + i => new BinInterpNode(bin.Position, $"{i}: {MakeEntryNodeString(bin, Pcc)}, {bin.ReadInt32()}"))); + } + + if (Pcc.Game >= MEGame.ME3) + { + int apexSize; + subnodes.Add(new BinInterpNode(bin.Position, $"APEX Size: {apexSize = bin.ReadInt32()}")); + //should always be zero, but just in case... + if (apexSize > 0) { - matNode.Items.Add(new BinInterpNode("Error reading Material!")); + subnodes.Add(new BinInterpNode(bin.Position, $"APEX mesh?: {apexSize} bytes") { Length = apexSize }); + bin.Skip(apexSize); } + } + + int cachedPhysBSPDataSize; + subnodes.Add(MakeInt32Node(bin, "size of byte")); + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysBSPData Size: {cachedPhysBSPDataSize = bin.ReadInt32()}")); + if (cachedPhysBSPDataSize > 0) + { + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysBSPData: {cachedPhysBSPDataSize} bytes") { Length = cachedPhysBSPDataSize }); + bin.Skip(cachedPhysBSPDataSize); + } - return matNode; - }, true, BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes)); - subnodes.Add(MakeVectorNode(bin, "Origin")); - subnodes.Add(MakeRotatorNode(bin, "Rotation Origin")); - subnodes.Add(MakeArrayNode(bin, "RefSkeleton", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc).Instanced}") + int cachedPhysSMDataMapCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysSMDataMap: ({cachedPhysSMDataMapCount = bin.ReadInt32()})") { - Items = + Items = ReadList(cachedPhysSMDataMapCount, i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}") { - MakeUInt32Node(bin, "Flags"), - MakeQuatNode(bin, "Bone Orientation (quaternion)"), - MakeVectorNode(bin, "Bone Position"), - MakeInt32Node(bin, "NumChildren"), - MakeInt32Node(bin, "ParentIndex"), - ListInitHelper.ConditionalAddOne( Pcc.Game >= MEGame.ME3, () => MakeColorNode(bin, "BoneColor")), - } - })); - subnodes.Add(MakeInt32Node(bin, "SkeletalDepth")); - int rawPointIndicesCount; - bool useFullPrecisionUVs = true; - int numTexCoords = 1; - subnodes.Add(MakeArrayNode(bin, "LODModels", i => + Items = + { + MakeVectorNode(bin, "Scale3D"), + new BinInterpNode(bin.Position, $"CachedDataIndex: {bin.ReadInt32()}") + } + }) + }); + + int cachedPhysSMDataStoreCount; + int cachedConvexElementsCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysSMDataStore: ({cachedPhysSMDataStoreCount = bin.ReadInt32()})") { - BinInterpNode node = new BinInterpNode(bin.Position, $"{i}"); - try + Items = ReadList(cachedPhysSMDataStoreCount, i => new BinInterpNode(bin.Position, $"{i}: CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") { - node.Items.Add(MakeArrayNode(bin, "Sections", j => new BinInterpNode(bin.Position, $"{j}") + Items = ReadList(cachedConvexElementsCount, j => { - Items = + int size; + var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") { - MakeUInt16Node(bin, "MaterialIndex"), - MakeUInt16Node(bin, "ChunkIndex"), - MakeUInt32Node(bin, "BaseIndex"), - ListInitHelper.ConditionalAddOne(Pcc.Game >= MEGame.ME3, - () => MakeUInt32Node(bin, "NumTriangles"), - () => MakeUInt16Node(bin, "NumTriangles")), - ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeByteNode(bin, "TriangleSorting")) - } - })); - node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] - { - MakeBoolIntNode(bin, "NeedsCPUAccess"), - MakeByteNode(bin, "Datatype size"), - })); - node.Items.Add(MakeInt32Node(bin, "Index size?")); - if (Pcc.Game == MEGame.UDK && bin.Skip(-4).ReadInt32() == 4) + Length = size + 8 + }; + bin.Skip(size); + return item; + }) + }) + }); + + int cachedPhysPerTriSMDataMapCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysPerTriSMDataMap: ({cachedPhysPerTriSMDataMapCount = bin.ReadInt32()})") + { + Items = ReadList(cachedPhysPerTriSMDataMapCount, i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}") + { + Items = { - node.Items.Add(MakeArrayNode(bin, "IndexBuffer", j => MakeUInt32Node(bin, $"{j}"))); + MakeVectorNode(bin, "Scale3D"), + new BinInterpNode(bin.Position, $"CachedDataIndex: {bin.ReadInt32()}") } - else + }) + }); + + int cachedPhysPerTriSMDataStoreCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysPerTriSMDataStore: ({cachedPhysPerTriSMDataStoreCount = bin.ReadInt32()})") + { + Items = ReadList(cachedPhysPerTriSMDataStoreCount, j => + { + int size; + var item = new BinInterpNode(bin.Position, $"{j}: CachedPerTriData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") { - node.Items.Add(MakeArrayNode(bin, "IndexBuffer", j => MakeUInt16Node(bin, $"{j}"))); - } - node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "ShadowIndices", j => MakeUInt16Node(bin, $"{j}")))); - node.Items.Add(MakeArrayNode(bin, "ActiveBoneIndices", j => MakeUInt16Node(bin, $"{j}"))); - node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "ShadowTriangleDoubleSided", j => MakeByteNode(bin, $"{j}")))); - node.Items.Add(MakeArrayNode(bin, "Chunks", j => new BinInterpNode(bin.Position, $"{j}") + Length = size + 8 + }; + bin.Skip(size); + return item; + }) + }); + + subnodes.Add(MakeInt32Node(bin, "CachedPhysBSPDataVersion")); + subnodes.Add(MakeInt32Node(bin, "CachedPhysSMDataVersion")); + + int forceStreamTexturesCount; + subnodes.Add(new BinInterpNode(bin.Position, $"ForceStreamTextures: ({forceStreamTexturesCount = bin.ReadInt32()})") + { + Items = ReadList(forceStreamTexturesCount, i => MakeBoolIntNode(bin, $"Texture: {MakeEntryNodeString(bin, Pcc)} | ForceStream")) + }); + + if (Pcc.Game == MEGame.UDK) + { + subnodes.Add(new BinInterpNode(bin.Position, "CachedPhysConvexBSPData") + { + Items = { - Items = + new BinInterpNode(bin.Position, $"CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") { - MakeUInt32Node(bin, "BaseVertexIndex"), - MakeArrayNode(bin, "RigidVertices", k => new BinInterpNode(bin.Position, $"{k}") - { - Items = - { - MakeVectorNode(bin, "Position"), - MakePackedNormalNode(bin, "TangentX"), - MakePackedNormalNode(bin, "TangentY"), - MakePackedNormalNode(bin, "TangentZ"), - MakeVector2DNode(bin, "UV"), - ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] - { - MakeVector2DNode(bin, "UV2"), - MakeVector2DNode(bin, "UV3"), - MakeVector2DNode(bin, "UV4"), - MakeColorNode(bin, "BoneColor"), - }), - MakeByteNode(bin, "Bone") - } - }), - MakeArrayNode(bin, "SoftVertices", k => new BinInterpNode(bin.Position, $"{k}") + Items = ReadList(cachedConvexElementsCount, j => { - Items = + int size; + var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") { - MakeVectorNode(bin, "Position"), - MakePackedNormalNode(bin, "TangentX"), - MakePackedNormalNode(bin, "TangentY"), - MakePackedNormalNode(bin, "TangentZ"), - MakeVector2DNode(bin, "UV"), - ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] - { - MakeVector2DNode(bin, "UV2"), - MakeVector2DNode(bin, "UV3"), - MakeVector2DNode(bin, "UV4"), - MakeColorNode(bin, "BoneColor"), - }), - new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceBones[{l}]"))), - new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceWeights[{l}]"))) - } - }), - MakeArrayNode(bin, "BoneMap", k => MakeUInt16Node(bin, $"{k}")), - MakeInt32Node(bin, "NumRigidVertices"), - MakeInt32Node(bin, "NumSoftVertices"), - MakeInt32Node(bin, "MaxBoneInfluences"), - } - })); - node.Items.Add(MakeUInt32Node(bin, "Size")); - node.Items.Add(MakeUInt32Node(bin, "NumVertices")); - node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "Edges", j => new BinInterpNode(bin.Position, $"{j}") - { - Items = - { - MakeInt32Node(bin, "Vertices[0]"), - MakeInt32Node(bin, "Vertices[1]"), - MakeInt32Node(bin, "Faces[0]"), - MakeInt32Node(bin, "Faces[1]"), - } - }))); - node.Items.Add(MakeArrayNode(bin, "RequiredBones", j => MakeByteNode(bin, $"{j}"))); - node.Items.Add(MakeUInt32Node(bin, "RawPointIndices BulkDataFlags")); - node.Items.Add(new BinInterpNode(bin.Position, $"RawPointIndices Count: {rawPointIndicesCount = bin.ReadInt32()}")); - node.Items.Add(MakeUInt32Node(bin, "RawPointIndices size")); - node.Items.Add(MakeUInt32Node(bin, "RawPointIndices file offset")); - node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, - () => MakeArrayNode(rawPointIndicesCount, bin, "RawPointIndices", k => MakeInt32Node(bin, $"{k}")), - () => MakeArrayNode(rawPointIndicesCount, bin, "RawPointIndices", k => MakeUInt16Node(bin, $"{k}")))); - node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeInt32Node(bin, "NumTexCoords"))); - BinInterpNode item = new BinInterpNode(bin.Position, "VertexBufferGPUSkin") - { - IsExpanded = true - }; - node.Items.Add(item); - item.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game != MEGame.ME1, () => new List - { - ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeInt32Node(bin, "NumTexCoords", out numTexCoords)), - MakeBoolIntNode(bin, "bUseFullPrecisionUVs", out useFullPrecisionUVs), - ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new ITreeItem[] - { - MakeBoolIntNode(bin, "bUsePackedPosition"), - MakeVectorNode(bin, "MeshExtension"), - MakeVectorNode(bin, "MeshOrigin"), - }), - })); - item.Items.Add(MakeInt32Node(bin, "vertex size")); - item.Items.Add(MakeArrayNode(bin, "VertexData", k => new BinInterpNode(bin.Position, $"{k}") - { - Items = - { - ListInitHelper.ConditionalAddOne(Pcc.Game <= MEGame.ME2, () => MakeVectorNode(bin, "Position")), - MakePackedNormalNode(bin, "TangentX"), - ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.ME1, () => MakePackedNormalNode(bin, "TangentY")), - MakePackedNormalNode(bin, "TangentZ"), - ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.ME1, () => MakeVector2DNode(bin, "UV")), - new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceBones[{l}]"))), - new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceWeights[{l}]"))), - ListInitHelper.ConditionalAddOne(Pcc.Game >= MEGame.ME3, () => MakeVectorNode(bin, "Position")), - ListInitHelper.ConditionalAdd(Pcc.Game != MEGame.ME1, - () => ListInitHelper.ConditionalAddOne(useFullPrecisionUVs, - () => MakeVector2DNode(bin, "UV"), - () => MakeVector2DHalfNode(bin, "UV"))), - ListInitHelper.ConditionalAddOne(numTexCoords > 1, () => MakeArrayNode(numTexCoords - 1, bin, "Additional UVs", - i => useFullPrecisionUVs ? MakeVector2DNode(bin, "UV") : MakeVector2DHalfNode(bin, "UV"))) - } - })); - int vertexInfluenceSize; - node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new List - { - new BinInterpNode(bin.Position, $"VertexInfluence size: {vertexInfluenceSize = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }, - ListInitHelper.ConditionalAdd(vertexInfluenceSize > 0, () => new ITreeItem[] - { - MakeArrayNode(bin, "VertexInfluences", i => MakeInt32Node(bin, $"{i}")), - MakeInt32Node(bin, "Unknown") - }) - })); - if (Pcc.Game is MEGame.UDK) - { - node.Items.Add(MakeBoolIntNode(bin, "NeedsCPUAccess")); - node.Items.Add(MakeByteNode(bin, "Datatype size")); - node.Items.Add(MakeInt32Node(bin, "index size", out int indexSize)); - if (indexSize == 4) - { - node.Items.Add(MakeArrayNode(bin, "Second IndexBuffer?", j => MakeUInt32Node(bin, $"{j}"))); - } - else - { - node.Items.Add(MakeArrayNode(bin, "Second IndexBuffer?", j => MakeUInt16Node(bin, $"{j}"))); + Length = size + 8 + }; + bin.Skip(size); + return item; + }) } } - } - catch (Exception e) - { - node.Items.Add(new BinInterpNode { Header = $"Error reading binary data: {e}" }); - } - return node; - }, true)); - subnodes.Add(MakeArrayNode(bin, "NameIndexMap", i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc).Instanced} => {bin.ReadInt32()}"))); - subnodes.Add(MakeArrayNode(bin, "PerPolyBoneKDOPs", i => new BinInterpNode(bin.Position, $"{i}") - { - Items = - { - MakekDOPTreeNode(bin), - MakeArrayNode(bin, "CollisionVerts", j => MakeVectorNode(bin, $"{j}")) - } - })); + }); + subnodes.Add(MakeInt32Node(bin, "CachedPhysConvexBSPVersion")); + } + + subnodes.Add(MakeEntryNode(bin, "NavListStart", Pcc)); + subnodes.Add(MakeEntryNode(bin, "NavListEnd", Pcc)); + subnodes.Add(MakeEntryNode(bin, "CoverListStart", Pcc)); + subnodes.Add(MakeEntryNode(bin, "CoverListEnd", Pcc)); if (Pcc.Game >= MEGame.ME3) { - subnodes.Add(MakeArrayNode(bin, "BoneBreakNames", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUnrealString()}"))); - subnodes.Add(MakeArrayNode(bin, "ClothingAssets", i => MakeEntryNode(bin, $"{i}"))); + subnodes.Add(MakeEntryNode(bin, "PylonListStart", Pcc)); + subnodes.Add(MakeEntryNode(bin, "PylonListEnd", Pcc)); } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - - private List StartStaticMeshCollectionActorScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - //get a list of staticmesh stuff from the props. - var smacitems = new List(); - var props = CurrentLoadedExport.GetProperty>("StaticMeshComponents"); - - foreach (var prop in props) + if (Pcc.Game is MEGame.ME3 or MEGame.LE3 or MEGame.UDK) { - if (prop.Value > 0) - { - smacitems.Add(Pcc.GetUExport(prop.Value)); - } - else + int guidToIntMapCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelCoverGuidRefs: ({guidToIntMapCount = bin.ReadInt32()})") { - smacitems.Add(null); - } - } - - //find start of class binary (end of props) - int start = binarystart; + Items = ReadList(guidToIntMapCount, i => MakeInt32Node(bin, $"{bin.ReadGuid()}")) + }); - //Lets make sure this binary is divisible by 64. - if ((data.Length - start) % 64 != 0) - { - subnodes.Add(new BinInterpNode + int coverListCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CoverLinkRefs: ({coverListCount = bin.ReadInt32()})") { - Tag = NodeType.Unknown, - Header = $"{start:X4} Binary data is not divisible by 64 ({data.Length - start})! SMCA binary data should be a length divisible by 64.", - Offset = start + Items = ReadList(coverListCount, i => MakeEntryNode(bin, $"{i}", Pcc)) }); - return subnodes; - } - int smcaindex = 0; - while (start < data.Length && smcaindex < smacitems.Count) - { - BinInterpNode smcanode = new BinInterpNode + int intToByteMapCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CoverIndexPairs: ({intToByteMapCount = bin.ReadInt32()})") { - Tag = NodeType.Unknown - }; - ExportEntry associatedData = smacitems[smcaindex]; - string staticmesh = ""; - string objtext = "Null - unused data"; - if (associatedData != null) + Items = ReadList(intToByteMapCount, i => new BinInterpNode(bin.Position, $"[{i}] {bin.ReadInt32()}: {bin.ReadByte()}")) + }); + + if (Pcc.Game != MEGame.UDK) { - objtext = $"[Export {associatedData.UIndex}] {associatedData.ObjectName.Instanced}"; + // BioWare only - //find associated static mesh value for display. - var smc_data = associatedData.DataReadOnly; - int staticmeshstart = 0x4; - bool found = false; - while (staticmeshstart < smc_data.Length && smc_data.Length - 8 >= staticmeshstart) + int guidToIntMap2Count; + subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelNavGuidRefs: ({guidToIntMap2Count = bin.ReadInt32()})") { - ulong nameindex = EndianReader.ToUInt64(smc_data, staticmeshstart, Pcc.Endian); - if (nameindex < (ulong)CurrentLoadedExport.FileRef.Names.Count && CurrentLoadedExport.FileRef.Names[(int)nameindex] == "StaticMesh") - { - //found it - found = true; - break; - } - else - { - staticmeshstart += 1; - } - } + Items = ReadList(guidToIntMap2Count, i => MakeInt32Node(bin, $"{bin.ReadGuid()}")) + }); - if (found) + int navListCount; + subnodes.Add(new BinInterpNode(bin.Position, $"NavRefs: ({navListCount = bin.ReadInt32()})") { - int staticmeshexp = EndianReader.ToInt32(smc_data, staticmeshstart + 0x18, Pcc.Endian); - if (staticmeshexp > 0 && staticmeshexp < CurrentLoadedExport.FileRef.ExportCount) - { - staticmesh = Pcc.GetEntry(staticmeshexp).ObjectName.Instanced; - } - } + Items = ReadList(navListCount, i => MakeEntryNode(bin, $"{i}", Pcc)) + }); + + int numbersCount; + subnodes.Add(new BinInterpNode(bin.Position, + $"NavRefIndices: ({numbersCount = bin.ReadInt32()})") + { + Items = ReadList(numbersCount, i => MakeInt32Node(bin, $"{i}")) + }); } + } - smcanode.Header = $"{start:X4} [{smcaindex}] {objtext} {staticmesh}"; - smcanode.Offset = start; - subnodes.Add(smcanode); + int crossLevelActorsCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelActors?: ({crossLevelActorsCount = bin.ReadInt32()})") + { + Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}", Pcc)) + }); - //Read nodes - for (int i = 0; i < 16; i++) - { - float smcadata = BitConverter.ToSingle(data, start); - BinInterpNode node = new BinInterpNode - { - Tag = NodeType.StructLeafFloat, - Header = start.ToString("X4") - }; + if (Pcc.Game is MEGame.ME1 or MEGame.LE1) + { + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?", Pcc)); + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?", Pcc)); + } - //TODO: Figure out what the rest of these mean - string label = i.ToString(); - switch (i) + if (Pcc.Game >= MEGame.ME3) + { + bool bInitialized; + int samplesCount; + subnodes.Add(new BinInterpNode(bin.Position, "PrecomputedLightVolume") + { + Items = { - case 0: - label = "X1:"; - break; - case 1: - label = "X2: X-scaling-Axis: "; - break; - case 2: - label = "X3:"; - break; - case 3: - label = "XT:"; - break; - case 4: - label = "Y1: Y-scaling axis"; - break; - case 5: - label = "Y2:"; - break; - case 6: - label = "Y3:"; - break; - case 7: - label = "YT:"; - break; - case 8: - label = "Z1:"; - break; - case 9: - label = "Z2:"; - break; - case 10: - label = "Z3: Z-scaling axis"; - break; - case 11: - label = "ZT:"; - break; - case 12: - label = "LocX:"; - break; - case 13: - label = "LocY:"; - break; - case 14: - label = "LocZ:"; - break; - case 15: - label = "CameraCollisionDistanceScalar:"; - break; + new BinInterpNode(bin.Position, $"bInitialized: ({bInitialized = bin.ReadBoolInt()})"), + ListInitHelper.ConditionalAdd(bInitialized, () => new ITreeItem[] + { + MakeBoxNode(bin, "Bounds"), + MakeFloatNode(bin, "SampleSpacing"), + new BinInterpNode(bin.Position, $"Samples ({samplesCount = bin.ReadInt32()})") + { + Items = ReadList(samplesCount, i => new BinInterpNode(bin.Position, $"{i}") + { + Items = + { + MakeVectorNode(bin, "Position"), + MakeFloatNode(bin, "Radius"), + ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] + { + MakeByteNode(bin, "IndirectDirectionTheta"), + MakeByteNode(bin, "IndirectDirectionPhi"), + MakeByteNode(bin, "EnvironmentDirectionTheta"), + MakeByteNode(bin, "EnvironmentDirectionPhi"), + MakeColorNode(bin, "IndirectRadiance"), + MakeColorNode(bin, "EnvironmentRadiance"), + MakeColorNode(bin, "AmbientRadiance"), + MakeByteNode(bin, "bShadowedFromDominantLights"), + }, () => new [] + { + //SirCxyrtyx: This is a color, but is serialized as an FQuantizedSHVectorRGB, a vector of colored, quantized spherical harmonic coefficients. + //Conversion to ARGB is possible, but devilishly tricky. Let me know if this is something that's actually needed + new BinInterpNode(bin.Position, $"Ambient Radiance? : {bin.ReadToBuffer(39)}"){ Length = 39} + }) + } + }) + } + }) } + }); + } + if (Pcc.Game == MEGame.UDK) + { + BinInterpNode item = new BinInterpNode(bin.Position, "PrecomputedVisibilityHandler") + { + IsExpanded = true + }; + subnodes.Add(item); + item.Items.Add(MakeVector2DNode(bin, "PrecomputedVisibilityCellBucketOriginXY")); + item.Items.Add(MakeFloatNode(bin, "PrecomputedVisibilityCellSizeXY")); + item.Items.Add(MakeFloatNode(bin, "PrecomputedVisibilityCellSizeZ")); + item.Items.Add(MakeInt32Node(bin, "PrecomputedVisibilityCellBucketSizeXY")); + item.Items.Add(MakeInt32Node(bin, "PrecomputedVisibilityNumCellBuckets")); - node.Header += $" {label} {smcadata}"; - - //TODO: Lookup staticmeshcomponent so we can see what this actually is without changing to the export + item = new BinInterpNode(bin.Position, "PrecomputedVolumeDistanceField") + { + IsExpanded = true + }; - node.Offset = start; - smcanode.Items.Add(node); - start += 4; - } + subnodes.Add(item); + item.Items.Add(MakeFloatNode(bin, "VolumeMaxDistance")); + item.Items.Add(MakeBoxNode(bin, "VolumeBox")); + item.Items.Add(MakeInt32Node(bin, "VolumeSizeX")); + item.Items.Add(MakeInt32Node(bin, "VolumeSizeY")); + item.Items.Add(MakeInt32Node(bin, "VolumeSizeZ")); + item.Items.Add(MakeArrayNode(bin, "Data", x=> MakeColorNode(bin, $"Color[{x}]"))); + item.Items.Add(MakeInt32Node(bin, "UDKUnknown")); - smcaindex++; } - //topLevelTree.ItemsSource = subnodes; - binarystart = start; + + binarystart = (int)bin.Position; } catch (Exception ex) { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } + return subnodes; } - private List StartStaticLightCollectionActorScan(byte[] data, ref int binarystart) + + private List StartPrefabInstanceScan(byte[] data, ref int binarystart) { + /* + * count: 4 bytes + * Prefab ref : 4 bytes + * Level Object : 4 bytes + * 0: 4 bytes + * + */ var subnodes = new List(); - try + if (!CurrentLoadedExport.HasStack) { - //get a list of lightcomponents from the props. - var slcaitems = new List(); - var props = CurrentLoadedExport.GetProperty>("LightComponents"); + return subnodes; + } - foreach (var prop in props) + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + subnodes.Add(MakeArrayNode(bin, "ArchetypeToInstanceMap", i => new BinInterpNode(bin.Position, $"{i}") { - if (prop.Value > 0) - { - slcaitems.Add(CurrentLoadedExport.FileRef.GetEntry(prop.Value) as ExportEntry); - } - else + IsExpanded = true, + Items = { - slcaitems.Add(null); + MakeEntryNode(bin, "Archetype", Pcc), + MakeEntryNode(bin, "Instance", Pcc) } - } - - //find start of class binary (end of props) - int start = binarystart; - - //Lets make sure this binary is divisible by 64. - if ((data.Length - start) % 64 != 0) - { - subnodes.Add(new BinInterpNode - { - Tag = NodeType.Unknown, - Header = $"{start:X4} Binary data is not divisible by 64 ({data.Length - start})! SLCA binary data should be a length divisible by 64.", - Offset = start - }); - return subnodes; - } - - int slcaindex = 0; - while (start < data.Length && slcaindex < slcaitems.Count) + }, true)); + subnodes.Add(MakeArrayNode(bin, "PrefabInstance_ObjectMap", i => new BinInterpNode(bin.Position, $"{i}") { - BinInterpNode slcanode = new BinInterpNode - { - Tag = NodeType.Unknown - }; - ExportEntry assossiateddata = slcaitems[slcaindex]; - string objtext = "Null - unused data"; - if (assossiateddata != null) - { - objtext = $"[Export {assossiateddata.UIndex}] {assossiateddata.ObjectName.Instanced}"; - } - - slcanode.Header = $"{start:X4} [{slcaindex}] {objtext}"; - slcanode.Offset = start; - subnodes.Add(slcanode); - - //Read nodes - for (int i = 0; i < 16; i++) + IsExpanded = true, + Items = { - float slcadata = BitConverter.ToSingle(data, start); - BinInterpNode node = new BinInterpNode - { - Tag = NodeType.StructLeafFloat, - Header = start.ToString("X4") - }; - - //TODO: Figure out what the rest of these mean - string label = i.ToString(); - switch (i) - { - case 1: - label = "ScalingXorY1:"; - break; - case 12: - label = "LocX:"; - break; - case 13: - label = "LocY:"; - break; - case 14: - label = "LocZ:"; - break; - case 15: - label = "CameraLayerDistance?:"; - break; - } - - node.Header += $" {label} {slcadata}"; - - node.Offset = start; - slcanode.Items.Add(node); - start += 4; + MakeEntryNode(bin, "Object:", Pcc), + MakeInt32Node(bin, "int") } - - slcaindex++; - } - - binarystart = start; + }, true)); } catch (Exception ex) { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } return subnodes; } [Flags] - enum EBulkDataFlags + public enum EBulkDataFlags { BULKDATA_None = 0, BULKDATA_StoreInSeparateFile = 1 << 0, @@ -5571,52 +1492,6 @@ enum EBulkDataFlags } - - private List StartBioGestureRulesDataScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - - if (binarystart >= data.Length) - { - return subnodes; - } - - int pos = binarystart; - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - try - { - var count = bin.ReadInt32(); - bin.Position -= 4; // Set back so the node can be made - subnodes.Add(MakeInt32Node(bin, "Count")); - - for (int i = 0; i < count; i++) - { - var node = new BinInterpNode(bin.Position, $"Rule {i}"); - subnodes.Add(node); - - node.Items.Add(MakeNameNode(bin, "Name")); - - var subcount = bin.ReadInt32(); - var subnode = new BinInterpNode(bin.Position - 4, $"Num somethings: {subcount}"); - node.Items.Add(subnode); - - for (int j = 0; j < subcount; j++) - { - // Read name, some integer - subnode.Items.Add(MakeNameNode(bin, "SomeName")); - subnode.Items.Add(MakeInt32Node(bin, "SomeNum")); - } - } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); - } - - return subnodes; - } - private List StartGenericScan(byte[] data, ref int binarystart) { binarystart = ByteShiftUpDownValue.Value + binarystart; @@ -5708,48 +1583,5 @@ private List StartGenericScan(byte[] data, ref int binarystart) } return subnodes; } - - private List StartBioMorphFaceScan(byte[] data, ref int binarystart) - { - var subnodes = new List(); - try - { - var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; - bin.JumpTo(binarystart); - - subnodes.Add(MakeArrayNode(bin, "LOD Count:", k => new BinInterpNode(bin.Position, $"{k}") - { - Items = - { - MakeInt32Node(bin, "Size of Vector"), - MakeArrayNode(bin, $"LOD {k} Vertex Positional Data", n => new BinInterpNode(bin.Position, $"{n}") - { - Items = - { - MakeVectorNode(bin, "Position"), - } - }), - } - }, true)); - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; - } - - private static BinInterpNode MakeSHANode(EndianReader bin, string name, out string sha) - { - var shaBytes = bin.ReadBytes(20); - StringBuilder sb = new StringBuilder(); - foreach (var b in shaBytes) - { - sb.Append(b.ToString("x2")); - } - - sha = sb.ToString(); - return new BinInterpNode(bin.Position, $"{name}: {sha}") { Length = 20 }; - } } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml index 2f39f95805..494b961f34 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml @@ -233,6 +233,7 @@ + diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs index ec3ab91e34..0a81b5c528 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs @@ -161,6 +161,8 @@ public BinaryInterpreterWPF() : base("Binary Interpreter") public ICommand OpenInPackageEditorCommand { get; set; } public ICommand FindDefinitionOfImportCommand { get; set; } public ICommand CopyGuidCommand { get; set; } + + public ICommand GoToReferencedOffsetCommand { get; set; } private void LoadCommands() { @@ -169,6 +171,7 @@ private void LoadCommands() OpenInPackageEditorCommand = new GenericCommand(OpenInPackageEditor, IsSelectedItemAnObjectRef); FindDefinitionOfImportCommand = new GenericCommand(FindDefinitionOfImport, IsSelectedItemAnImportObjectRef); CopyGuidCommand = new GenericCommand(CopyGuid, IsSelectedItemAGuid); + GoToReferencedOffsetCommand = new GenericCommand(GoToReferencedOffset, IsSelectedItemAnOffsetRef); } private void CopyGuid() @@ -187,7 +190,6 @@ private bool IsSelectedItemAnObjectRef() { return BinaryInterpreter_TreeView.SelectedItem is BinInterpNode b && IsObjectNodeType(b); } - private bool IsSelectedItemAnImportObjectRef() { return BinaryInterpreter_TreeView.SelectedItem is BinInterpNode b && IsImportObjectNodeType(b); @@ -197,6 +199,19 @@ private bool IsSelectedItemAGuid() { return BinaryInterpreter_TreeView.SelectedItem is BinInterpNode { Tag: NodeType.Guid }; } + + private void GoToReferencedOffset() + { + if (BinaryInterpreter_TreeView.SelectedItem is BinInterpNodeOffsetReference { Tag: NodeType.ReferenceToOffset, OffsetTarget: >=0 } b) + { + AttemptSelectEntryWithOffset(TreeViewItems, b.OffsetTarget); + } + } + + private bool IsSelectedItemAnOffsetRef() + { + return BinaryInterpreter_TreeView.SelectedItem is BinInterpNode { Tag: NodeType.ReferenceToOffset }; + } private void FireNavigateCallback() { @@ -466,50 +481,6 @@ public override void LoadExport(ExportEntry exportEntry) } #region static stuff - public enum NodeType : sbyte - { - Unknown = -1, - StructProperty = 0, - IntProperty = 1, - FloatProperty = 2, - ObjectProperty = 3, - NameProperty = 4, - BoolProperty = 5, - ByteProperty = 6, - ArrayProperty = 7, - StrProperty = 8, - StringRefProperty = 9, - DelegateProperty = 10, - None, - BioMask4Property, - - ArrayLeafObject, - ArrayLeafName, - ArrayLeafEnum, - ArrayLeafStruct, - ArrayLeafBool, - ArrayLeafString, - ArrayLeafFloat, - ArrayLeafInt, - ArrayLeafByte, - - StructLeafByte, - StructLeafFloat, - StructLeafDeg, //indicates this is a StructProperty leaf that is in degrees (actually unreal rotation units) - StructLeafInt, - StructLeafObject, - StructLeafName, - StructLeafBool, - StructLeafStr, - StructLeafArray, - StructLeafEnum, - StructLeafStruct, - - // For right clicking things. - Guid, - - Root, - } #endregion @@ -686,7 +657,7 @@ private BinInterpNode PerformScanBackground(BinInterpNode topLevelTree, int bina subNodes.AddRange(Scan_WwiseStream(data)); break; case "WwiseBank": - subNodes.AddRange(Scan_WwiseBank(data)); + subNodes.AddRange(new WwiseBankScans().Scan_WwiseBank(data, CurrentLoadedExport)); break; case "WwiseEvent": subNodes.AddRange(Scan_WwiseEvent(data, ref binarystart)); @@ -932,6 +903,48 @@ private bool AttemptSelectPreviousEntry(IEnumerable subNodes) return false; } + private bool AttemptSelectEntryWithOffset(IEnumerable subNodes, int offset) + { + var binNodes = subNodes.OfType().OrderBy(o => o.Offset); + BinInterpNode closestOffset = null; + + foreach (BinInterpNode b in binNodes) + { + if (b.Offset == offset) + { + b.IsProgramaticallySelecting = true; + b.IsSelected = true; + return true; + } + + if(b.Offset > offset && closestOffset is not null) + { + break; + } + + if (b.Offset < offset) + { + closestOffset = b; + } + } + + if (closestOffset is null) return false; + + if (closestOffset.Items is null) // Handle offset being inside a leaf node with no children + { + closestOffset.IsProgramaticallySelecting = true; + closestOffset.IsSelected = true; + return true; + } + if(AttemptSelectEntryWithOffset(closestOffset.Items, offset)) + { + closestOffset.IsExpanded = true; + return true; + } + + return false; + } + internal void SetHexboxSelectedOffset(int v) { if (BinaryInterpreter_Hexbox != null) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs new file mode 100644 index 0000000000..61f32b22e5 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using System.Text; +using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorerCore.Gammtek.IO; +using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal; + +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public static class BinaryNodeFactory +{ + public static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; + + public static BinInterpNode MakeReverseBoolIntNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32() != 1}", NodeType.StructLeafBool) { Length = 4 }; + + public static BinInterpNode MakeBoolIntNode(EndianReader bin, string name, out bool boolVal) + { + return new BinInterpNode(bin.Position, $"{name}: {boolVal = bin.ReadBoolInt()}", NodeType.StructLeafBool) + { Length = 4 }; + } + + public static BinInterpNode MakeBoolByteNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolByte()}") { Length = 1 }; + + public static BinInterpNode MakeBoolByteNode(EndianReader bin, string name, out bool value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadBoolByte()}") { Length = 1 }; + + public static BinInterpNode MakeByteNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; + + public static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadByte()}") { Length = 1 }; + + public static BinInterpNode MakeSByteNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; + + public static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadSByte()}") { Length = 1 }; + + public static BinInterpNode MakeInt16Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; + + public static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt16()}") { Length = 2 }; + + public static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; + + public static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt16()}") { Length = 2 }; + + public static BinInterpNode MakeInt32Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; + + public static BinInterpNode MakeInt32Node(EndianReader bin, string name, out int value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; + + public static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; + + public static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt32()}") { Length = 4 }; + + public static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; + + public static BinInterpNode MakeInt64Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; + + public static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt64()}") { Length = 8 }; + + public static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; + + public static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt64()}") { Length = 8 }; + + public static BinInterpNode MakeFloatNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; + + public static BinInterpNode MakeFloatNode(EndianReader bin, string name, out float value) + { + return new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadFloat()}", NodeType.StructLeafFloat) + { Length = 4 }; + } + + public static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string name, bool create) + { + if (create) + { + return new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) + { Length = 4 }; + } + + return null; + } + + public static BinInterpNode MakeNameNode(EndianReader bin, string name, IMEPackage pcc) => new BinInterpNode(bin.Position, + $"{name}: {bin.ReadNameReference(pcc).Instanced}", NodeType.StructLeafName) { Length = 8 }; + + public static BinInterpNode MakeNameNode(EndianReader bin, string name, IMEPackage pcc, out NameReference nameRef) => + new BinInterpNode(bin.Position, $"{name}: {nameRef = bin.ReadNameReference(pcc).Instanced}", + NodeType.StructLeafName) { Length = 8 }; + + public static BinInterpNode MakeEntryNode(EndianReader bin, string name, IMEPackage pcc) + { + return new BinInterpNode(bin.Position, $"{name}: {MakeEntryNodeString(bin, pcc)}", NodeType.StructLeafObject) + { Length = 4 }; + } + + public static BinInterpNode MakeEntryNode(EndianReader bin, string name, IMEPackage pcc, out int uIndex) + { + return new BinInterpNode(bin.Position, $"{name}: {MakeEntryNodeString(bin, pcc, out uIndex)}", NodeType.StructLeafObject) { Length = 4 }; + } + + public static string MakeEntryNodeString(EndianReader bin, IMEPackage pcc) + { + var uIndex = bin.ReadInt32(); + return $"#{uIndex} {pcc.GetEntryString(uIndex)}"; + } + + public static string MakeEntryNodeString(EndianReader bin, IMEPackage pcc, out int uIndex) + { + uIndex = bin.ReadInt32(); + return $"#{uIndex} {pcc.GetEntryString(uIndex)}"; + } + + public static BinInterpNode MakeArrayNode(int count, EndianReader bin, string name, Func selector, bool isExpanded = false) + { + return new BinInterpNode(bin.Position, $"{name} ({count})") + { + IsExpanded = isExpanded, + Items = ReadList(count, selector) + }; + } + + public static BinInterpNode MakeArrayNode(EndianReader bin, string name, Func selector, bool isExpanded = false, + BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) + { + int count; + return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadInt32()})") + { + IsExpanded = isExpanded, + Items = ReadList(count, selector), + ArrayAddAlgorithm = arrayAddAlgo, + Length = 4 + }; + } + + public static BinInterpNode MakeArrayNodeByteCount(EndianReader bin, string name, Func selector, bool isExpanded = false, + BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) + { + int count; + return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadByte()})") + { + IsExpanded = isExpanded, + Items = ReadList(count, selector), + ArrayAddAlgorithm = arrayAddAlgo, + Length = 1 + }; + } + + public static BinInterpNode MakeArrayNodeInt16Count(EndianReader bin, string name, Func selector, bool isExpanded = false, + BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) + { + int count; + return new BinInterpNode(bin.Position, $"{name} ({count = bin.ReadInt16()})") + { + IsExpanded = isExpanded, + Items = ReadList(count, selector), + ArrayAddAlgorithm = arrayAddAlgo, + Length = 2 + }; + } + + public static BinInterpNode MakeByteArrayNode(EndianReader bin, string name) + { + int pos = (int)bin.Position; + int count = bin.ReadInt32(); + bin.Skip(count); + return new BinInterpNode(pos, $"{name} ({count} bytes)"); + } + + public static BinInterpNode MakePackedNormalNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, + $"{name}: (X: {bin.ReadByte() / 127.5f - 1}, Y: {bin.ReadByte() / 127.5f - 1}, Z: {bin.ReadByte() / 127.5f - 1}, W: {bin.ReadByte() / 127.5f - 1})") + { + Length = 4 + }; + + public static BinInterpNode MakeVectorNodeEditable(EndianReader bin, string name, bool expanded = false) + { + var node = new BinInterpNode(bin.Position, + $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()})") { Length = 12 }; + bin.Position -= 12; + node.Items.Add(MakeFloatNode(bin, "X")); + node.Items.Add(MakeFloatNode(bin, "Y")); + node.Items.Add(MakeFloatNode(bin, "Z")); + node.IsExpanded = expanded; + return node; + } + + public static BinInterpNode MakeVector2DNodeEditable(EndianReader bin, string name, bool expanded = false) + { + var node = new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") + { Length = 8 }; + bin.Position -= 8; + node.Items.Add(MakeFloatNode(bin, "X")); + node.Items.Add(MakeFloatNode(bin, "Y")); + node.IsExpanded = expanded; + return node; + } + + public static BinInterpNode MakeVectorNode(EndianReader bin, string name) + { + var node = new BinInterpNode(bin.Position, + $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()})") { Length = 12 }; + bin.Position -= 12; + node.Items.Add(MakeFloatNode(bin, "X")); + node.Items.Add(MakeFloatNode(bin, "Y")); + node.Items.Add(MakeFloatNode(bin, "Z")); + return node; + } + + public static BinInterpNode MakeQuatNode(EndianReader bin, string name) + { + var node = new BinInterpNode(bin.Position, + $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()}, Z: {bin.ReadFloat()}, W: {bin.ReadFloat()})") + { Length = 16 }; + bin.Position -= 16; + node.Items.Add(MakeFloatNode(bin, "X")); + node.Items.Add(MakeFloatNode(bin, "Y")); + node.Items.Add(MakeFloatNode(bin, "Z")); + node.Items.Add(MakeFloatNode(bin, "W")); + return node; + } + + public static BinInterpNode MakeRotatorNode(EndianReader bin, string name) + { + var node = new BinInterpNode(bin.Position, + $"{name}: (Pitch: {bin.ReadInt32()}, Yaw: {bin.ReadInt32()}, Roll: {bin.ReadInt32()})") { Length = 12 }; + bin.Position -= 12; + node.Items.Add(MakeInt32Node(bin, "Pitch")); + node.Items.Add(MakeInt32Node(bin, "Yaw")); + node.Items.Add(MakeInt32Node(bin, "Roll")); + return node; + } + + public static BinInterpNode MakeBoxNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, name) + { + IsExpanded = true, + Items = + { + MakeVectorNode(bin, "Min"), + MakeVectorNode(bin, "Max"), + new BinInterpNode(bin.Position, $"IsValid: {bin.ReadBoolByte()}") + }, + Length = 25 + }; + + public static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; + + public static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat16()}, Y: {bin.ReadFloat16()})") { Length = 4 }; + + public static BinInterpNode MakeColorNode(EndianReader bin, string name) + { + return new BinInterpNode(bin.Position, $"{name}") + { + Length = 4, + Items = + { + new BinInterpNode(bin.Position, $"B: {bin.ReadByte()}"), + new BinInterpNode(bin.Position, $"G: {bin.ReadByte()}"), + new BinInterpNode(bin.Position, $"R: {bin.ReadByte()}"), + new BinInterpNode(bin.Position, $"A: {bin.ReadByte()}"), + } + }; + } + + public static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) + { + return new BinInterpNode(bin.Position, $"{name}") + { + Items = + { + MakeVectorNode(bin, "Origin"), + MakeVectorNode(bin, "BoxExtent"), + MakeFloatNode(bin, "SphereRadius") + } + }; + } + + public static BinInterpNode MakeGuidNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadGuid()}", NodeType.Guid) { Length = 16 }; + + public static BinInterpNode MakeMaterialGuidNode(EndianReader bin, string name, + Dictionary materialGuidMap = null) + { + var guid = bin.ReadGuid(); + var node = new BinInterpNode(bin.Position - 16, $"{name}: {guid}") { Length = 16 }; + +#if DEBUG + if (materialGuidMap != null && materialGuidMap.TryGetValue(guid, out var matName)) + { + node.Header += " " + matName; + } +#endif + + node.Tag = NodeType.Guid; + return node; + } + + public static BinInterpNode MakeStringNode(EndianReader bin, string nodeName, MEGame game) + { + int pos = (int)bin.Position; + int strLen = bin.ReadInt32(); + string str; + if (game is MEGame.ME3 or MEGame.LE3) + { + strLen *= -2; + str = bin.BaseStream.ReadStringUnicodeNull(strLen); + } + else + { + str = bin.BaseStream.ReadStringLatin1Null(strLen); + } + + return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; + } + + public static BinInterpNode MakeStringUTF8Node(EndianReader bin, string nodeName) + { + int pos = (int)bin.Position; + int strLen = bin.ReadInt32(); + string str = bin.BaseStream.ReadStringUtf8(strLen); + return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; + } + + public static BinInterpNode MakeSHANode(EndianReader bin, string name, out string sha) + { + var shaBytes = bin.ReadBytes(20); + StringBuilder sb = new StringBuilder(); + foreach (var b in shaBytes) + { + sb.Append(b.ToString("x2")); + } + + sha = sb.ToString(); + return new BinInterpNode(bin.Position, $"{name}: {sha}") { Length = 20 }; + } + + public static List ReadList(int count, Func selector) + { + //sanity check. if this number is too small, feel free to increase + if (count > 5097152) + { + throw new Exception($"Is this actually a list? {count} seems like an incorrect count"); + } + var list = new List(); + try + { + for (int i = 0; i < count; i++) + { + list.Add(selector(i)); + } + } + catch (Exception ex) + { + list.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return list; + } + + public static BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T : Enum + { + var value = bin.ReadByte(); + var parsedValue = Enum.GetName(typeof(T), value); + if (string.IsNullOrEmpty(parsedValue)) parsedValue = "None"; + return new BinInterpNode(bin.Position - 1, $"{name}: {parsedValue}") { Length = 1 }; + } + + public static BinInterpNode MakeUInt32EnumNode(EndianReader bin, string name) where T : Enum + { + var value = bin.ReadUInt32(); + var parsedValue = Enum.GetName(typeof(T), value); + if (string.IsNullOrEmpty(parsedValue)) parsedValue = "None"; + return new BinInterpNode(bin.Position - 4, $"{name}: {parsedValue}") { Length = 4 }; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs new file mode 100644 index 0000000000..bae06e0154 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs @@ -0,0 +1,2023 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorer.Tools.TlkManagerNS; +using LegendaryExplorerCore.Gammtek.IO; +using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal; +using LegendaryExplorerCore.Unreal.Classes; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; + +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public partial class BinaryInterpreterWPF +{ + private List StartBioPawnScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + int count; + subnodes.Add(new BinInterpNode(bin.Position, $"AnimationMap? ({count = bin.ReadInt32()})") + { + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc)}: {MakeEntryNodeString(bin, Pcc)}", NodeType.StructLeafObject) { Length = 4 }) + }); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartSFXMorphFaceFrontEndDataSourceScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + subnodes.Add(MakeArrayNode(bin, "DefaultSettingsNames", i => new BinInterpNode(bin.Position, $"{i}") + { + IsExpanded = true, + Items = + { + MakeStringNode(bin, "Name", Pcc.Game), + MakeInt32Node(bin, "Index") + } + }, true)); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartBioCreatureSoundSetScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + subnodes.Add(MakeArrayNode(bin, "UnkToCueMap?", i => new BinInterpNode(bin.Position, $"{i}") + { + IsExpanded = true, + Items = + { + MakeByteNode(bin, "Unknown byte"), + MakeInt32Node(bin, "index into m_aAllCues?") + } + }, true)); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartBioTlkFileSetScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + int offset = binarystart; + if (data.Length > binarystart) + { + int count = BitConverter.ToInt32(data, offset); + subnodes.Add(new BinInterpNode + { + Header = $"0x{offset:X4} Count: {count}", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + //offset += 4; + //offset += 8; //skip 8 + + for (int i = 0; i < count; i++) + { + int langRef = BitConverter.ToInt32(data, offset); + int langTlkCount = BitConverter.ToInt32(data, offset + 8); + var languageNode = new BinInterpNode + { + Header = $"0x{offset:X4} {CurrentLoadedExport.FileRef.GetNameEntry(langRef)} - {langTlkCount} entries", + Offset = offset, + Tag = NodeType.StructLeafName, + IsExpanded = true + }; + subnodes.Add(languageNode); + offset += 12; + + for (int k = 0; k < langTlkCount; k++) + { + int tlkIndex = BitConverter.ToInt32(data, offset); //-1 in reader + languageNode.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X4} TLK #{k} export: {tlkIndex} {CurrentLoadedExport.FileRef.GetEntryString(tlkIndex)}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + } + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioStateEventMapScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + int offset = binarystart; + + int eCount = BitConverter.ToInt32(data, offset); + var EventCountNode = new BinInterpNode + { + Header = $"0x{offset:X4} State Event Count: {eCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(EventCountNode); + + for (int e = 0; e < eCount; e++) //EVENTS + { + int iEventID = BitConverter.ToInt32(data, offset); //EVENT ID + var EventIDs = new BinInterpNode + { + Header = $"0x{offset:X5} [{e}] State Transition ID: {iEventID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + EventCountNode.Items.Add(EventIDs); + + int EventMapInstVer = BitConverter.ToInt32(data, offset); //Event Instance Version + EventIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {EventMapInstVer} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int nTransitions = BitConverter.ToInt32(data, offset); //Count of State Events + var TransitionsIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Transitions: {nTransitions} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + EventIDs.Items.Add(TransitionsIDs); + + for (int t = 0; t < nTransitions; t++) //TRANSITIONS + { + int transTYPE = BitConverter.ToInt32(data, offset); //Get TYPE + if (transTYPE == 0) // TYPE 0 = BOOL STATE EVENT + { + offset += 8; + int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot + offset -= 8; + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Transition on Bool {tPlotID}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + tPlotID = BitConverter.ToInt32(data, offset); //Plot + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Plot ID: {tPlotID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tNewValue = BitConverter.ToInt32(data, offset); //NewValue + bool bNewValue = false; + if (tNewValue == 1) { bNewValue = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} New Value: {tNewValue} {bNewValue} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool + bool bUseParam = false; + if (tUseParam == 1) { bUseParam = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (transTYPE == 1) //TYPE 1 = CONSEQUENCE + { + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Consequence", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tConsequenceParam = BitConverter.ToInt32(data, offset); //Consequence parameter + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Consequence Parameter: {tConsequenceParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (transTYPE == 2) // TYPE 2 = FLOAT TRANSITION + { + offset += 8; + int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot + offset -= 8; + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} transition on Float {tPlotID}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + if (Pcc.Game.IsGame2()) + { + int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool + bool bIncrement = false; + if (tIncrement == 1) { bIncrement = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + + tPlotID = BitConverter.ToInt32(data, offset); //Plot + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Plot ID: {tPlotID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + float tNewValue = BitConverter.ToInt32(data, offset); //NewValue + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} New Value: {tNewValue} ", + Offset = offset, + Tag = NodeType.StructLeafFloat + }); + offset += 4; + + int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool + bool bUseParam = false; + if (tUseParam == 1) { bUseParam = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + if (!Pcc.Game.IsGame2()) + { + int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool + bool bIncrement = false; + if (tIncrement == 1) { bIncrement = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + } + else if (transTYPE == 3) // TYPE 3 = FUNCTION + { + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Function", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int PackageName = BitConverter.ToInt32(data, offset); //Package name + offset += 4; + int PackageIdx = BitConverter.ToInt32(data, offset); //Package name idx + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Package Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(PackageName), PackageIdx).Instanced}", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 4; + + int ClassName = BitConverter.ToInt32(data, offset); //Class name + offset += 4; + int ClassIdx = BitConverter.ToInt32(data, offset); //Class name idx + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Class Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(ClassName), ClassIdx).Instanced}", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 4; + + int FunctionName = BitConverter.ToInt32(data, offset); //Function name + offset += 4; + int FunctionIdx = BitConverter.ToInt32(data, offset); //Function name idx + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Function Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(FunctionName), FunctionIdx).Instanced}", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 4; + + int Parameter = BitConverter.ToInt32(data, offset); //Parameter + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Parameter: {Parameter} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (transTYPE == 4) // TYPE 4 = INT TRANSITION + { + offset += 8; + int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot + offset -= 8; + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} transition on INT {tPlotID}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + tPlotID = BitConverter.ToInt32(data, offset); //Plot + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Plot ID: {tPlotID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tNewValue = BitConverter.ToInt32(data, offset); //NewValue + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} New Value: {tNewValue} ", + Offset = offset, + Tag = NodeType.StructLeafFloat + }); + offset += 4; + + int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool + bool bUseParam = false; + if (tUseParam == 1) { bUseParam = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tIncrement = BitConverter.ToInt32(data, offset); //Increment bool + bool bIncrement = false; + if (tIncrement == 1) { bIncrement = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Increment value: {tIncrement} {bIncrement} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (transTYPE == 5) // TYPE 5 = LOCAL BOOL + { + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Local Bool", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 36; + TransitionsIDs.Items.Add(nTransition); + } + else if (transTYPE == 6) // TYPE 6 = LOCAL FLOAT + { + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Local Float", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 36; + TransitionsIDs.Items.Add(nTransition); + } + else if (transTYPE == 7) // TYPE 7 = LOCAL INT + { + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Local Int", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tObjtag = BitConverter.ToInt32(data, offset); //Use Object tag?? + bool bObjtag = false; + if (tObjtag == 1) { bObjtag = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Object Tag: {tObjtag} {bObjtag} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int FunctionName = BitConverter.ToInt32(data, offset); //Function name + offset += 4; + int FunctionIdx = BitConverter.ToInt32(data, offset); //Function name + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Function Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(FunctionName), FunctionIdx).Instanced}", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 4; + + int TagName = BitConverter.ToInt32(data, offset); //Object name + offset += 4; + int TagIdx = BitConverter.ToInt32(data, offset); //Object idx + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Object Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(TagName), TagIdx).Instanced}", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 4; + + int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool + bool bUseParam = false; + if (tUseParam == 1) { bUseParam = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tNewValue = BitConverter.ToInt32(data, offset); //NewValue + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} New Value: {tNewValue} ", + Offset = offset, + Tag = NodeType.StructLeafFloat + }); + offset += 4; + } + else if (transTYPE == 8) // TYPE 8 = SUBSTATE + { + offset += 8; + int tPlotID = BitConverter.ToInt32(data, offset); //Get Plot + offset -= 8; + var nTransition = new BinInterpNode + { + Header = $"0x{offset:X5} Type: {transTYPE} Substate Transition on Bool {tPlotID}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + TransitionsIDs.Items.Add(nTransition); + + int TransInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {TransInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + tPlotID = BitConverter.ToInt32(data, offset); //Plot + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Plot ID: {tPlotID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tNewValue = BitConverter.ToInt32(data, offset); //NewState Bool + bool bNewValue = false; + if (tNewValue == 1) { bNewValue = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} New State: {tNewValue} {bNewValue}", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tUseParam = BitConverter.ToInt32(data, offset); //Use Parameter bool + bool bUseParam = false; + if (tUseParam == 1) { bUseParam = true; } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Use parameter: {tUseParam} {bUseParam} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tParentType = BitConverter.ToInt32(data, offset); //Parent OR type flag + bool bParentType = false; + string sParentType = "ALL of siblings TRUE => Parent TRUE"; + if (tParentType == 1) + { + bParentType = true; + sParentType = "ANY of siblings TRUE => Parent TRUE"; + } + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Parent OR type: {tParentType} {bParentType} {sParentType}", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int ParentIdx = BitConverter.ToInt32(data, offset); //Parent Bool + nTransition.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Parent Bool: {ParentIdx} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int sibCount = BitConverter.ToInt32(data, offset); //Sibling Substates + var SiblingIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Sibling Substates Count: {sibCount} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + nTransition.Items.Add(SiblingIDs); + + for (int s = 0; s < sibCount; s++) //SIBLING SUBSTATE BOOLS + { + int nSibling = BitConverter.ToInt32(data, offset); + var nSiblings = new BinInterpNode + { + Header = $"0x{offset:X5} Sibling: {s} Bool: {nSibling}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + SiblingIDs.Items.Add(nSiblings); + offset += 4; + } + } + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioQuestMapScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + var game = CurrentLoadedExport.FileRef.Game; + try + { + int offset = binarystart; + + int qCount = BitConverter.ToInt32(data, offset); + var QuestNode = new BinInterpNode + { + Header = $"0x{offset:X4} Quest Count: {qCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(QuestNode); + + for (int i = 0; i < qCount; i++) //QUESTS + { + int iQuestID = BitConverter.ToInt32(data, offset); //QUEST ID + var QuestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Quest ID: {iQuestID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + QuestNode.Items.Add(QuestIDs); + + int instanceVersion = BitConverter.ToInt32(data, offset); //Unknown1 + QuestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} InstanceVersion: {instanceVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int isMission = BitConverter.ToInt32(data, offset); //Unknown2 + bool isMissionB = isMission == 1; + QuestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} IsMission: {isMission} {isMissionB} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int gCount = BitConverter.ToInt32(data, offset); //Goal Count + var GoalsIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Goals: {gCount} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + QuestIDs.Items.Add(GoalsIDs); + + for (int g = 0; g < gCount; g++) //GOALS + { + //Add either state or Conditional as starting node + offset += 12; + int gConditional = BitConverter.ToInt32(data, offset); //Conditional + offset += 4; + int gState = BitConverter.ToInt32(data, offset); //State + offset -= 16; + int goalStart = gState; + string startType = "Bool"; + if (gState == -1) + { + goalStart = gConditional; + startType = "Conditional"; + } + var nGoalIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Goal start plot/cnd: {goalStart} {startType}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + GoalsIDs.Items.Add(nGoalIDs); + + int iGoalInstVersion = BitConverter.ToInt32(data, offset); //Goal Instance Version + nGoalIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Goal Instance Version: {iGoalInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int gTitle = BitConverter.ToInt32(data, offset); //Goal Name + string gttlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(gTitle, game, CurrentLoadedExport.FileRef); + nGoalIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Goal Name StrRef: {gTitle} {gttlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int gDescription = BitConverter.ToInt32(data, offset); //Goal Description + string gdtlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(gDescription, game, CurrentLoadedExport.FileRef); + nGoalIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Goal Description StrRef: {gDescription} {gdtlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + gConditional = BitConverter.ToInt32(data, offset); //Conditional + nGoalIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Conditional: {gConditional} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + gState = BitConverter.ToInt32(data, offset); //State + nGoalIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Bool State: {gState} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + + int tCount = BitConverter.ToInt32(data, offset); //Task Count + var TaskIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Tasks Count: {tCount} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + QuestIDs.Items.Add(TaskIDs); + + for (int t = 0; t < tCount; t++) //TASKS + { + var nTaskIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Task: {t}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + TaskIDs.Items.Add(nTaskIDs); + + int iTaskInstVersion = BitConverter.ToInt32(data, offset); //Task Instance Version + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Task Instance Version: {iTaskInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int tFinish = BitConverter.ToInt32(data, offset); //Primary Codex + bool bFinish = tFinish == 1; + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Task Finishes Quest: {tFinish} {bFinish}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int tTitle = BitConverter.ToInt32(data, offset); //Task Name + string tttlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(tTitle, game, CurrentLoadedExport.FileRef); + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Task Name StrRef: {tTitle} {tttlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int tDescription = BitConverter.ToInt32(data, offset); //Task Description + string tdtlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(tDescription, game, CurrentLoadedExport.FileRef); + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Task Description StrRef: {tDescription} {tdtlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int piCount = BitConverter.ToInt32(data, offset); //Plot item Count + var PlotIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Plot Item Count: {piCount} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + nTaskIDs.Items.Add(PlotIDs); + + for (int pi = 0; pi < piCount; pi++) //TASK PLOT ITEMS + { + int iPlotItem = BitConverter.ToInt32(data, offset); //Plot item index + var nPlotItems = new BinInterpNode + { + Header = $"0x{offset:X5} Plot items: {pi} Index: {iPlotItem}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + PlotIDs.Items.Add(nPlotItems); + offset += 4; + } + + int planetName = BitConverter.ToInt32(data, offset); //Planet name + offset += 4; + int planetIdx = BitConverter.ToInt32(data, offset); //Name index + offset -= 4; + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Planet Name: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(planetName), planetIdx).Instanced} ", + Offset = offset, + Tag = NodeType.StructLeafName + }); + offset += 8; + + int wpStrLgth = BitConverter.ToInt32(data, offset); //String length for waypoint + offset += 4; + string wpRef = "No Waypoint data"; + if (wpStrLgth > 0) + { + //offset += 1; + MemoryStream ms = new MemoryStream(data); + ms.Position = offset; + wpRef = ms.ReadStringLatin1Null(wpStrLgth); + } + nTaskIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Waypoint ref: {wpRef} ", + Offset = offset, + Tag = NodeType.StructLeafStr + }); + offset += wpStrLgth; + } + + int pCount = BitConverter.ToInt32(data, offset); //Plot Item Count + var PlotItemIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Plot Items: {pCount} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + QuestIDs.Items.Add(PlotItemIDs); + + for (int p = 0; p < pCount; p++) //PLOT ITEM + { + //Add count starting node + var nPlotItemIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Plot Item: {p} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + PlotItemIDs.Items.Add(nPlotItemIDs); + + int iPlotInstVersion = BitConverter.ToInt32(data, offset); //Plot Item Instance Version + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Plot item Instance Version: {iPlotInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pTitle = BitConverter.ToInt32(data, offset); //Plot item Name + string pitlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(pTitle, game, CurrentLoadedExport.FileRef); + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Goal Name StrRef: {pTitle} {pitlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int pIcon = BitConverter.ToInt32(data, offset); //Icon Index + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Icon Index: {pIcon} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pConditional = BitConverter.ToInt32(data, offset); //Conditional + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Conditional: {pConditional} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pState = BitConverter.ToInt32(data, offset); //Int + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Integer State: {pState} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pTarget = BitConverter.ToInt32(data, offset); //Target Index + nPlotItemIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Item Count Target: {pTarget} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + } + + int bsCount = BitConverter.ToInt32(data, offset); + var bsNode = new BinInterpNode + { + Header = $"0x{offset:X4} Bool Journal Events: {bsCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(bsNode); + + for (int b = 0; b < bsCount; b++) + { + int iBoolEvtID = BitConverter.ToInt32(data, offset); //BOOL STATE ID + var BoolEvtIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Bool Journal Event: {iBoolEvtID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + bsNode.Items.Add(BoolEvtIDs); + + int bsInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + var BoolQuestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {bsInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + BoolEvtIDs.Items.Add(BoolQuestIDs); + + int bqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count + var bqstNode = new BinInterpNode + { + Header = $"0x{offset:X4} Related Quests: {bqstCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + BoolQuestIDs.Items.Add(bqstNode); + + for (int bq = 0; bq < bqstCount; bq++) //Related Quests + { + offset += 16; + int bqQuest = BitConverter.ToInt32(data, offset); //Bool quest ID + var bquestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Related Quest: {bqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset -= 16; + bqstNode.Items.Add(bquestIDs); + + int bqInstVersion = BitConverter.ToInt32(data, offset); //Bool quest Instance Version + bquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {bqInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int bqTask = BitConverter.ToInt32(data, offset); //Bool quest Instance Version + bquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Related Task Link: {bqTask} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int bqConditional = BitConverter.ToInt32(data, offset); //Bool quest Conditional + bquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Conditional: {bqConditional} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int bqState = BitConverter.ToInt32(data, offset); //Bool quest State + bquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Bool State: {bqState} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + bqQuest = BitConverter.ToInt32(data, offset); //Bool quest ID + bquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Quest Link: {bqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + } + + int isCount = BitConverter.ToInt32(data, offset); + var isNode = new BinInterpNode + { + Header = $"0x{offset:X4} Int Journal Events: {isCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(isNode); + + for (int iEvt = 0; iEvt < isCount; iEvt++) //INTEGER STATE EVENTS + { + int iInttEvtID = BitConverter.ToInt32(data, offset); + var IntEvtIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Int Journal Event: {iInttEvtID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + isNode.Items.Add(IntEvtIDs); + + int isInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + var IntQuestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {isInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + IntEvtIDs.Items.Add(IntQuestIDs); + + int iqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count + var iqstNode = new BinInterpNode + { + Header = $"0x{offset:X4} Related Quests: {iqstCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + IntQuestIDs.Items.Add(iqstNode); + + for (int iq = 0; iq < iqstCount; iq++) //Related Quests + { + offset += 16; + int iqQuest = BitConverter.ToInt32(data, offset); //int quest ID + var iquestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Related Quest: {iqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset -= 16; + iqstNode.Items.Add(iquestIDs); + + int iqInstVersion = BitConverter.ToInt32(data, offset); //Int quest Instance Version + iquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {iqInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int iqTask = BitConverter.ToInt32(data, offset); //Int quest Instance Version + iquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Related Task Link: {iqTask} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int iqConditional = BitConverter.ToInt32(data, offset); //Int quest Conditional + iquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Conditional: {iqConditional} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int iqState = BitConverter.ToInt32(data, offset); //Int quest State + iquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Bool State: {iqState} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + iqQuest = BitConverter.ToInt32(data, offset); //Int quest ID + iquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Quest Link: {iqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + } + + int fsCount = BitConverter.ToInt32(data, offset); + var fsNode = new BinInterpNode + { + Header = $"0x{offset:X4} Float Journal Events: {fsCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(fsNode); + + for (int f = 0; f < fsCount; f++) //FLOAT STATE EVENTS + { + int iFloatEvtID = BitConverter.ToInt32(data, offset); //FLOAT STATE ID + var FloatEvtIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Float Journal Event: {iFloatEvtID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + fsNode.Items.Add(FloatEvtIDs); + + int fsInstVersion = BitConverter.ToInt32(data, offset); //Instance Version + var FloatQuestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {fsInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + FloatEvtIDs.Items.Add(FloatQuestIDs); + + int fqstCount = BitConverter.ToInt32(data, offset); //Related Quests Count + var fqstNode = new BinInterpNode + { + Header = $"0x{offset:X4} Related Quests: {fqstCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + FloatQuestIDs.Items.Add(fqstNode); + + for (int fq = 0; fq < fqstCount; fq++) //Related Quests + { + offset += 16; + int fqQuest = BitConverter.ToInt32(data, offset); //float quest ID + var fquestIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Related Quest: {fqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset -= 16; + fqstNode.Items.Add(fquestIDs); + + int fqInstVersion = BitConverter.ToInt32(data, offset); //float quest Instance Version + fquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {fqInstVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int fqTask = BitConverter.ToInt32(data, offset); //Float quest Instance Version + fquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Related Task Link: {fqTask} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int fqConditional = BitConverter.ToInt32(data, offset); //Float quest Conditional + fquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Conditional: {fqConditional} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int fqState = BitConverter.ToInt32(data, offset); //Float quest State + fquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Bool State: {fqState} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + fqQuest = BitConverter.ToInt32(data, offset); //Float quest ID + fquestIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Quest Link: {fqQuest} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioCodexMapScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + var game = CurrentLoadedExport.FileRef.Game; + try + { + int offset = binarystart; + + int sCount = BitConverter.ToInt32(data, offset); + var SectionsNode = new BinInterpNode + { + Header = $"0x{offset:X4} Codex Section Count: {sCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(SectionsNode); + + for (int i = 0; i < sCount; i++) + { + int iSectionID = BitConverter.ToInt32(data, offset); //Section ID + var SectionIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Section ID: {iSectionID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + SectionsNode.Items.Add(SectionIDs); + + int instVersion = BitConverter.ToInt32(data, offset); //Instance Version + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {instVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int sTitle = BitConverter.ToInt32(data, offset); //Codex Title + string ttlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(sTitle, game, CurrentLoadedExport.FileRef); + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Title StrRef: {sTitle} {ttlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int sDescription = BitConverter.ToInt32(data, offset); //Codex Description + string dtlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(sDescription, game, CurrentLoadedExport.FileRef); + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Description StrRef: {sDescription} {dtlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int sTexture = BitConverter.ToInt32(data, offset); //Texture ID + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Texture ID: {sTexture} ", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int sPriority = BitConverter.ToInt32(data, offset); //Priority + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Priority: {sPriority} (5 is low, 1 is high)", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + if (instVersion >= 3) + { + int sndExport = BitConverter.ToInt32(data, offset); + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X8} Codex Sound: {sndExport} {CurrentLoadedExport.FileRef.GetEntryString(sndExport)}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + } + + int sPrimary = BitConverter.ToInt32(data, offset); //Primary Codex + bool bPrimary = false; + if (sPrimary == 1) { bPrimary = true; } + SectionIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Is Primary Codex: {sPrimary} {bPrimary}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + } + //START OF CODEX PAGES SECTION + int pCount = BitConverter.ToInt32(data, offset); + var PagesNode = new BinInterpNode + { + Header = $"0x{offset:X4} Codex Page Count: {pCount}", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + subnodes.Add(PagesNode); + + for (int i = 0; i < pCount; i++) + { + int iPageID = BitConverter.ToInt32(data, offset); //Page ID + var PageIDs = new BinInterpNode + { + Header = $"0x{offset:X5} Page Bool: {iPageID} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }; + offset += 4; + PagesNode.Items.Add(PageIDs); + + int instVersion = BitConverter.ToInt32(data, offset); //Instance Version + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Instance Version: {instVersion} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pTitle = BitConverter.ToInt32(data, offset); //Codex Title + string ttlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(pTitle, game, CurrentLoadedExport.FileRef); + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Page Title StrRef: {pTitle} {ttlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pDescription = BitConverter.ToInt32(data, offset); //Codex Description + string dtlkLookup = TLKManagerWPF.GlobalFindStrRefbyID(pDescription, game, CurrentLoadedExport.FileRef); + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Page Description StrRef: {pDescription} {dtlkLookup}", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pTexture = BitConverter.ToInt32(data, offset); //Texture ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Texture ID: {pTexture} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pPriority = BitConverter.ToInt32(data, offset); //Priority + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Priority: {pPriority} (5 is low, 1 is high)", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + if (instVersion == 4) //ME3 use object reference found sound then section + { + int sndExport = BitConverter.ToInt32(data, offset); + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X8} Codex Sound: {sndExport} {CurrentLoadedExport.FileRef.GetEntryString(sndExport)}", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + + int pSection = BitConverter.ToInt32(data, offset); //Section ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Reference: {pSection} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (instVersion == 3 && Pcc.Game != MEGame.LE1) //ME2 use Section then no sound reference //LE1 uses something else... + { + int pSection = BitConverter.ToInt32(data, offset); //Section ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Reference: {pSection} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + } + else if (instVersion == 3 && Pcc.Game == MEGame.LE1) + { + int unkSection = BitConverter.ToInt32(data, offset); //Unknown ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Unknown Int: {unkSection} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int pSection = BitConverter.ToInt32(data, offset); //Section ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Reference: {pSection} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int sndStrLgth = BitConverter.ToInt32(data, offset); //String length for sound + string sndRef = "No sound data"; + if (sndStrLgth > 0) + { + MemoryStream ms = new MemoryStream(data); + ms.Position = offset + 4; + sndRef = ms.ReadStringLatin1Null(sndStrLgth); + } + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} SoundRef String: {sndRef} ", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += 4; + offset += sndStrLgth; + } + else //ME1 has different order (section ID then codex sound) and uses a string reference. + { + int pSection = BitConverter.ToInt32(data, offset); //Section ID + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} Section Reference: {pSection} ", + Offset = offset, + Tag = NodeType.StructLeafInt + }); + offset += 4; + + int sndStrLgth = BitConverter.ToInt32(data, offset); //String length for sound + offset += 4; + string sndRef = "No sound data"; + if (sndStrLgth > 0) + { + MemoryStream ms = new MemoryStream(data); + ms.Position = offset; + sndRef = ms.ReadStringLatin1Null(sndStrLgth); + } + PageIDs.Items.Add(new BinInterpNode + { + Header = $"0x{offset:X5} SoundRef String: {sndRef} ", + Offset = offset, + Tag = NodeType.StructLeafObject + }); + offset += sndStrLgth; + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioGestureRuntimeDataScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + subnodes.Add(MakeArrayNode(bin, "m_mapAnimSetOwners", i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc)} => {bin.ReadNameReference(Pcc)}") + { + Length = 16 + })); + + int count; + if (CurrentLoadedExport.Game.IsGame1()) + { + var propDataNode = new BinInterpNode(bin.Position, $"m_mapCharTypeOverrides ({count = bin.ReadInt32()} items)"); + subnodes.Add(propDataNode); + for (int i = 0; i < count; i++) + { + propDataNode.Items.Add(new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) + { + Length = 8, + IsExpanded = true, + Items = + { + MakeNameNode(bin, "nm_Female", Pcc), + MakeNameNode(bin, "nm_Asari", Pcc), + MakeNameNode(bin, "nm_Turian", Pcc), + MakeNameNode(bin, "nm_Salarian", Pcc), + MakeNameNode(bin, "nm_Quarian", Pcc), + MakeNameNode(bin, "nm_Other", Pcc), + MakeNameNode(bin, "nm_Krogan", Pcc), + MakeNameNode(bin, "nm_Geth", Pcc), + MakeNameNode(bin, "nm_Other_Artificial", Pcc) + } + }); + } + } + else + { + var propDataNode = new BinInterpNode(bin.Position, $"m_mapMeshProps ({count = bin.ReadInt32()} items)"); + subnodes.Add(propDataNode); + for (int i = 0; i < count; i++) + { + BinInterpNode node = new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) + { + Length = 8 + }; + propDataNode.Items.Add(node); + node.Items.Add(MakeNameNode(bin, "nmPropName", Pcc)); + node.Items.Add(MakeStringNode(bin, "sMesh", Pcc.Game)); + node.Items.Add(MakeNameNode(bin, "nmAttachTo", Pcc)); + node.Items.Add(MakeVectorNode(bin, "vOffsetLocation")); + node.Items.Add(MakeRotatorNode(bin, "rOffsetRotation")); + node.Items.Add(MakeVectorNode(bin, "vOffsetScale")); + int count2; + var propActionsNode = new BinInterpNode(bin.Position, $"mapActions ({count2 = bin.ReadInt32()} items)") + { + IsExpanded = true + }; + node.Items.Add(propActionsNode); + for (int j = 0; j < count2; j++) + { + BinInterpNode node2 = new BinInterpNode(bin.Position, $"{j}: {bin.ReadNameReference(Pcc)}", NodeType.StructLeafName) + { + Length = 8 + }; + propActionsNode.Items.Add(node2); + node2.Items.Add(MakeNameNode(bin, "nmActionName", Pcc)); + if (CurrentLoadedExport.Game.IsGame2()) + { + node2.Items.Add(MakeStringNode(bin, "sEffect", Pcc.Game)); + } + + node2.Items.Add(MakeBoolIntNode(bin, "bActivate")); + node2.Items.Add(MakeNameNode(bin, "nmAttachTo", Pcc)); + node2.Items.Add(MakeVectorNode(bin, "vOffsetLocation")); + node2.Items.Add(MakeRotatorNode(bin, "rOffsetRotation")); + node2.Items.Add(MakeVectorNode(bin, "vOffsetScale")); + if (CurrentLoadedExport.Game.IsGame3()) + { + node2.Items.Add(MakeStringNode(bin, "sParticleSys", Pcc.Game)); + node2.Items.Add(MakeStringNode(bin, "sClientEffect", Pcc.Game)); + node2.Items.Add(MakeBoolIntNode(bin, "bCooldown")); + node2.Items.Add(new BinInterpNode(bin.Position, "tSpawnParams") + { + Length = 0x38, + IsExpanded = true, + Items = + { + MakeVectorNode(bin, "vHitLocation"), + MakeVectorNode(bin, "vHitNormal"), + MakeNameNode(bin, "nmHitBone", Pcc), + MakeVectorNode(bin, "vRayDir"), + MakeVectorNode(bin, "vSpawnValue") + } + }); + } + } + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List Scan_Bio2DA(byte[] data) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(data) { Endian = Pcc.Endian }; + bin.JumpTo(CurrentLoadedExport.propsEnd()); + + bool isIndexed = !bin.ReadBoolInt(); + bin.Skip(-4); + if (isIndexed) + { + subnodes.Add(MakeUInt32Node(bin, "Zero")); + } + + int cellCount; + subnodes.Add(new BinInterpNode(bin.Position, $"Populated Cell Count: {cellCount = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }); + + for (int i = 0; i < cellCount; i++) + { + Bio2DACell.Bio2DADataType type; + subnodes.Add(new BinInterpNode(bin.Position, $"Cell {(isIndexed ? bin.ReadInt32() : i)}", NodeType.StructLeafInt) + { + Items = + { + new BinInterpNode(bin.Position, $"Type: {type = (Bio2DACell.Bio2DADataType)bin.ReadByte()}") { Length = 1 }, + type switch + { + Bio2DACell.Bio2DADataType.TYPE_INT => MakeInt32Node(bin, "Value"), + Bio2DACell.Bio2DADataType.TYPE_NAME => MakeNameNode(bin, "Value", Pcc), + Bio2DACell.Bio2DADataType.TYPE_FLOAT => MakeFloatNode(bin, "Value"), + Bio2DACell.Bio2DADataType.TYPE_NULL => new BinInterpNode("Value: NULL"), + _ => throw new ArgumentOutOfRangeException() + } + } + }); + } + + if (!isIndexed) + { + subnodes.Add(MakeUInt32Node(bin, "Zero")); + } + + int columnCount; + subnodes.Add(new BinInterpNode(bin.Position, $"Column Count: {columnCount = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }); + + for (int i = 0; i < columnCount; i++) + { + subnodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.ReadNameReference(Pcc)}, Index: {bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 12 }); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioSquadCombatScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + subnodes.Add(MakeArrayNode(bin, "Count", i => + { + string entry = null; + if (Pcc.Game.IsLEGame()) + { + entry = Pcc.GetEntryString(bin.ReadInt32()); + } + + var guid = bin.ReadGuid(); + int num = bin.ReadInt32(); + + return new BinInterpNode(bin.Position, $"{guid}: {num} {entry}"); + })); + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioDynamicAnimSetScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + + try + { + int binarypos = binarystart; + int count = EndianReader.ToInt32(data, binarypos, CurrentLoadedExport.FileRef.Endian); + subnodes.Add(new BinInterpNode + { + Header = $"0x{binarypos:X4} Count: {count}" + }); + binarypos += 4; //+ int + for (int i = 0; i < count; i++) + { + int nameIndex = EndianReader.ToInt32(data, binarypos, CurrentLoadedExport.FileRef.Endian); + int nameIndexNum = EndianReader.ToInt32(data, binarypos + 4, CurrentLoadedExport.FileRef.Endian); + int shouldBe1 = EndianReader.ToInt32(data, binarypos + 8, CurrentLoadedExport.FileRef.Endian); + + var name = CurrentLoadedExport.FileRef.GetNameEntry(nameIndex); + string nodeValue = $"{new NameReference(name, nameIndexNum).Instanced}"; + if (shouldBe1 != 1) + { + //ERROR + nodeValue += " - Not followed by 1 (integer)!"; + } + + subnodes.Add(new BinInterpNode + { + Header = $"0x{binarypos:X4} Name: {nodeValue}", + Tag = NodeType.StructLeafName, + Offset = binarypos, + }); + subnodes.Add(new BinInterpNode + { + Header = $"0x{(binarypos + 8):X4} Unknown 1: {shouldBe1}", + Tag = NodeType.StructLeafInt, + Offset = (binarypos + 8), + }); + binarypos += 12; + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartBioStageScan(byte[] data, ref int binarystart) + { + /* + * Length (int) + Name: m_aCameraList + int unknown 0 + Count + int unknown + [Camera name + unreal property data]*/ + var subnodes = new List(); + //if ((CurrentLoadedExport.Header[0x1f] & 0x2) != 0) + { + int pos = binarystart; + if (data.Length > binarystart) + { + int length = BitConverter.ToInt32(data, binarystart); + subnodes.Add(new BinInterpNode + { + Header = $"{binarystart:X4} Length: {length}", + Offset = pos + }); + pos += 4; + if (length != 0) + { + int nameindex = BitConverter.ToInt32(data, pos); + int num = BitConverter.ToInt32(data, pos + 4); + + var name = new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameindex), num); + subnodes.Add(new BinInterpNode + { + Header = $"{(pos - binarystart):X4} Array Name: {name.Instanced}", + Offset = pos, + Tag = NodeType.StructLeafName + }); + + pos += 8; + int shouldbezero = BitConverter.ToInt32(data, pos); + if (shouldbezero != 0) + { + Debug.WriteLine($"NOT ZERO FOUND: {pos}"); + } + pos += 4; + + int count = BitConverter.ToInt32(data, pos); + subnodes.Add(new BinInterpNode + { + Header = $"{(pos - binarystart):X4} Count: {count}", + Offset = pos + }); + pos += 4; + + shouldbezero = BitConverter.ToInt32(data, pos); + if (shouldbezero != 0) + { + Debug.WriteLine($"NOT ZERO FOUND: {pos}"); + } + pos += 4; + try + { + var stream = new MemoryStream(data); + for (int i = 0; i < count; i++) + { + nameindex = BitConverter.ToInt32(data, pos); + num = BitConverter.ToInt32(data, pos + 4); + BinInterpNode parentnode = new BinInterpNode + { + Header = $"{(pos - binarystart):X4} Camera {i + 1}: {new NameReference(CurrentLoadedExport.FileRef.GetNameEntry(nameindex), num).Instanced}", + Tag = NodeType.StructLeafName, + Offset = pos + }; + subnodes.Add(parentnode); + pos += 8; + stream.Seek(pos, SeekOrigin.Begin); + var props = PropertyCollection.ReadProps(CurrentLoadedExport, stream, "BioStageCamera", includeNoneProperty: true); + + UPropertyTreeViewEntry topLevelTree = new UPropertyTreeViewEntry(); //not used, just for holding and building data. + foreach (Property prop in props) + { + InterpreterExportLoader.GenerateUPropertyTreeForProperty(prop, topLevelTree, CurrentLoadedExport); + } + subnodes.AddRange(topLevelTree.ChildrenProperties); + + //finish writing function here + pos = (int)stream.Position; + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + } + } + } + return subnodes; + } + + private List StartBioGestureRulesDataScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + + if (binarystart >= data.Length) + { + return subnodes; + } + + int pos = binarystart; + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + try + { + var count = bin.ReadInt32(); + bin.Position -= 4; // Set back so the node can be made + subnodes.Add(MakeInt32Node(bin, "Count")); + + for (int i = 0; i < count; i++) + { + var node = new BinInterpNode(bin.Position, $"Rule {i}"); + subnodes.Add(node); + + node.Items.Add(MakeNameNode(bin, "Name", Pcc)); + + var subcount = bin.ReadInt32(); + var subnode = new BinInterpNode(bin.Position - 4, $"Num somethings: {subcount}"); + node.Items.Add(subnode); + + for (int j = 0; j < subcount; j++) + { + // Read name, some integer + subnode.Items.Add(MakeNameNode(bin, "SomeName", Pcc)); + subnode.Items.Add(MakeInt32Node(bin, "SomeNum")); + } + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode() { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartBioMorphFaceScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + subnodes.Add(MakeArrayNode(bin, "LOD Count:", k => new BinInterpNode(bin.Position, $"{k}") + { + Items = + { + MakeInt32Node(bin, "Size of Vector"), + MakeArrayNode(bin, $"LOD {k} Vertex Positional Data", n => new BinInterpNode(bin.Position, $"{n}") + { + Items = + { + MakeVectorNode(bin, "Position"), + } + }), + } + }, true)); + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs new file mode 100644 index 0000000000..9b1dfed6d0 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs @@ -0,0 +1,504 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorerCore.Gammtek.IO; +using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; + +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public partial class BinaryInterpreterWPF +{ + private List StartStaticMeshComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + bool bLoadVertexColorData; + uint numVertices; + + int lodDataCount = bin.ReadInt32(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"LODData count: {lodDataCount}")); + subnodes.AddRange(ReadList(lodDataCount, i => + { + BinInterpNode node = new BinInterpNode(bin.Position, $"LODData {i}") + { + IsExpanded = true + }; + node.Items.Add(new BinInterpNode(bin.Position, $"ShadowMaps ({bin.ReadInt32()})") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeEntryNode(bin, $"{j}", Pcc)) + }); + node.Items.Add(new BinInterpNode(bin.Position, $"ShadowVertexBuffers ({bin.ReadInt32()})") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeEntryNode(bin, $"{j}", Pcc)) + }); + node.Items.Add(MakeLightMapNode(bin)); + node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new List + { + new BinInterpNode(bin.Position, $"bLoadVertexColorData ({bLoadVertexColorData = bin.ReadBoolByte()})"), + ListInitHelper.ConditionalAdd(bLoadVertexColorData, () => new ITreeItem[] + { + new BinInterpNode(bin.Position, "OverrideVertexColors ") + { + Items = + { + MakeUInt32Node(bin, "Stride:"), + new BinInterpNode(bin.Position, $"NumVertices: {numVertices = bin.ReadUInt32()}"), + ListInitHelper.ConditionalAdd(numVertices > 0, () => new ITreeItem[] + { + MakeInt32Node(bin, "FColor size"), + new BinInterpNode(bin.Position, $"VertexData ({bin.ReadInt32()})") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeColorNode(bin, $"{j}")) + }, + }), + } + } + }) + })); + return node; + })); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartFluidSurfaceComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + if (Pcc.Game == MEGame.ME3) + { + subnodes.Add(MakeLightMapNode(bin)); + } + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartTerrainComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + bool bIsLeaf; + subnodes.Add(MakeArrayNode(bin, "CollisionVertices", i => MakeVectorNode(bin, $"{i}"))); + subnodes.Add(new BinInterpNode(bin.Position, "BVTree") + { + IsExpanded = true, + Items = + { + MakeArrayNode(bin, "Nodes", i => new BinInterpNode(bin.Position, $"{i}") + { + Items = + { + MakeBoxNode(bin, "BoundingVolume"), + new BinInterpNode(bin.Position, $"bIsLeaf: {bIsLeaf = bin.ReadBoolInt()}"), + ListInitHelper.ConditionalAdd(bIsLeaf, () => new ITreeItem[] + { + MakeUInt16Node(bin, "XPos"), + MakeUInt16Node(bin, "YPos"), + MakeUInt16Node(bin, "XSize"), + MakeUInt16Node(bin, "YSize"), + }, () => new ITreeItem[] + { + MakeUInt16Node(bin, "NodeIndex[0]"), + MakeUInt16Node(bin, "NodeIndex[1]"), + MakeUInt16Node(bin, "NodeIndex[2]"), + MakeUInt16Node(bin, "NodeIndex[3]"), + }), + MakeFloatNodeConditional(bin, "Unknown float", CurrentLoadedExport.Game != MEGame.UDK), + } + }) + } + }); + subnodes.Add(MakeArrayNode(bin, "PatchBounds", i => new BinInterpNode(bin.Position, $"{i}") + { + Items = + { + MakeFloatNode(bin, "MinHeight"), + MakeFloatNode(bin, "MaxHeight"), + MakeFloatNode(bin, "MaxDisplacement"), + } + })); + subnodes.Add(MakeLightMapNode(bin)); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartBrushComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + int cachedConvexElementsCount; + subnodes.Add(new BinInterpNode(bin.Position, "CachedPhysBrushData") + { + IsExpanded = true, + Items = + { + new BinInterpNode(bin.Position, $"CachedConvexElements ({cachedConvexElementsCount = bin.ReadInt32()})") + { + Items = ReadList(cachedConvexElementsCount, j => + { + int size; + var item = new BinInterpNode(bin.Position, $"{j}: ConvexElementData (size of byte: {bin.ReadInt32()}) (number of bytes: {size = bin.ReadInt32()})") + { + Length = size + 8 + }; + bin.Skip(size); + return item; + }) + } + } + }); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartModelComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + try + { + int count; + bin.JumpTo(binarystart); + subnodes.Add(MakeEntryNode(bin, "Model", Pcc)); + subnodes.Add(MakeInt32Node(bin, "ZoneIndex")); + subnodes.Add(new BinInterpNode(bin.Position, $"Elements ({count = bin.ReadInt32()})") + { + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: FModelElement") + { + Items = + { + MakeLightMapNode(bin), + MakeEntryNode(bin, "Component", Pcc), + MakeEntryNode(bin, "Material", Pcc), + new BinInterpNode(bin.Position, $"Nodes ({count = bin.ReadInt32()})") + { + Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadUInt16()}")) + }, + new BinInterpNode(bin.Position, $"ShadowMaps ({count = bin.ReadInt32()})") + { + Items = ReadList(count, j => MakeEntryNode(bin, $"{j}", Pcc)) + }, + new BinInterpNode(bin.Position, $"IrrelevantLights ({count = bin.ReadInt32()})") + { + Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadGuid()}")) + } + } + }) + }); + subnodes.Add(new BinInterpNode(bin.Position, $"ComponentIndex: {bin.ReadUInt16()}")); + subnodes.Add(new BinInterpNode(bin.Position, $"Nodes ({count = bin.ReadInt32()})") + { + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUInt16()}")) + }); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartLightComponentScan(byte[] data, int binarystart) + { + var subnodes = new List(); + if (Pcc.Game == MEGame.UDK) + { + return subnodes; + } + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + int count; + foreach (string propName in new[] { "InclusionConvexVolumes", "ExclusionConvexVolumes" }) + { + subnodes.Add(new BinInterpNode(bin.Position, $"{propName} ({count = bin.ReadInt32()})") + { + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{i}") + { + Items = + { + new BinInterpNode(bin.Position, $"Planes ({count = bin.ReadInt32()})") + { + Items = ReadList(count, j => + new BinInterpNode(bin.Position, $"{j}: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()}, W: {bin.ReadSingle()})")) + }, + new BinInterpNode(bin.Position, $"PermutedPlanes ({count = bin.ReadInt32()})") + { + Items = ReadList(count, j => + new BinInterpNode(bin.Position, $"{j}: (X: {bin.ReadSingle()}, Y: {bin.ReadSingle()}, Z: {bin.ReadSingle()}, W: {bin.ReadSingle()})")) + } + } + }) + }); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private List StartDecalComponentScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + int numStaticRecievers; + int count; + int fDecalVertexSize; + var item = new BinInterpNode(bin.Position, $"StaticReceivers: {numStaticRecievers = bin.ReadInt32()}") + { + IsExpanded = true + }; + item.Items = ReadList(numStaticRecievers, i => + { + var node = new BinInterpNode(bin.Position, $"{i}"); + try + { + node.Items.Add(MakeEntryNode(bin, "Component", Pcc)); + node.Items.Add(new BinInterpNode(bin.Position, $"FDecalVertex Size: {fDecalVertexSize = bin.ReadInt32()}")); + BinInterpNode interpNode = new BinInterpNode(bin.Position, $"Vertices ({count = bin.ReadInt32()})") + { + Length = 4 + fDecalVertexSize * count + }; + interpNode.Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}") + { + Length = fDecalVertexSize, + Items = + { + MakeVectorNode(bin, "Position"), + MakePackedNormalNode(bin, "TangentX"), + MakePackedNormalNode(bin, "TangentZ"), + ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] + { + MakeVector2DNode(bin, "LegacyProjectedUVs") + }), + MakeVector2DNode(bin, "LightMapCoordinate"), + ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] + { + MakeVector2DNode(bin, "LegacyNormalTransform[0]"), + MakeVector2DNode(bin, "LegacyNormalTransform[1]") + }), + } + }); + node.Items.Add(interpNode); + node.Items.Add(MakeInt32Node(bin, "unsigned short size")); + node.Items.Add(new BinInterpNode(bin.Position, $"Indices ({count = bin.ReadInt32()})") + { + Length = 4 + count * 2, + Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadUInt16()}") { Length = 2 }) + }); + node.Items.Add(MakeUInt32Node(bin, "NumTriangles")); + node.Items.Add(MakeLightMapNode(bin)); + if (Pcc.Game >= MEGame.ME3) + { + node.Items.Add(new BinInterpNode(bin.Position, $"ShadowMap1D ({count = bin.ReadInt32()})") + { + Length = 4 + count * 4, + Items = ReadList(count, j => new BinInterpNode(bin.Position, $"{j}: {MakeEntryNodeString(bin, Pcc)}") { Length = 4 }) + }); + node.Items.Add(MakeInt32Node(bin, "Data")); + node.Items.Add(MakeInt32Node(bin, "InstanceIndex")); + } + } + catch (Exception e) + { + node.Items.Add(new BinInterpNode { Header = $"Error reading binary data: {e}" }); + } + return node; + }); + subnodes.Add(item); + + binarystart = (int)bin.Position; + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } + + private BinInterpNode MakeLightMapNode(EndianReader bin, List<(int, int)> lightmapChunksToRemove = null) + { + ELightMapType lightMapType; + int bulkSerializeElementCount; + int bulkSerializeDataSize; + + var retvalue = new BinInterpNode(bin.Position, "LightMap ") + { + IsExpanded = true, + Items = + { + new BinInterpNode(bin.Position, $"LightMapType: {lightMapType = (ELightMapType) bin.ReadInt32()}"), + ListInitHelper.ConditionalAdd(lightMapType != ELightMapType.LMT_None, () => + { + //chunk starts at 0 - postion of LM type + var chunk = ((int)bin.Position - 4,0); + var tree = new List + { + new BinInterpNode(bin.Position, $"LightGuids ({bin.ReadInt32()})") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{j}: {bin.ReadGuid()}")) + }, + ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_1D, () => new ITreeItem[] + { + MakeEntryNode(bin, "Owner", Pcc), + MakeUInt32Node(bin, "BulkDataFlags:"), + new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), + new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), + MakeInt32Node(bin, "BulkDataOffsetInFile"), + new BinInterpNode(bin.Position, $"DirectionalSamples: ({bulkSerializeElementCount})") + { + Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, + $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) + }) + }, + MakeVectorNode(bin, "ScaleVector 1"), + MakeVectorNode(bin, "ScaleVector 2"), + MakeVectorNode(bin, "ScaleVector 3"), + Pcc.Game < MEGame.ME3 ? MakeVectorNode(bin, "ScaleVector 4") : null, + MakeUInt32Node(bin, "BulkDataFlags:"), + new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), + new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), + MakeInt32Node(bin, "BulkDataOffsetInFile"), + new BinInterpNode(bin.Position, $"SimpleSamples: ({bulkSerializeElementCount})") + { + Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, + $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) + }) + }, + }.NonNull()), + ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_2D, () => new List + { + MakeEntryNode(bin, "Texture 1", Pcc), + MakeVectorNodeEditable(bin, "ScaleVector 1", true), + MakeEntryNode(bin, "Texture 2", Pcc), + MakeVectorNodeEditable(bin, "ScaleVector 2", true), + MakeEntryNode(bin, "Texture 3", Pcc), + MakeVectorNodeEditable(bin, "ScaleVector 3", true), + ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] + { + MakeEntryNode(bin, "Texture 4", Pcc), + MakeVectorNodeEditable(bin, "ScaleVector 4", true), + }), + MakeVector2DNodeEditable(bin, "CoordinateScale", true), + MakeVector2DNodeEditable(bin, "CoordinateBias", true) + }), + ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_3, () => new ITreeItem[] + { + MakeInt32Node(bin, "Unknown"), + MakeUInt32Node(bin, "BulkDataFlags:"), + new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), + new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), + MakeInt32Node(bin, "BulkDataOffsetInFile"), + new BinInterpNode(bin.Position, $"DirectionalSamples?: ({bulkSerializeElementCount})") + { + Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, + $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) + }) + }, + MakeVectorNode(bin, "ScaleVector?"), + MakeVectorNode(bin, "ScaleVector?") + }), + ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_4 || lightMapType == ELightMapType.LMT_6, () => new List + { + MakeEntryNode(bin, "Texture 1", Pcc), + new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), + MakeEntryNode(bin, "Texture 2", Pcc), + new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), + MakeEntryNode(bin, "Texture 3", Pcc), + new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), + new ListInitHelper.InitCollection(ReadList(4, j => MakeFloatNode(bin, "Unknown float"))), + }), + ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_5, () => new ITreeItem[] + { + MakeInt32Node(bin, "Unknown"), + MakeUInt32Node(bin, "BulkDataFlags:"), + new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), + new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), + MakeInt32Node(bin, "BulkDataOffsetInFile"), + new BinInterpNode(bin.Position, $"SimpleSamples?: ({bulkSerializeElementCount})") + { + Items = ReadList(bulkSerializeElementCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = ReadList(bulkSerializeDataSize / bulkSerializeElementCount / 4, k => new BinInterpNode(bin.Position, + $"(B: {bin.ReadByte()}, G: {bin.ReadByte()}, R: {bin.ReadByte()}, A: {bin.ReadByte()})")) + }) + }, + MakeVectorNode(bin, "ScaleVector?") + }), + }; + chunk.Item2 = (int)bin.Position; + lightmapChunksToRemove?.Add(chunk); + return tree; + }) + } + }; + + return retvalue; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs new file mode 100644 index 0000000000..c00cdb46c6 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs @@ -0,0 +1,669 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorerCore.Gammtek.IO; +using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; + +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public partial class BinaryInterpreterWPF +{ + /// + /// Reads the common header for FaceFX archives. + /// + private (int version, MEGame game) ReadFaceFXHeader(EndianReader bin, List subnodes) + { + var archiveSize = bin.ReadInt32(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Archive size: {archiveSize} ({FileSize.FormatSize(archiveSize)})")); + subnodes.Add(new BinInterpNode(bin.Position, $"Magic: {bin.ReadInt32():X8}") { Length = 4 }); + int versionID = bin.ReadInt32(); //1710 = ME1, 1610 = ME2, 1731 = ME3. + var game = versionID == 1710 ? MEGame.ME1 : + versionID == 1610 ? MEGame.ME2 : + versionID == 1731 ? MEGame.ME3 : + MEGame.Unknown; + var vIdStr = versionID.ToString(); + var vers = new Version(vIdStr[0] - '0', vIdStr[1] - '0', vIdStr[2] - '0', vIdStr[3] - '0'); //Mega hack + subnodes.Add(new BinInterpNode(bin.Position - 4, $"SDK Version: {versionID} ({vers})") { Length = 4 }); + if (versionID == 1731) + { + subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); + } + + subnodes.Add(new BinInterpNode(bin.Position, $"Licensee: {bin.ReadFaceFXString(game)}")); + subnodes.Add(new BinInterpNode(bin.Position, $"Project: {bin.ReadFaceFXString(game)}")); + + var licenseeVersion = bin.ReadInt32(); + vIdStr = licenseeVersion.ToString(); + vers = new Version(vIdStr[0] - '0', vIdStr[1] - '0', vIdStr[2] - '0', vIdStr[3] - '0'); //Mega hack + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Licensee version: {vIdStr} ({vers})") { Length = 4 }); + + return (versionID, game); + } + + private List StartFaceFXAnimSetScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + ReadFaceFXHeader(bin, subnodes); + + if (Pcc.Game == MEGame.ME2) + { + subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); + } + else + { + subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + + if (Pcc.Game != MEGame.ME2) + { + int hNodeCount = bin.ReadInt32(); + var hNodes = new List(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Nodes: {hNodeCount} items") + { + Items = hNodes + }); + for (int i = 0; i < hNodeCount; i++) + { + var hNodeNodes = new List(); + hNodes.Add(new BinInterpNode(bin.Position, $"{i}") + { + Items = hNodeNodes + }); + hNodeNodes.Add(MakeInt32Node(bin, "Unknown")); + var nNameCount = bin.ReadInt32(); + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name Count: {nNameCount}") { Length = 4 }); + for (int n = 0; n < nNameCount; n++) + { + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + } + } + + int nameCount = bin.ReadInt32(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Names: {nameCount} items") + { + //ME2 different to ME3/1 + Items = ReadList(nameCount, i => new BinInterpNode(bin.Skip(Pcc.Game != MEGame.ME2 ? 0 : 4).Position, $"{i}: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")) + }); + + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + if (Pcc.Game == MEGame.ME2) + { + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + } + + int lineCount = bin.ReadInt32(); + var lines = new List(); + + subnodes.Add(new BinInterpNode(bin.Position - 4, $"FaceFXLines: {lineCount} items") + { + Items = lines + }); + for (int i = 0; i < lineCount; i++) + { + var nodes = new List(); + lines.Add(new BinInterpNode(bin.Position, $"{i}") + { + Items = nodes + }); + if (Pcc.Game == MEGame.ME2) + { + nodes.Add(MakeInt32Node(bin, "Unknown")); + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + nodes.Add(MakeInt32Node(bin, "Name")); + if (Pcc.Game == MEGame.ME2) + { + nodes.Add(MakeInt32Node(bin, "Unknown")); + } + int animationCount = bin.ReadInt32(); + var anims = new List(); + nodes.Add(new BinInterpNode(bin.Position - 4, $"Animations: {animationCount} items") + { + Items = anims + }); + for (int j = 0; j < animationCount; j++) + { + var animNodes = new List(); + anims.Add(new BinInterpNode(bin.Position, $"{j}") + { + Items = animNodes + }); + if (Pcc.Game == MEGame.ME2) + { + animNodes.Add(MakeInt32Node(bin, "Unknown")); + animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + animNodes.Add(MakeInt32Node(bin, "Name")); + animNodes.Add(MakeInt32Node(bin, "Unknown")); + if (Pcc.Game == MEGame.ME2) + { + animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + } + + int pointsCount = bin.ReadInt32(); + nodes.Add(new BinInterpNode(bin.Position - 4, $"Points: {pointsCount} items") + { + Items = ReadList(pointsCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = new List + { + new BinInterpNode(bin.Position, $"Time: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"Weight: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"InTangent: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"LeaveTangent: {bin.ReadFloat()}") {Length = 4} + } + }) + }); + + if (pointsCount > 0) + { + if (Pcc.Game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + nodes.Add(new BinInterpNode(bin.Position, $"NumKeys: {bin.ReadInt32()} items") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{bin.ReadInt32()} keys")) + }); + } + nodes.Add(new BinInterpNode(bin.Position, $"Fade In Time: {bin.ReadFloat()}") { Length = 4 }); + nodes.Add(new BinInterpNode(bin.Position, $"Fade Out Time: {bin.ReadFloat()}") { Length = 4 }); + nodes.Add(MakeInt32Node(bin, "Unknown")); + if (Pcc.Game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + nodes.Add(new BinInterpNode(bin.Position, $"Path: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + if (Pcc.Game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + nodes.Add(new BinInterpNode(bin.Position, $"ID: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + nodes.Add(MakeInt32Node(bin, "index")); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } + + private List StartFaceFXAssetScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + try + { + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + var (version, game) = ReadFaceFXHeader(bin, subnodes); + + if (game == MEGame.ME2) + { + subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt32():X8}") { Length = 4 }); + } + else + { + subnodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + //Node Table + if (game != MEGame.ME2) + { + int hNodeCount = bin.ReadInt32(); + var hNodes = new List(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Nodes: {hNodeCount} items") + { + Items = hNodes + }); + for (int i = 0; i < hNodeCount; i++) + { + var hNodeNodes = new List(); + hNodes.Add(new BinInterpNode(bin.Position, $"{i}") + { + Items = hNodeNodes + }); + hNodeNodes.Add(MakeInt32Node(bin, "Unknown")); + var nNameCount = bin.ReadInt32(); + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name Count: {nNameCount}") { Length = 4 }); + for (int n = 0; n < nNameCount; n++) + { + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Name: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + hNodeNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + } + } + + //Name Table + + var nameTable = new List(); + int nameCount = bin.ReadInt32(); + var nametablePos = bin.Position - 4; + var nametabObj = new List(); + + // Does this need byte aligned? ME2 seems like it does... + //if (game == MEGame.ME2) + //{ + // bin.ReadByte(); // Align to bytes? + //} + + for (int m = 0; m < nameCount; m++) + { + var pos = bin.Position; + var mName = bin.ReadFaceFXString(game, true); + nameTable.Add(mName); + nametabObj.Add(new BinInterpNode(pos, $"{m}: {mName}")); + //if (game != MEGame.ME2) + //{ + // bin.Skip(4); + //} + } + + subnodes.Add(new BinInterpNode(nametablePos, $"Names: {nameCount} items") + { + //ME1 and ME3 same, ME2 different + Items = nametabObj + }); + + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + + if (game == MEGame.ME2) + { + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt16Node(bin, "Unknown")); + subnodes.Add(MakeInt16Node(bin, "Unknown")); + subnodes.Add(MakeInt16Node(bin, "Unknown")); + } + + //LIST A - BONES + var bonesList = new List(); + var bonesCount = bin.ReadInt32(); + if (game == MEGame.ME2) + { + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt16Node(bin, "Unknown")); + } + subnodes.Add(new BinInterpNode(((game == MEGame.ME2) ? (bin.Position - 10) : (bin.Position - 4)), $"Bone Nodes: {bonesCount} items") + { + Items = bonesList + }); + + for (int a = 0; a < bonesCount; a++) //NOT EXACT?? + { + var boneNode = new List(); + bonesList.Add(new BinInterpNode(bin.Position, $"{nameTable[bin.ReadInt32()]}") + { + Items = boneNode + }); + boneNode.Add(MakeFloatNode(bin, "X")); + boneNode.Add(MakeFloatNode(bin, "Y")); + boneNode.Add(MakeFloatNode(bin, "Z")); + + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + if (game != MEGame.ME2) + { + boneNode.Add(MakeFloatNode(bin, "Unknown float")); + boneNode.Add(MakeFloatNode(bin, "Bone weight?")); + //while (true) + //{ + // var item = bin.ReadInt32(); + // if (item == 2147483647) + // { + // tableItems.Add(new BinInterpNode(bin.Position - 4, $"End Marker: FF FF FF 7F") { Length = 4 }); + // tableItems.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + // break; + // } + + // bin.Skip(-4); + // tableItems.Add(MakeFloatNode(bin, "Unknown float")); + + //} + //Name list to Bones and other facefx? + var unkNameList1 = new List(); + var countUk1 = bin.ReadInt32(); + boneNode.Add(new BinInterpNode(bin.Position - 4, $"Functions?: {countUk1} items") + { + Items = unkNameList1 + }); + for (int b = 0; b < countUk1; b++) + { + var unameVal = bin.ReadInt32(); + var unkNameList1items = new List(); + unkNameList1.Add(new BinInterpNode(bin.Position - 4, $"{b},{unameVal}") + { + Items = unkNameList1items + }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"{nameTable[bin.ReadInt32()]}")); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); + unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); + unkNameList1items.Add(MakeInt32Node(bin, "Unknown")); + } + } + } + + //LIST B - COMBINER NODES + //FROM HERE ME3 ONLY WIP + + //I have literally no idea how this works in ME2 + + var combinerList = new List(); + var combinerListNames = new List(); + var combinerCount = bin.ReadInt32(); + + if (game == MEGame.ME2) + { + subnodes.Add(MakeInt32Node(bin, "Unknown")); + } + + subnodes.Add(new BinInterpNode(bin.Position - (game == MEGame.ME2 ? 8 : 4), $"Combiner nodes: {combinerCount} items") + { + Items = combinerList + }); + + for (int b = 0, i = 0; i < combinerCount; b++, i++) + { + var bLocation = bin.Position; + // There seem to be several types, known types are 0, 4, 6, 8. + int formatType; + if (game == MEGame.ME2) + { + formatType = bin.ReadInt16(); + } + else formatType = bin.ReadInt32(); + + var nameIdx = bin.ReadInt32(); + + var combinerNode = new List(); + combinerList.Add(new BinInterpNode(bin.Position - 4, $"{b}: {nameTable[nameIdx]} - {(FxNodeType)formatType}") + { + Items = combinerNode + }); + combinerListNames.Add(nameTable[nameIdx]); + + combinerNode.Add(new BinInterpNode(bin.Position - 8, $"Format: {formatType} - {(FxNodeType)formatType}")); + combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Table index: {nameIdx}")); + combinerNode.Add(new BinInterpNode(bin.Position, $"Minimum Value: {bin.ReadSingle()}") { Length = 4 }); + combinerNode.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + combinerNode.Add(new BinInterpNode(bin.Position, $"Maximum Value: {bin.ReadSingle()}") { Length = 4 }); + combinerNode.Add(new BinInterpNode(bin.Position, $"Unknown float: {bin.ReadSingle()}") { Length = 4 }); + var inputOp = bin.ReadInt32(); + combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Input Operation: {inputOp} - {(FxInputOperation)inputOp}")); + + // Parent links section + var parentLinks = new List(); //Name list to Bones and other facefx phenomes? + var parentLinksCount = bin.ReadInt32(); + combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Parent Links: {parentLinksCount} items") + { + Items = parentLinks + }); + for (int n2 = 0; n2 < parentLinksCount; n2++) + { + var combinerIdx = bin.ReadInt32(); + var linkedNode = combinerIdx < b ? combinerListNames[combinerIdx] : ""; + var parentLinkItems = new List(); + parentLinks.Add(new BinInterpNode(bin.Position - 4, $"Combiner Idx: {combinerIdx} {linkedNode}") + { + Items = parentLinkItems + }); + var linkFunction = bin.ReadInt32(); + parentLinkItems.Add(new BinInterpNode(bin.Position - 4, $"Link Function: {(FxLinkFunction)linkFunction}")); + var n3count = bin.ReadInt32(); + parentLinkItems.Add(new BinInterpNode(bin.Position - 4, $"Parameter Count: {n3count}")); + for (int n3 = 0; n3 < n3count; n3++) + { + parentLinkItems.Add(new BinInterpNode(bin.Position, $"Function Parameter {n3}: {bin.ReadSingle()}") { Length = 4 }); + } + } + + // Parameters section + int parameterCount = bin.ReadInt32(); + var fxaParameter = new List(parameterCount); + combinerNode.Add(new BinInterpNode(bin.Position - 4, $"Parameters: {parameterCount} items") + { + Items = fxaParameter + }); + for (int fxaIndex = 0; fxaIndex < parameterCount; fxaIndex++) + { + int fxaIdxVal = bin.ReadInt32(); + var fxaInfoItem = new List(); + fxaParameter.Add(new BinInterpNode(bin.Position - 4, $"{nameTable[fxaIdxVal]} - {fxaIdxVal}") + { + Items = fxaInfoItem + }); + int parameterFmt = bin.ReadInt32(); + fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Name: {nameTable[fxaIdxVal]} ({fxaIdxVal})")); + fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Format: {(FxNodeParamFormat)parameterFmt} ({parameterFmt})") { Length = 4 }); + // Parameter format - 0 means first int is the param value, 3 means there is a string on the end that is the param value + + var firstUnkIntName = parameterFmt == 0 ? "Int Value" : "Unknown int"; + fxaInfoItem.Add(new BinInterpNode(bin.Position, $"{firstUnkIntName}: {bin.ReadInt32()}") { Length = 4 }); + fxaInfoItem.Add(new BinInterpNode(bin.Position, $"Float value?: {bin.ReadSingle()}") { Length = 4 }); + fxaInfoItem.Add(new BinInterpNode(bin.Position, $"Unknown int: {bin.ReadInt32()}") { Length = 4 }); + + if (parameterFmt == 3) + { + var unkStringLength = bin.ReadInt32(); + fxaInfoItem.Add(new BinInterpNode(bin.Position - 4, $"Parameter Value: {bin.BaseStream.ReadStringLatin1(unkStringLength)}")); + } + } + } + + // Fix names for bone node functions now that we've parsed combiner table - this is terrible code + foreach (var bone in bonesList) + { + var functions = (bone as BinInterpNode).Items[^1]; + if (functions is BinInterpNode functionNode && functionNode.Header.Contains("Function")) + { + foreach (var funcItem in functionNode.Items) + { + if (funcItem is BinInterpNode func) + { + var ints = func.Header.Split(',', ' ').Where(str => Int32.TryParse(str, out _)).Select(str => Convert.ToInt32(str)).ToArray(); + if (ints.Length != 2) break; + func.Header = $"{ints[0]}: Combiner Node {ints[1]} ({combinerListNames[ints[1]]})"; + } + } + } + } + + // Unknown Table C First 4 bytes + // Theory 1: This could refer to a number of "unique" entries, it seems to be a number of entries in the table that have only 1 string reference to the combiner. + // Subtracting: (entries in this table that have 2 or more strings) - (total amount of combiner entires) = seems to result in same number that exists in these first 4 bytes. + byte[] unkHeaderC = bin.ReadBytes(4); + var unkListC = new List(); + subnodes.Add(new BinInterpNode(bin.Position - 4, $"Unknown Table C - Combiner Mapping?") + { + Items = unkListC + }); + + for (int c = 0; c < combinerCount; c++) + { + // Table begins with an unknown ID - this ID seems to be from some kind of global table as same entries with same names use same IDs across different FaceFX files + // (not 100% sure as only smallish sample of about 20 files was checked) + int entryID = bin.ReadInt32(); + // Add tree item with entry ID as an idicator + var unkListCitems = new List(); + unkListC.Add(new BinInterpNode(bin.Position - 4, $"{c}: {entryID}") + { + Items = unkListCitems + }); + unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Unknown int: {entryID}") { Length = 4 }); + // String count: + int stringCount = bin.ReadInt32(); + unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"String count: {stringCount}") { Length = 4 }); + // Combiner entry name: + int stringLength = bin.ReadInt32(); + string stringText = bin.ReadEndianASCIIString(stringLength); + unkListCitems.Add(new BinInterpNode(bin.Position - stringLength - 4, $"Combiner String: {stringText}") { Length = stringLength + 4 }); + // Combiner entry ID: + int name = bin.ReadInt32(); + unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Combiner ID: {name} {combinerListNames[name]}") { Length = 4 }); + // Do the same for next strings if theres more than one. + for (int i = 1; i < stringCount; i++) + { + c++; + stringLength = bin.ReadInt32(); + stringText = bin.ReadEndianASCIIString(stringLength); + unkListCitems.Add(new BinInterpNode(bin.Position - stringLength - 4, $"Combiner String: {stringText}") { Length = stringLength + 4 }); + name = bin.ReadInt32(); + unkListCitems.Add(new BinInterpNode(bin.Position - 4, $"Combiner ID: {name} {combinerListNames[name]}") { Length = 4 }); + } + } + + if (game == MEGame.ME2) + { + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + } + + subnodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); + + int lineCount; + var lines = new List(); + subnodes.Add(new BinInterpNode(bin.Position, $"FaceFXLines: {lineCount = bin.ReadInt32()}") + { + Items = lines + }); + for (int i = 0; i < lineCount; i++) + { + var nodes = new List(); + lines.Add(new BinInterpNode(bin.Position, $"{i}") + { + Items = nodes + }); + nodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); + int animationCount = bin.ReadInt32(); + var anims = new List(); + nodes.Add(new BinInterpNode(bin.Position - 4, $"Animations: {animationCount} items") + { + Items = anims + }); + for (int j = 0; j < animationCount; j++) + { + var animNodes = new List(); + anims.Add(new BinInterpNode(bin.Position, $"{j}") + { + Items = animNodes + }); + animNodes.Add(new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 }); + animNodes.Add(MakeInt32Node(bin, "Unknown")); + if (game == MEGame.ME2) + { + animNodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + } + int pointsCount = bin.ReadInt32(); + nodes.Add(new BinInterpNode(bin.Position - 4, $"Points: {pointsCount} items") + { + Items = ReadList(pointsCount, j => new BinInterpNode(bin.Position, $"{j}") + { + Items = new List + { + new BinInterpNode(bin.Position, $"Time: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"Weight: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"InTangent: {bin.ReadFloat()}") {Length = 4}, + new BinInterpNode(bin.Position, $"LeaveTangent: {bin.ReadFloat()}") {Length = 4} + } + }) + }); + if (pointsCount > 0) + { + if (game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + + nodes.Add(new BinInterpNode(bin.Position, $"NumKeys: {bin.ReadInt32()} items") + { + Items = ReadList(bin.Skip(-4).ReadInt32(), j => new BinInterpNode(bin.Position, $"{bin.ReadInt32()} keys")) + }); + } + + nodes.Add(new BinInterpNode(bin.Position, $"Fade In Time: {bin.ReadFloat()}") { Length = 4 }); + nodes.Add(new BinInterpNode(bin.Position, $"Fade Out Time: {bin.ReadFloat()}") { Length = 4 }); + nodes.Add(MakeInt32Node(bin, "Unknown")); + if (game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + + nodes.Add(new BinInterpNode(bin.Position, $"Path: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + if (game == MEGame.ME2) + { + nodes.Add(new BinInterpNode(bin.Position, $"Unknown: {bin.ReadInt16()}") { Length = 2 }); + } + + nodes.Add(new BinInterpNode(bin.Position, $"ID: {bin.BaseStream.ReadStringLatin1(bin.ReadInt32())}")); + nodes.Add(MakeInt32Node(bin, "index")); + } + + subnodes.Add(MakeInt32Node(bin, "unknown")); + + subnodes.Add(MakeArrayNode(bin, "Unknown Table D - Mapping?", i => new BinInterpNode(bin.Position, $"{i}") + { + IsExpanded = true, + Items = + { + new BinInterpNode(bin.Position, $"Column Id?: {bin.ReadInt32()}") {Length = 4}, + new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") {Length = 4}, + MakeFloatNode(bin, "Unk Float") + } + })); + subnodes.Add(MakeArrayNode(bin, "Lip sync phoneme list:", i => new BinInterpNode(bin.Position, $"Name: {nameTable[bin.ReadInt32()]}") { Length = 4 })); + subnodes.Add(MakeInt32Node(bin, "Unknown")); + if (game is MEGame.LE1 or MEGame.LE2) + { + subnodes.Add(MakeArrayNode(bin, "Unknown Ints", i => new BinInterpNode(bin.Position, $"Unknown: {nameTable[bin.ReadInt32()]}") { Length = 4 })); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs index 2ee5d5536c..ba8727b61d 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using LegendaryExplorer.SharedUI.Interfaces; using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls { @@ -180,14 +178,14 @@ private BinInterpNode MakeMaterialResourceNode(EndianReader bin, string name, Di List nodes = node.Items; try { - nodes.Add(MakeArrayNode(bin, "Compile Errors", i => MakeStringNode(bin, $"{i}"))); - nodes.Add(MakeArrayNode(bin, "TextureDependencyLengthMap", i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}: {bin.ReadInt32()}"))); + nodes.Add(MakeArrayNode(bin, "Compile Errors", i => MakeStringNode(bin, $"{i}", Pcc.Game))); + nodes.Add(MakeArrayNode(bin, "TextureDependencyLengthMap", i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}: {bin.ReadInt32()}"))); nodes.Add(MakeInt32Node(bin, "MaxTextureDependencyLength")); nodes.Add(MakeMaterialGuidNode(bin, "ID", materialGuidMap)); nodes.Add(MakeUInt32Node(bin, "NumUserTexCoords")); if (Pcc.Game >= MEGame.ME3) { - nodes.Add(MakeArrayNode(bin, "UniformExpressionTextures", i => MakeEntryNode(bin, $"{i}"))); + nodes.Add(MakeArrayNode(bin, "UniformExpressionTextures", i => MakeEntryNode(bin, $"{i}", Pcc))); } else { @@ -281,5 +279,122 @@ private BinInterpNode MakeMaterialResourceNode(EndianReader bin, string name, Di } return node; } + + private BinInterpNode ReadMaterialUniformExpression(EndianReader bin, string prefix = "") + { + NameReference expressionType = bin.ReadNameReference(Pcc); + var node = new BinInterpNode(bin.Position - 8, $"{prefix}{(string.IsNullOrEmpty(prefix) ? "" : ": ")}{expressionType.Instanced}"); + + switch (expressionType.Name) + { + case "FMaterialUniformExpressionAbs": + case "FMaterialUniformExpressionCeil": + case "FMaterialUniformExpressionFloor": + case "FMaterialUniformExpressionFrac": + case "FMaterialUniformExpressionPeriodic": + case "FMaterialUniformExpressionSquareRoot": + node.Items.Add(ReadMaterialUniformExpression(bin, "X")); + break; + case "FMaterialUniformExpressionAppendVector": + node.Items.Add(ReadMaterialUniformExpression(bin, "A")); + node.Items.Add(ReadMaterialUniformExpression(bin, "B")); + node.Items.Add(MakeUInt32Node(bin, "NumComponentsA:")); + break; + case "FMaterialUniformExpressionClamp": + node.Items.Add(ReadMaterialUniformExpression(bin, "Input")); + node.Items.Add(ReadMaterialUniformExpression(bin, "Min")); + node.Items.Add(ReadMaterialUniformExpression(bin, "Max")); + break; + case "FMaterialUniformExpressionConstant": + node.Items.Add(MakeFloatNode(bin, "R")); + node.Items.Add(MakeFloatNode(bin, "G")); + node.Items.Add(MakeFloatNode(bin, "B")); + node.Items.Add(MakeFloatNode(bin, "A")); + node.Items.Add(MakeByteNode(bin, "ValueType")); + break; + case "FMaterialUniformExpressionFmod": + case "FMaterialUniformExpressionMax": + case "FMaterialUniformExpressionMin": + node.Items.Add(ReadMaterialUniformExpression(bin, "A")); + node.Items.Add(ReadMaterialUniformExpression(bin, "B")); + break; + case "FMaterialUniformExpressionFoldedMath": + node.Items.Add(ReadMaterialUniformExpression(bin, "A")); + node.Items.Add(ReadMaterialUniformExpression(bin, "B")); + node.Items.Add(new BinInterpNode(bin.Position, $"Op: {(EFoldedMathOperation)bin.ReadByte()}")); + break; + case "FMaterialUniformExpressionRealTime": + //intentionally left blank. outputs current real-time, has no parameters + break; + case "FMaterialUniformExpressionScalarParameter": + node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); + node.Items.Add(MakeFloatNode(bin, "DefaultValue")); + break; + case "FMaterialUniformExpressionSine": + node.Items.Add(ReadMaterialUniformExpression(bin, "X")); + node.Items.Add(MakeBoolIntNode(bin, "bIsCosine")); + break; + case "FMaterialUniformExpressionTexture": + case "FMaterialUniformExpressionFlipBookTextureParameter": + if (Pcc.Game >= MEGame.ME3) + { + node.Items.Add(MakeInt32Node(bin, "TextureIndex")); + } + else + { + node.Items.Add(MakeEntryNode(bin, "TextureIndex", Pcc)); + } + break; + case "FMaterialUniformExpressionFlipbookParameter": + node.Items.Add(MakeInt32Node(bin, "Index:")); + node.Items.Add(MakeEntryNode(bin, "TextureIndex", Pcc)); + break; + case "FMaterialUniformExpressionTextureParameter": + node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); + node.Items.Add(MakeInt32Node(bin, "TextureIndex")); + break; + case "FMaterialUniformExpressionTime": + //intentionally left blank. outputs current scene time, has no parameters + break; + case "FMaterialUniformExpressionVectorParameter": + node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); + node.Items.Add(MakeFloatNode(bin, "Default R")); + node.Items.Add(MakeFloatNode(bin, "Default G")); + node.Items.Add(MakeFloatNode(bin, "Default B")); + node.Items.Add(MakeFloatNode(bin, "Default A")); + break; + case "FMaterialUniformExpressionFractionOfEffectEnabled": + //Not sure what it does, but it doesn't seem to have any parameters + break; + default: + throw new ArgumentException(expressionType.Instanced); + } + + return node; + } + + private enum EFoldedMathOperation : byte + { + Add, + Sub, + Mul, + Div, + Dot + } + + [Flags] + public enum ECoordTransformUsage : uint + { + // no transforms used + UsedCoord_None = 0, + // local to world used + UsedCoord_World = 1 << 0, + // local to view used + UsedCoord_View = 1 << 1, + // local to local used + UsedCoord_Local = 1 << 2, + // World Position used + UsedCoord_WorldPos = 1 << 3 + } } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs index a033512e97..2b4d983780 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs @@ -7,6 +7,7 @@ using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Unreal.Classes; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -23,7 +24,7 @@ private IEnumerable StartStaticMeshScan(byte[] data, ref int binaryst bin.JumpTo(binarystart); subnodes.Add(MakeBoxSphereBoundsNode(bin, "Bounds")); - subnodes.Add(MakeEntryNode(bin, "BodySetup")); + subnodes.Add(MakeEntryNode(bin, "BodySetup", Pcc)); subnodes.Add(MakekDOPTreeNode(bin)); subnodes.Add(MakeInt32Node(bin, "InternalVersion")); @@ -110,7 +111,7 @@ private IEnumerable StartStaticMeshScan(byte[] data, ref int binaryst { matOffsets.Add(bin.Position); BinInterpNode node = new BinInterpNode(bin.Position, $"{j}"); - node.Items.Add(MakeEntryNode(bin, "Material")); + node.Items.Add(MakeEntryNode(bin, "Material", Pcc)); node.Items.Add(MakeBoolIntNode(bin, "EnableCollision")); node.Items.Add(MakeBoolIntNode(bin, "OldEnableCollision")); node.Items.Add(MakeBoolIntNode(bin, "bEnableShadowCasting")); @@ -270,7 +271,7 @@ private IEnumerable StartStaticMeshScan(byte[] data, ref int binaryst } if (Pcc.Game >= MEGame.ME3) { - subnodes.Add(MakeStringNode(bin, "HighResSourceMeshName")); + subnodes.Add(MakeStringNode(bin, "HighResSourceMeshName", Pcc.Game)); subnodes.Add(MakeUInt32Node(bin, "HighResSourceMeshCRC")); subnodes.Add(MakeGuidNode(bin, "LightingGuid")); } @@ -291,7 +292,7 @@ private IEnumerable StartStaticMeshScan(byte[] data, ref int binaryst Items = ReadList(matOffsets.Count, i => { bin.JumpTo(matOffsets[i]); - var matNode = MakeEntryNode(bin, $"Material[{i}]"); + var matNode = MakeEntryNode(bin, $"Material[{i}]", Pcc); try { if (Pcc.GetEntry(bin.Skip(-4).ReadInt32()) is ExportEntry matExport) @@ -327,7 +328,7 @@ private IEnumerable StartFracturedStaticMeshScan(byte[] data, ref int var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - subnodes.Add(MakeEntryNode(bin, "SourceStaticMesh")); + subnodes.Add(MakeEntryNode(bin, "SourceStaticMesh", Pcc)); subnodes.Add(MakeArrayNode(bin, "Fragments", i => new BinInterpNode(bin.Position, $"{i}") { Items = @@ -462,4 +463,251 @@ private BinInterpNode MakekDOPTreeNode(EndianReader bin) } }; } + + private List StartSkeletalMeshScan(byte[] data, ref int binarystart) + { + var subnodes = new List(); + var game = CurrentLoadedExport.FileRef.Game; + try + { + PackageCache cache = new PackageCache(); + var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; + bin.JumpTo(binarystart); + + subnodes.Add(MakeBoxSphereBoundsNode(bin, "Bounds")); + subnodes.Add(MakeArrayNode(bin, "Materials", i => + { + var matNode = MakeEntryNode(bin, $"{i}", Pcc); + try + { + var value = bin.Skip(-4).ReadInt32(); + if (value != 0 && Pcc.GetEntry(value) is ExportEntry matExport) + { + foreach (IEntry texture in MaterialInstanceConstant.GetTextures(matExport, cache)) + { + matNode.Items.Add(new BinInterpNode(-1, $"#{texture.UIndex} {texture.FileRef.GetEntryString(texture.UIndex)}", NodeType.StructLeafObject) { UIndexValue = texture.UIndex }); + } + } + } + catch + { + matNode.Items.Add(new BinInterpNode("Error reading Material!")); + } + + return matNode; + }, true, BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes)); + subnodes.Add(MakeVectorNode(bin, "Origin")); + subnodes.Add(MakeRotatorNode(bin, "Rotation Origin")); + subnodes.Add(MakeArrayNode(bin, "RefSkeleton", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadNameReference(Pcc).Instanced}") + { + Items = + { + MakeUInt32Node(bin, "Flags"), + MakeQuatNode(bin, "Bone Orientation (quaternion)"), + MakeVectorNode(bin, "Bone Position"), + MakeInt32Node(bin, "NumChildren"), + MakeInt32Node(bin, "ParentIndex"), + ListInitHelper.ConditionalAddOne( Pcc.Game >= MEGame.ME3, () => MakeColorNode(bin, "BoneColor")), + } + })); + subnodes.Add(MakeInt32Node(bin, "SkeletalDepth")); + int rawPointIndicesCount; + bool useFullPrecisionUVs = true; + int numTexCoords = 1; + subnodes.Add(MakeArrayNode(bin, "LODModels", i => + { + BinInterpNode node = new BinInterpNode(bin.Position, $"{i}"); + try + { + node.Items.Add(MakeArrayNode(bin, "Sections", j => new BinInterpNode(bin.Position, $"{j}") + { + Items = + { + MakeUInt16Node(bin, "MaterialIndex"), + MakeUInt16Node(bin, "ChunkIndex"), + MakeUInt32Node(bin, "BaseIndex"), + ListInitHelper.ConditionalAddOne(Pcc.Game >= MEGame.ME3, + () => MakeUInt32Node(bin, "NumTriangles"), + () => MakeUInt16Node(bin, "NumTriangles")), + ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeByteNode(bin, "TriangleSorting")) + } + })); + node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] + { + MakeBoolIntNode(bin, "NeedsCPUAccess"), + MakeByteNode(bin, "Datatype size"), + })); + node.Items.Add(MakeInt32Node(bin, "Index size?")); + if (Pcc.Game == MEGame.UDK && bin.Skip(-4).ReadInt32() == 4) + { + node.Items.Add(MakeArrayNode(bin, "IndexBuffer", j => MakeUInt32Node(bin, $"{j}"))); + } + else + { + node.Items.Add(MakeArrayNode(bin, "IndexBuffer", j => MakeUInt16Node(bin, $"{j}"))); + } + node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "ShadowIndices", j => MakeUInt16Node(bin, $"{j}")))); + node.Items.Add(MakeArrayNode(bin, "ActiveBoneIndices", j => MakeUInt16Node(bin, $"{j}"))); + node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "ShadowTriangleDoubleSided", j => MakeByteNode(bin, $"{j}")))); + node.Items.Add(MakeArrayNode(bin, "Chunks", j => new BinInterpNode(bin.Position, $"{j}") + { + Items = + { + MakeUInt32Node(bin, "BaseVertexIndex"), + MakeArrayNode(bin, "RigidVertices", k => new BinInterpNode(bin.Position, $"{k}") + { + Items = + { + MakeVectorNode(bin, "Position"), + MakePackedNormalNode(bin, "TangentX"), + MakePackedNormalNode(bin, "TangentY"), + MakePackedNormalNode(bin, "TangentZ"), + MakeVector2DNode(bin, "UV"), + ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] + { + MakeVector2DNode(bin, "UV2"), + MakeVector2DNode(bin, "UV3"), + MakeVector2DNode(bin, "UV4"), + MakeColorNode(bin, "BoneColor"), + }), + MakeByteNode(bin, "Bone") + } + }), + MakeArrayNode(bin, "SoftVertices", k => new BinInterpNode(bin.Position, $"{k}") + { + Items = + { + MakeVectorNode(bin, "Position"), + MakePackedNormalNode(bin, "TangentX"), + MakePackedNormalNode(bin, "TangentY"), + MakePackedNormalNode(bin, "TangentZ"), + MakeVector2DNode(bin, "UV"), + ListInitHelper.ConditionalAdd(Pcc.Game == MEGame.UDK, () => new ITreeItem[] + { + MakeVector2DNode(bin, "UV2"), + MakeVector2DNode(bin, "UV3"), + MakeVector2DNode(bin, "UV4"), + MakeColorNode(bin, "BoneColor"), + }), + new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceBones[{l}]"))), + new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceWeights[{l}]"))) + } + }), + MakeArrayNode(bin, "BoneMap", k => MakeUInt16Node(bin, $"{k}")), + MakeInt32Node(bin, "NumRigidVertices"), + MakeInt32Node(bin, "NumSoftVertices"), + MakeInt32Node(bin, "MaxBoneInfluences"), + } + })); + node.Items.Add(MakeUInt32Node(bin, "Size")); + node.Items.Add(MakeUInt32Node(bin, "NumVertices")); + node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game != MEGame.UDK, () => MakeArrayNode(bin, "Edges", j => new BinInterpNode(bin.Position, $"{j}") + { + Items = + { + MakeInt32Node(bin, "Vertices[0]"), + MakeInt32Node(bin, "Vertices[1]"), + MakeInt32Node(bin, "Faces[0]"), + MakeInt32Node(bin, "Faces[1]"), + } + }))); + node.Items.Add(MakeArrayNode(bin, "RequiredBones", j => MakeByteNode(bin, $"{j}"))); + node.Items.Add(MakeUInt32Node(bin, "RawPointIndices BulkDataFlags")); + node.Items.Add(new BinInterpNode(bin.Position, $"RawPointIndices Count: {rawPointIndicesCount = bin.ReadInt32()}")); + node.Items.Add(MakeUInt32Node(bin, "RawPointIndices size")); + node.Items.Add(MakeUInt32Node(bin, "RawPointIndices file offset")); + node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, + () => MakeArrayNode(rawPointIndicesCount, bin, "RawPointIndices", k => MakeInt32Node(bin, $"{k}")), + () => MakeArrayNode(rawPointIndicesCount, bin, "RawPointIndices", k => MakeUInt16Node(bin, $"{k}")))); + node.Items.Add(ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeInt32Node(bin, "NumTexCoords"))); + BinInterpNode item = new BinInterpNode(bin.Position, "VertexBufferGPUSkin") + { + IsExpanded = true + }; + node.Items.Add(item); + item.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game != MEGame.ME1, () => new List + { + ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.UDK, () => MakeInt32Node(bin, "NumTexCoords", out numTexCoords)), + MakeBoolIntNode(bin, "bUseFullPrecisionUVs", out useFullPrecisionUVs), + ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new ITreeItem[] + { + MakeBoolIntNode(bin, "bUsePackedPosition"), + MakeVectorNode(bin, "MeshExtension"), + MakeVectorNode(bin, "MeshOrigin"), + }), + })); + item.Items.Add(MakeInt32Node(bin, "vertex size")); + item.Items.Add(MakeArrayNode(bin, "VertexData", k => new BinInterpNode(bin.Position, $"{k}") + { + Items = + { + ListInitHelper.ConditionalAddOne(Pcc.Game <= MEGame.ME2, () => MakeVectorNode(bin, "Position")), + MakePackedNormalNode(bin, "TangentX"), + ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.ME1, () => MakePackedNormalNode(bin, "TangentY")), + MakePackedNormalNode(bin, "TangentZ"), + ListInitHelper.ConditionalAddOne(Pcc.Game == MEGame.ME1, () => MakeVector2DNode(bin, "UV")), + new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceBones[{l}]"))), + new ListInitHelper.InitCollection(ReadList(4, l => MakeByteNode(bin, $"InfluenceWeights[{l}]"))), + ListInitHelper.ConditionalAddOne(Pcc.Game >= MEGame.ME3, () => MakeVectorNode(bin, "Position")), + ListInitHelper.ConditionalAdd(Pcc.Game != MEGame.ME1, + () => ListInitHelper.ConditionalAddOne(useFullPrecisionUVs, + () => MakeVector2DNode(bin, "UV"), + () => MakeVector2DHalfNode(bin, "UV"))), + ListInitHelper.ConditionalAddOne(numTexCoords > 1, () => MakeArrayNode(numTexCoords - 1, bin, "Additional UVs", + i => useFullPrecisionUVs ? MakeVector2DNode(bin, "UV") : MakeVector2DHalfNode(bin, "UV"))) + } + })); + int vertexInfluenceSize; + node.Items.Add(ListInitHelper.ConditionalAdd(Pcc.Game >= MEGame.ME3, () => new List + { + new BinInterpNode(bin.Position, $"VertexInfluence size: {vertexInfluenceSize = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }, + ListInitHelper.ConditionalAdd(vertexInfluenceSize > 0, () => new ITreeItem[] + { + MakeArrayNode(bin, "VertexInfluences", i => MakeInt32Node(bin, $"{i}")), + MakeInt32Node(bin, "Unknown") + }) + })); + if (Pcc.Game is MEGame.UDK) + { + node.Items.Add(MakeBoolIntNode(bin, "NeedsCPUAccess")); + node.Items.Add(MakeByteNode(bin, "Datatype size")); + node.Items.Add(MakeInt32Node(bin, "index size", out int indexSize)); + if (indexSize == 4) + { + node.Items.Add(MakeArrayNode(bin, "Second IndexBuffer?", j => MakeUInt32Node(bin, $"{j}"))); + } + else + { + node.Items.Add(MakeArrayNode(bin, "Second IndexBuffer?", j => MakeUInt16Node(bin, $"{j}"))); + } + } + } + catch (Exception e) + { + node.Items.Add(new BinInterpNode { Header = $"Error reading binary data: {e}" }); + } + return node; + }, true)); + subnodes.Add(MakeArrayNode(bin, "NameIndexMap", i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc).Instanced} => {bin.ReadInt32()}"))); + subnodes.Add(MakeArrayNode(bin, "PerPolyBoneKDOPs", i => new BinInterpNode(bin.Position, $"{i}") + { + Items = + { + MakekDOPTreeNode(bin), + MakeArrayNode(bin, "CollisionVerts", j => MakeVectorNode(bin, $"{j}")) + } + })); + if (Pcc.Game >= MEGame.ME3) + { + subnodes.Add(MakeArrayNode(bin, "BoneBreakNames", i => new BinInterpNode(bin.Position, $"{i}: {bin.ReadUnrealString()}"))); + subnodes.Add(MakeArrayNode(bin, "ClothingAssets", i => MakeEntryNode(bin, $"{i}", Pcc))); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeType.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeType.cs new file mode 100644 index 0000000000..12a1098755 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeType.cs @@ -0,0 +1,47 @@ +namespace LegendaryExplorer.UserControls.ExportLoaderControls; + +public enum NodeType : sbyte +{ + Unknown = -1, + StructProperty = 0, + IntProperty = 1, + FloatProperty = 2, + ObjectProperty = 3, + NameProperty = 4, + BoolProperty = 5, + ByteProperty = 6, + ArrayProperty = 7, + StrProperty = 8, + StringRefProperty = 9, + DelegateProperty = 10, + None, + BioMask4Property, + + ArrayLeafObject, + ArrayLeafName, + ArrayLeafEnum, + ArrayLeafStruct, + ArrayLeafBool, + ArrayLeafString, + ArrayLeafFloat, + ArrayLeafInt, + ArrayLeafByte, + + StructLeafByte, + StructLeafFloat, + StructLeafDeg, //indicates this is a StructProperty leaf that is in degrees (actually unreal rotation units) + StructLeafInt, + StructLeafObject, + StructLeafName, + StructLeafBool, + StructLeafStr, + StructLeafArray, + StructLeafEnum, + StructLeafStruct, + + // For right clicking things. + Guid, + + Root, + ReferenceToOffset +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ScriptObjectScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ScriptObjectScans.cs index 9c90dc50f2..f034fa5221 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ScriptObjectScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ScriptObjectScans.cs @@ -6,6 +6,7 @@ using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -92,8 +93,8 @@ private List StartPropertyScan(byte[] data, ref int binaryStart) if (Pcc.Platform is MEPackage.GamePlatform.PC || (Pcc.Game is not MEGame.ME3 && Pcc.Platform is MEPackage.GamePlatform.Xenon)) { // This seems missing on Xenon 2011. Not sure about others - subnodes.Add(MakeNameNode(bin, "Category")); - subnodes.Add(MakeEntryNode(bin, "ArraySizeEnum")); + subnodes.Add(MakeNameNode(bin, "Category", Pcc)); + subnodes.Add(MakeEntryNode(bin, "ArraySizeEnum", Pcc)); } if (ObjectFlagsMask.HasFlag(UnrealFlags.EPropertyFlags.Net)) @@ -110,19 +111,19 @@ private List StartPropertyScan(byte[] data, ref int binaryStart) case "ComponentProperty": case "ArrayProperty": case "InterfaceProperty": - subnodes.Add(MakeEntryNode(bin, "Holds objects of type")); + subnodes.Add(MakeEntryNode(bin, "Holds objects of type", Pcc)); break; case "DelegateProperty": - subnodes.Add(MakeEntryNode(bin, "Holds objects of type")); - subnodes.Add(MakeEntryNode(bin, "Same as above but only if this is in a function or struct")); + subnodes.Add(MakeEntryNode(bin, "Holds objects of type", Pcc)); + subnodes.Add(MakeEntryNode(bin, "Same as above but only if this is in a function or struct", Pcc)); break; case "ClassProperty": - subnodes.Add(MakeEntryNode(bin, "Outer class")); - subnodes.Add(MakeEntryNode(bin, "Class type")); + subnodes.Add(MakeEntryNode(bin, "Outer class", Pcc)); + subnodes.Add(MakeEntryNode(bin, "Class type", Pcc)); break; case "MapProperty": - subnodes.Add(MakeEntryNode(bin, "Key Type")); - subnodes.Add(MakeEntryNode(bin, "Value Type")); + subnodes.Add(MakeEntryNode(bin, "Key Type", Pcc)); + subnodes.Add(MakeEntryNode(bin, "Value Type", Pcc)); break; } } @@ -137,9 +138,9 @@ private IEnumerable MakeUFieldNodes(EndianReader bin) { if (Pcc.Game is not MEGame.UDK) { - yield return MakeEntryNode(bin, "SuperClass"); + yield return MakeEntryNode(bin, "SuperClass", Pcc); } - yield return MakeEntryNode(bin, "Next item in compiling chain"); + yield return MakeEntryNode(bin, "Next item in compiling chain", Pcc); } private IEnumerable MakeUStructNodes(EndianReader bin) @@ -150,16 +151,16 @@ private IEnumerable MakeUStructNodes(EndianReader bin) } if (Pcc.Game is MEGame.UDK) { - yield return MakeEntryNode(bin, "SuperClass"); + yield return MakeEntryNode(bin, "SuperClass", Pcc); } if (Pcc.Game is MEGame.ME1 or MEGame.ME2 or MEGame.UDK && Pcc.Platform != MEPackage.GamePlatform.PS3) { - yield return MakeEntryNode(bin, "ScriptText"); + yield return MakeEntryNode(bin, "ScriptText", Pcc); } - yield return MakeEntryNode(bin, "ChildListStart"); + yield return MakeEntryNode(bin, "ChildListStart", Pcc); if (Pcc.Game is MEGame.ME1 or MEGame.ME2 or MEGame.UDK && Pcc.Platform != MEPackage.GamePlatform.PS3) { - yield return MakeEntryNode(bin, "C++ Text"); + yield return MakeEntryNode(bin, "C++ Text", Pcc); yield return MakeInt32Node(bin, "Source file line number"); yield return MakeInt32Node(bin, "Source file text position"); } @@ -287,12 +288,12 @@ private List StartClassScan(byte[] data) { subnodes.Add(MakeByteNode(bin, "Unknown byte")); } - subnodes.Add(MakeEntryNode(bin, "Outer Class")); - subnodes.Add(MakeNameNode(bin, "Class Config Name")); + subnodes.Add(MakeEntryNode(bin, "Outer Class", Pcc)); + subnodes.Add(MakeNameNode(bin, "Class Config Name", Pcc)); if (Pcc.Game <= MEGame.ME2 && Pcc.Platform != MEPackage.GamePlatform.PS3) { - subnodes.Add(MakeArrayNode(bin, "Unknown name list 1", i => MakeNameNode(bin, $"{i}"))); + subnodes.Add(MakeArrayNode(bin, "Unknown name list 1", i => MakeNameNode(bin, $"{i}", Pcc))); } subnodes.Add(MakeArrayNode(bin, "Component Table", i => @@ -302,18 +303,18 @@ private List StartClassScan(byte[] data) if (Pcc.Game is MEGame.UDK) { - subnodes.Add(MakeArrayNode(bin, "DontSortCategories", i => MakeNameNode(bin, $"{i}"))); - subnodes.Add(MakeArrayNode(bin, "HideCategories", i => MakeNameNode(bin, $"{i}"))); - subnodes.Add(MakeArrayNode(bin, "AutoExpandCategories", i => MakeNameNode(bin, $"{i}"))); - subnodes.Add(MakeArrayNode(bin, "AutoCollapseCategories", i => MakeNameNode(bin, $"{i}"))); + subnodes.Add(MakeArrayNode(bin, "DontSortCategories", i => MakeNameNode(bin, $"{i}", Pcc))); + subnodes.Add(MakeArrayNode(bin, "HideCategories", i => MakeNameNode(bin, $"{i}", Pcc))); + subnodes.Add(MakeArrayNode(bin, "AutoExpandCategories", i => MakeNameNode(bin, $"{i}", Pcc))); + subnodes.Add(MakeArrayNode(bin, "AutoCollapseCategories", i => MakeNameNode(bin, $"{i}", Pcc))); subnodes.Add(MakeBoolIntNode(bin, "bForceScriptOrder")); - subnodes.Add(MakeArrayNode(bin, "Unknown name list", i => MakeNameNode(bin, $"{i}"))); - subnodes.Add(MakeStringNode(bin, "Class Name?")); + subnodes.Add(MakeArrayNode(bin, "Unknown name list", i => MakeNameNode(bin, $"{i}", Pcc))); + subnodes.Add(MakeStringNode(bin, "Class Name?", Pcc.Game)); } if (Pcc.Game >= MEGame.ME3 || Pcc.Platform == MEPackage.GamePlatform.PS3) { - subnodes.Add(MakeNameNode(bin, "DLL Bind Name")); + subnodes.Add(MakeNameNode(bin, "DLL Bind Name", Pcc)); if (Pcc.Game is not MEGame.UDK) { subnodes.Add(MakeUInt32Node(bin, "Unknown")); @@ -321,17 +322,17 @@ private List StartClassScan(byte[] data) } else { - subnodes.Add(MakeArrayNode(bin, "Unknown name list 2", i => MakeNameNode(bin, $"{i}"))); + subnodes.Add(MakeArrayNode(bin, "Unknown name list 2", i => MakeNameNode(bin, $"{i}", Pcc))); } if (Pcc.Game is MEGame.LE2 || Pcc.Platform == MEPackage.GamePlatform.PS3 && Pcc.Game == MEGame.ME2) { subnodes.Add(MakeUInt32Node(bin, "LE2 & PS3 ME2 Unknown")); } - subnodes.Add(MakeEntryNode(bin, "Defaults")); + subnodes.Add(MakeEntryNode(bin, "Defaults", Pcc)); if (Pcc.Game.IsGame3()) { - subnodes.Add(MakeArrayNode(bin, "Virtual Function Table", i => MakeEntryNode(bin, $"{i}: "))); + subnodes.Add(MakeArrayNode(bin, "Virtual Function Table", i => MakeEntryNode(bin, $"{i}: ", Pcc))); } } catch (Exception ex) @@ -375,7 +376,7 @@ private List StartEnumScan(byte[] data, ref int binaryStart) NameReference enumName = CurrentLoadedExport.ObjectName; for (int i = 0; i < enumCount; i++) { - subnodes.Add(MakeNameNode(bin, $"{enumName}[{i}]")); + subnodes.Add(MakeNameNode(bin, $"{enumName}[{i}]", Pcc)); } } catch (Exception ex) @@ -395,7 +396,7 @@ private List StartConstScan(byte[] data, ref int binaryStart) bin.Skip(binaryStart); subnodes.AddRange(MakeUFieldNodes(bin)); - subnodes.Add(MakeStringNode(bin, "Literal Value")); + subnodes.Add(MakeStringNode(bin, "Literal Value", Pcc.Game)); } catch (Exception ex) { @@ -460,7 +461,7 @@ private List StartFunctionScan(byte[] data, ref int binaryStart) } if ((Pcc.Game.IsGame1() || Pcc.Game.IsGame2() || Pcc.Game is MEGame.UDK) && Pcc.Platform != MEPackage.GamePlatform.PS3) { - subnodes.Add(MakeNameNode(bin, "FriendlyName")); + subnodes.Add(MakeNameNode(bin, "FriendlyName", Pcc)); } } catch (Exception ex) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ShaderCacheScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ShaderCacheScans.cs index 97f0333bb8..97362717eb 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ShaderCacheScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ShaderCacheScans.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Composition.Primitives; using System.Diagnostics; -using System.IO; using LegendaryExplorer.SharedUI.Interfaces; using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -262,7 +261,7 @@ void ReadVertexFactoryMap() nodes.Add(new BinInterpNode(bin.Position, $"MaterialId: {bin.ReadGuid()}") { Length = 16 }); - nodes.Add(MakeStringNode(bin, "Friendly Name")); + nodes.Add(MakeStringNode(bin, "Friendly Name", Pcc.Game)); nodes.Add(ReadFStaticParameterSet(bin)); @@ -311,7 +310,7 @@ void ReadVertexFactoryMap() subnodes.Add(shaderCachePayloads); for (int i = 0; i < numShaderCachePayloads; i++) { - shaderCachePayloads.Items.Add(MakeEntryNode(bin, $"Payload {i}")); + shaderCachePayloads.Items.Add(MakeEntryNode(bin, $"Payload {i}", Pcc)); } } else if (CurrentLoadedExport.Game == MEGame.ME1 && CurrentLoadedExport.FileRef.Platform != MEPackage.GamePlatform.PS3) @@ -322,7 +321,7 @@ void ReadVertexFactoryMap() for (int i = 0; i < numSomething; i++) { var node = new BinInterpNode(bin.Position, $"Something {i}"); - node.Items.Add(MakeNameNode(bin, "SomethingName?")); + node.Items.Add(MakeNameNode(bin, "SomethingName?", Pcc)); node.Items.Add(MakeGuidNode(bin, "SomethingGuid?")); somethings.Items.Add(node); } @@ -1578,7 +1577,7 @@ BinInterpNode FVertexFactoryParameterRef() { Items = { - MakeNameNode(bin, "VertexFactoryType", out var vertexFactoryName), + MakeNameNode(bin, "VertexFactoryType", Pcc, out var vertexFactoryName), MakeUInt32HexNode(bin, "File offset to end of FVertexFactoryParameterRef (may be inaccurate in modded files)") }, IsExpanded = true diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/TextureScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/TextureScans.cs index ffe79e79b4..218416e0b3 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/TextureScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/TextureScans.cs @@ -3,10 +3,10 @@ using System.IO; using LegendaryExplorer.SharedUI.Interfaces; using LegendaryExplorerCore.Gammtek.IO; -using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls { diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index d46b9fd794..ab4f35352b 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -1,24 +1,24 @@ -using DocumentFormat.OpenXml.Math; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; using LegendaryExplorer.SharedUI.Interfaces; -using LegendaryExplorer.UnrealExtensions; using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; -using LegendaryExplorerCore.Unreal.BinaryConverters; using ME3Tweaks.Wwiser.Formats; using ME3Tweaks.Wwiser.Model; +using ME3Tweaks.Wwiser.Model.Action; +using ME3Tweaks.Wwiser.Model.Action.Specific; +using ME3Tweaks.Wwiser.Model.GlobalSettings; using ME3Tweaks.Wwiser.Model.Hierarchy; using ME3Tweaks.Wwiser.Model.Hierarchy.Enums; using ME3Tweaks.Wwiser.Model.ParameterNode; using ME3Tweaks.Wwiser.Model.ParameterNode.Positioning; using ME3Tweaks.Wwiser.Model.RTPC; -using System; -using System.Collections.Generic; -using System.IO; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.AccumType; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.CurveScaling; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.GroupType; -using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.ParameterId; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.PriorityOverrideFlags; using static ME3Tweaks.Wwiser.Model.Hierarchy.MediaInformation; using static ME3Tweaks.Wwiser.Model.Hierarchy.RanSeqFlags; @@ -28,22 +28,24 @@ using static ME3Tweaks.Wwiser.Model.ParameterNode.Positioning.PositioningChunk; using static ME3Tweaks.Wwiser.Model.RTPC.RtpcType; using static ME3Tweaks.Wwiser.Model.State.SyncType; +using LanguageId = ME3Tweaks.Wwiser.Model.LanguageId; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; -public partial class BinaryInterpreterWPF +public class WwiseBankScans { private record WwiseItem(uint Id, string Name, long Position); private Dictionary WwiseIdMap = new(); + private List<(BinInterpNodeOffsetReference, uint)> WwiseRefs = new(); + private BinInterpNode MakeWwiseIdNode(EndianReader bin, string refName, string nodeName = "ID") { - var id = bin.ReadUInt32(); - bin.Skip(-4); - var item = new WwiseItem(id, refName, bin.Position); - WwiseIdMap.Add(id, item); - var node = MakeUInt32Node(bin, nodeName); + var node = MakeUInt32Node(bin, nodeName, out var id); + var item = new WwiseItem(id, refName, bin.Position - 4); + WwiseIdMap[id] = item; return node; } @@ -51,15 +53,8 @@ private BinInterpNode MakeWwiseIdRefNode(EndianReader bin, string name) { var pos = bin.Position; var id = bin.ReadUInt32(); - var node = new BinInterpNode(pos, $"{name}: {id}") { Length = 4 }; - if (WwiseIdMap.TryGetValue(id, out var item)) - { - node.Header += $" (Ref to {item.Name})"; - } - else - { - node.Header += $" (Ref)"; - } + var node = new BinInterpNodeOffsetReference(pos, $"{name}: {id}") { Length = 4 }; + WwiseRefs.Add((node, id)); return node; } @@ -68,12 +63,17 @@ private BinInterpNode MakeWwiseUniNode(EndianReader bin, string name) var node = new BinInterpNode(bin.Position, $"{name}: ") { Length = 4 }; Span span = stackalloc byte[4]; var read = bin.BaseStream.Read(span); + if (read != 4) + { + node.Header += "Error reading data. Expected 4 bytes."; + return node; + } uint value = BitConverter.ToUInt32(span); if (value > 0x10000000) { // float var f = BitConverter.ToSingle(span); - node.Header += f.ToString(); + node.Header += f.ToString(CultureInfo.InvariantCulture); } else { @@ -95,30 +95,28 @@ private BinInterpNode MakeWwiseVarCountNode(EndianReader bin, string name) return node; } - private BinInterpNode MakeUInt32EnumNode(EndianReader bin, string name) where T : Enum - { - var value = bin.ReadUInt32(); - var parsedValue = Enum.GetName(typeof(T), value); - if (string.IsNullOrEmpty(parsedValue)) parsedValue = "None"; - return new BinInterpNode(bin.Position - 4, $"{name}: {parsedValue}") { Length = 4 }; - } - - private BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T : Enum + private static BinInterpNode MakeArrayNodeWwiseVarCount(EndianReader bin, string name, Func selector, bool IsExpanded = false, + BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) { - var value = bin.ReadByte(); - var parsedValue = Enum.GetName(typeof(T), value); - if (string.IsNullOrEmpty(parsedValue)) parsedValue = "None"; - return new BinInterpNode(bin.Position - 1, $"{name}: {parsedValue}") { Length = 1 }; + var pos = bin.Position; + uint count; + return new BinInterpNode(bin.Position, $"{name} ({count = VarCount.ReadResizingUint(bin.BaseStream)})") + { + IsExpanded = IsExpanded, + Items = ReadList((int)count, selector), + ArrayAddAlgorithm = arrayAddAlgo, + Length = (int)(bin.Position - pos) + }; } - private List Scan_WwiseBank(byte[] data) + public List Scan_WwiseBank(byte[] data, ExportEntry export) { var subnodes = new List(); WwiseIdMap.Clear(); - var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; - bin.JumpTo(CurrentLoadedExport.propsEnd()); + var bin = new EndianReader(new MemoryStream(data)) { Endian = export.FileRef.Endian }; + bin.JumpTo(export.propsEnd()); - if (Pcc.Game is MEGame.ME2 or MEGame.LE2) + if (export.Game is MEGame.ME2 or MEGame.LE2) { subnodes.Add(MakeUInt32Node(bin, "Unk1")); subnodes.Add(MakeUInt32Node(bin, "Unk2")); @@ -127,7 +125,7 @@ private List Scan_WwiseBank(byte[] data) return subnodes; } } - subnodes.Add(new BinInterpNode(bin.Position, $"BulkDataFlags: {(EBulkDataFlags)bin.ReadUInt32()}")); + subnodes.Add(new BinInterpNode(bin.Position, $"BulkDataFlags: {(BinaryInterpreterWPF.EBulkDataFlags)bin.ReadUInt32()}")); subnodes.Add(MakeInt32Node(bin, "Element Count", out int dataSize)); subnodes.Add(MakeInt32Node(bin, "BulkDataSizeOnDisk")); subnodes.Add(MakeUInt32HexNode(bin, "BulkDataOffsetInFile")); @@ -171,21 +169,47 @@ private List Scan_WwiseBank(byte[] data) case "INIT": Scan_WwiseBank_INIT(chunkNode, bin); break; + case "STMG": + Scan_WwiseBank_STMG(chunkNode, bin, version); + break; + case "ENVS": + Scan_WwiseBank_ENVS(chunkNode, bin, version); + break; + } // Just in case we don't parse chunk in full - jump to next chunk bin.JumpTo(start + size + 8); } + + // At the end of the file, fill in all the reference details + AddNodeReferences(); return subnodes; } + public void AddNodeReferences() + { + foreach(var (refNode, refId) in WwiseRefs) + { + if (WwiseIdMap.TryGetValue(refId, out var item)) + { + refNode.Header += $" (Ref to {item.Name})"; + refNode.OffsetTarget = (int)item.Position; + } + else + { + refNode.Header += $" (Ref to unknown)"; + } + } + WwiseRefs = new List<(BinInterpNodeOffsetReference, uint)>(); + } + private (uint, bool) Scan_WwiseBank_BKHD(BinInterpNode root, EndianReader bin, int size) { - var version = bin.ReadUInt32(); bool useFeedback = false; - bin.Skip(-4); - - root.Items.Add(MakeUInt32Node(bin, "WwiseVersion")); + uint version; + + root.Items.Add(MakeUInt32Node(bin, "WwiseVersion", out version)); root.Items.Add(MakeWwiseIdNode(bin, "SoundBank", "SoundBankId")); if(version <= 122) @@ -197,11 +221,9 @@ private List Scan_WwiseBank(byte[] data) root.Items.Add(MakeUInt32Node(bin, "LanguageIDStringHash")); } - if(version > 27 && version < 126) + if(version is > 27 and < 126) { - useFeedback = bin.ReadBoolByte(); - bin.Skip(-1); - root.Items.Add(MakeBoolByteNode(bin, "UseFeedback")); + root.Items.Add(MakeBoolByteNode(bin, "UseFeedback", out useFeedback)); } if(version > 126) @@ -248,37 +270,68 @@ private void Scan_WwiseBank_DIDX(BinInterpNode root, EndianReader bin, int size) private void Scan_WwiseBank_HIRC(BinInterpNode root, EndianReader bin, uint version, bool useFeedback) { - root.Items.Add(MakeArrayNode(bin, "Items", i => MakeHIRCNode(i, bin, version, useFeedback), IsExpanded: true)); + root.Items.Add(MakeArrayNode(bin, "Items", i => MakeHIRCNode(i, bin, version, useFeedback), isExpanded: true)); } - private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bool useFeedback) + public BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bool useFeedback) { var start = bin.Position; var root = new BinInterpNode(bin.Position, $"{index}: "); var type = HircSmartType.DeserializeStatic(bin.BaseStream, version); - - if(version <= 48) - { - root.Items.Add(new BinInterpNode(bin.Position - 4, $"Type: {type}") { Length = 4 }); - } - else - { - root.Items.Add(new BinInterpNode(bin.Position - 1, $"Type: {type}") { Length = 1 }); - } - - var fullSize = bin.ReadInt32() + (version <= 48 ? 8 : 5); - - bin.Skip(-4); - root.Items.Add(MakeUInt32Node(bin, "Size")); + var typeLen = (version <= 48) ? 4 : 1; + root.Items.Add(new BinInterpNode(bin.Position - typeLen, $"Type: {type}") { Length = typeLen }); + + + root.Items.Add(MakeUInt32Node(bin, "Size", out var fullSize)); + fullSize += (uint)(version <= 48 ? 8 : 5); root.Items.Add(MakeWwiseIdNode(bin, type.ToString())); root.Header += $"{type}"; - root.Length = fullSize; + root.Length = (int)fullSize; switch(type) { + case HircType.State: + if (version <= 56) + { + root.Items.Add(MakeFloatNode(bin, "Volume")); + root.Items.Add(MakeFloatNode(bin, "LFEVolume")); + root.Items.Add(MakeFloatNode(bin, "Pitch")); + root.Items.Add(MakeFloatNode(bin, "LPF")); + if(version <= 52) + { + root.Items.Add(MakeByteEnumNode(bin, "VolumeValueMeaning")); + root.Items.Add(MakeByteEnumNode(bin, "LFEValueMeaning")); + root.Items.Add(MakeByteEnumNode(bin, "PitchValueMeaning")); + root.Items.Add(MakeByteEnumNode(bin, "LPFValueMeaning")); + } + } + else + { + var propBPos = bin.Position; + var propCount = (version <= 126) ? bin.ReadByte() : (byte)bin.ReadUInt16(); + root.Items.Add(new BinInterpNode(propBPos, $"Prop Count: {propCount}") { Length = (int)(bin.Position - propBPos) }); + root.Items.Add(MakeArrayNode(propCount, bin, "ParameterIds", i => + { + var pidPos = bin.Position; + var (paramId, modParamId, customId) = ParameterId.DeserializeStatic(bin.BaseStream, version, false); // TODO: use modulator not handles on high versions! + var pidLength = (int)(bin.Position - pidPos); + if (customId != null) + { + return new BinInterpNode(pidPos, $"CustomId: {customId}") { Length = pidLength }; + } + return paramId.HasValue + ? new BinInterpNode(pidPos, $"ParameterId: {Enum.GetName(paramId.Value)}") + { Length = pidLength } + : new BinInterpNode(pidPos, $"ModulatorParameterId: {Enum.GetName(modParamId.Value)}") + { Length = pidLength }; + }, true)); + root.Items.Add(MakeArrayNode(propCount, bin, "Values", i => MakeFloatNode(bin, $"{i}"), true)); + } + + break; case HircType.Sound: Scan_HIRC_BankSourceData(root, bin, version); Scan_HIRC_NodeBaseParams(root, bin, version, useFeedback); @@ -289,8 +342,117 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo root.Items.Add(MakeInt16Node(bin, "LoopModMax")); } break; + case HircType.Action: + var (actionType, actionFlags) = ActionType.DeserializeStatic(bin.BaseStream, version); + root.Header += $" ({actionType.ToString()})"; + var typeLength = version <= 56 ? 4 : 2; + root.Items.Add(new BinInterpNode(bin.Position - typeLength, + $"ActionType: {actionType.ToString()}, Flags: {actionFlags.ToString()}") { Length = typeLength }); + root.Items.Add(MakeWwiseIdRefNode(bin, "Target ID")); + if (version <= 56) + { + root.Items.Add(MakeInt32Node(bin, "Delay")); + root.Items.Add(MakeInt32Node(bin, "DelayModMin")); + root.Items.Add(MakeInt32Node(bin, "DelayModMax")); + root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + } + + if (version > 65) root.Items.Add(MakeBoolByteNode(bin, "IsBus")); + if (version > 56) Scan_HIRC_InitialParams(root, bin, version); + switch (actionType) + { + case ActionTypeValue.Play: + case ActionTypeValue.PlayAndContinue: + case ActionTypeValue.PlayEventUnknown: + if (version <= 56) + { + root.Items.Add(MakeInt32Node(bin, "TransitionTime")); + root.Items.Add(MakeInt32Node(bin, "TransitionTimeModMin")); + root.Items.Add(MakeInt32Node(bin, "TransitionTimeModMax")); + } + root.Items.Add(MakeByteEnumNode(bin, "CurveInterpolation")); + + if (version <= 56) + { + Scan_HIRC_ActionSpecificParams(root, bin, version, actionType); + Scan_HIRC_ActionExceptParams(root, bin, version); + } + root.Items.Add(MakeWwiseIdRefNode(bin, "BankId")); + break; + case ActionTypeValue.SetState: + case ActionTypeValue.SetSwitch: + root.Items.Add(MakeWwiseIdRefNode(bin, "GroupId")); + root.Items.Add(MakeWwiseIdRefNode(bin, "TargetStateId")); + break; + case ActionTypeValue.SetRTPC: + root.Items.Add(MakeWwiseIdRefNode(bin, "RTPCId")); + root.Items.Add(MakeFloatNode(bin, "RTPCValue")); + break; + case ActionTypeValue.SetFX1: + case ActionTypeValue.SetFX2: + root.Items.Add(MakeBoolByteNode(bin, "IsAudioDeviceElement")); + root.Items.Add(MakeByteNode(bin, "SlotIndex")); + root.Items.Add(MakeWwiseIdRefNode(bin, "FXId")); + root.Items.Add(MakeBoolByteNode(bin, "IsShared")); + Scan_HIRC_ActionExceptParams(root, bin, version); + break; + case ActionTypeValue.BypassFX1: + case ActionTypeValue.BypassFX2: + case ActionTypeValue.BypassFX3: + case ActionTypeValue.BypassFX4: + case ActionTypeValue.BypassFX5: + case ActionTypeValue.BypassFX6: + case ActionTypeValue.BypassFX7: + root.Items.Add(MakeBoolByteNode(bin, "IsBypass")); + root.Items.Add(MakeByteNode(bin, "TargetMask")); + Scan_HIRC_ActionExceptParams(root, bin, version); + break; + case ActionTypeValue.Seek: + root.Items.Add(MakeBoolByteNode(bin, "IsSeekRelativeToDuration")); + root.Items.Add(MakeFloatNode(bin, "SeekValue")); + root.Items.Add(MakeFloatNode(bin, "SeekValueModMin")); + root.Items.Add(MakeFloatNode(bin, "SeekValueModMax")); + root.Items.Add(MakeBoolByteNode(bin, "SnapToNearestMarker")); + Scan_HIRC_ActionExceptParams(root, bin, version); + break; + case ActionTypeValue.UseState1: + case ActionTypeValue.UseState2: + if (version == 56) + { + root.Items.Add(MakeInt32Node(bin, "Time")); + root.Items.Add(MakeInt32Node(bin, "TimeModMin")); + root.Items.Add(MakeInt32Node(bin, "TimeModMax")); + root.Items.Add(MakeByteEnumNode(bin, "CurveInterpolation")); + Scan_HIRC_ActionSpecificParams(root, bin, version, actionType); + Scan_HIRC_ActionExceptParams(root, bin, version); + } + break; + case ActionTypeValue.Release: + case ActionTypeValue.PlayEvent: + case ActionTypeValue.Event1: + case ActionTypeValue.Event2: + case ActionTypeValue.Event3: + case ActionTypeValue.Duck: + case ActionTypeValue.Break: + case ActionTypeValue.Trigger: + break; + default: + if (version <= 56) + { + root.Items.Add(MakeInt32Node(bin, "TransitionTime")); + root.Items.Add(MakeInt32Node(bin, "TransitionTimeModMin")); + root.Items.Add(MakeInt32Node(bin, "TransitionTimeModMax")); + } + root.Items.Add(MakeByteEnumNode(bin, "CurveInterpolation")); + Scan_HIRC_ActionSpecificParams(root, bin, version, actionType); + Scan_HIRC_ActionExceptParams(root, bin, version); + break; + } + break; case HircType.Event: - root.Items.Add(MakeArrayNode(bin, "Actions", i => MakeWwiseIdRefNode(bin, i.ToString()), IsExpanded:true)); + root.Items.Add(version <= 122 + ? MakeArrayNode(bin, "Actions", i => MakeWwiseIdRefNode(bin, i.ToString())) + : MakeArrayNodeWwiseVarCount(bin, "Actions", i => MakeWwiseIdRefNode(bin, i.ToString()))); break; case HircType.RandomSequenceContainer: Scan_HIRC_NodeBaseParams(root, bin, version, useFeedback); @@ -317,14 +479,21 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo } else root.Items.Add(MakeByteEnumNode(bin, "RandSeqFlags")); root.Items.Add(MakeArrayNode(bin, "Children", i => MakeWwiseIdRefNode(bin, $"Child {i}"))); - root.Items.Add(MakeArrayNodeInt16Count(bin, "Playlist", i => + var playlistArrayFunc = (int i) => { var n = new BinInterpNode(bin.Position, $"Item {i}"); - n.Items.Add(MakeWwiseIdNode(bin, "PlaylistItemId")); - if (version <= 56) n.Items.Add(MakeByteNode(bin, "Weight")); - else n.Items.Add(MakeInt32Node(bin, "Weight")); + n.Items.Add(MakeWwiseIdRefNode(bin, "PlaylistItemId")); + n.Items.Add(version <= 56 ? MakeByteNode(bin, "Weight") : MakeInt32Node(bin, "Weight")); return n; - })); + }; + if (version <= 38) + { + root.Items.Add(MakeArrayNode(bin, "Playlist", playlistArrayFunc)); + } + else + { + root.Items.Add(MakeArrayNodeInt16Count(bin, "Playlist", playlistArrayFunc)); + } break; case HircType.SwitchContainer: Scan_HIRC_NodeBaseParams(root, bin, version, useFeedback); @@ -338,7 +507,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo { var g = new BinInterpNode(bin.Position, $"Group {i}"); g.Items.Add(MakeWwiseIdNode(bin, "GroupId")); - g.Items.Add(MakeArrayNode(bin, "ItemIDs", i => MakeWwiseIdRefNode(bin, $"Item {i}"))); + g.Items.Add(MakeArrayNode(bin, "ItemIDs", j => MakeWwiseIdRefNode(bin, $"Item {j}"))); return g; })); root.Items.Add(MakeArrayNode(bin, "SwitchParams", i => @@ -393,10 +562,11 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo if (version <= 56) l.Items.Add(MakeFloatNode(bin, "CrossfadingRtpcDefaultValue")); root.Items.Add(MakeArrayNode(bin, "AssociatedChildren", j => MakeArrayNode(bin, $"Child {j} Curves", k => { - var gItem = new BinInterpNode(bin.Position, $"Graph Item {k}"); - gItem.Items.Add(MakeFloatNode(bin, "From")); - gItem.Items.Add(MakeFloatNode(bin, "To")); + var gItem = new BinInterpNode(bin.Position, ""); + gItem.Items.Add(MakeFloatNode(bin, "From", out float from)); + gItem.Items.Add(MakeFloatNode(bin, "To", out float to)); gItem.Items.Add(MakeUInt32EnumNode(bin, "CurveInterpolation")); + gItem.Header += $"{k}: {from} to {to}"; return gItem; }))); return l; @@ -404,9 +574,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo break; case HircType.Attenuation: if (version > 136) root.Items.Add(MakeBoolByteNode(bin, "IsHeightSpreadEnabled")); - var isConeEnabled = bin.ReadBoolByte(); - bin.Skip(-1); - root.Items.Add(MakeBoolByteNode(bin, "IsConeEnabled")); + root.Items.Add(MakeBoolByteNode(bin, "IsConeEnabled", out var isConeEnabled)); if (isConeEnabled) { root.Items.Add(MakeFloatNode(bin, "InsideDegrees")); @@ -426,18 +594,41 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo { var c = new BinInterpNode(bin.Position, $"Item {i}"); c.Items.Add(MakeByteEnumNode(bin, "CurveScaling")); - c.Items.Add(MakeArrayNodeInt16Count(bin, $"Graph", k => - { - var gItem = new BinInterpNode(bin.Position, $"Graph Item {k}"); - gItem.Items.Add(MakeFloatNode(bin, "From")); - gItem.Items.Add(MakeFloatNode(bin, "To")); - gItem.Items.Add(MakeUInt32EnumNode(bin, "CurveInterpolation")); - return gItem; - }, true)); + c.Items.Add(MakeArrayNodeInt16Count(bin, $"Graph", k => MakeWwiseGraphItem(bin, k), true)); return c; }, true)); Scan_HIRC_RTPCParameterNodeBase(root, bin, version); break; + case HircType.FxShareSet: + case HircType.FxCustom: + root.Items.Add(MakeUInt32Node(bin, "PluginID")); + root.Items.Add(MakeUInt32Node(bin, "PluginParametersSize", out var paramLength)); + root.Items.Add(new BinInterpNode(bin.Position, "PluginParameters") { Length = (int)paramLength }); + bin.Skip(paramLength); + + root.Items.Add(MakeArrayNodeByteCount(bin, "Media", i => + { + var item = new BinInterpNode(bin.Position, i.ToString()); + item.Items.Add(MakeByteNode(bin, "Index")); + item.Items.Add(MakeWwiseIdRefNode(bin, "SourceID")); + return item; + })); + + Scan_HIRC_RTPCParameterNodeBase(root, bin, version); + + if(version is > 123 and < 126) root.Items.Add(MakeUInt16Node(bin, "Unk1")); + + if(version > 126) Scan_HIRC_State(root, bin, version); + + if(version > 90) root.Items.Add(MakeArrayNodeInt16Count(bin, "RTPCInitValues", i => + { + var item = new BinInterpNode(bin.Position, i.ToString()); + item.Items.Add(MakeWwiseIdRefNode(bin, "ParameterID")); + if(version > 126) item.Items.Add(MakeByteNode(bin, "RTPCAccum")); + item.Items.Add(MakeFloatNode(bin, "InitValue")); + return item; + })); + break; } // Just in case we don't parse item in full - jump to next item @@ -445,11 +636,96 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo return root; } + private BinInterpNode MakeWwiseGraphItem(EndianReader bin, int k) + { + var gItem = new BinInterpNode(bin.Position, $"Graph Item {k}"); + gItem.Items.Add(MakeFloatNode(bin, "From")); + gItem.Items.Add(MakeFloatNode(bin, "To")); + gItem.Items.Add(MakeUInt32EnumNode(bin, "CurveInterpolation")); + return gItem; + } + + private void Scan_HIRC_ActionExceptParams(BinInterpNode root, EndianReader bin, uint version) + { + root.Items.Add(MakeArrayNodeWwiseVarCount(bin, "Exceptions", i => + { + var item = MakeWwiseIdNode(bin, $"Exception {i}", $"{i}: ID"); + if (version > 65) + { + item.Items.Add(MakeBoolByteNode(bin, "IsBus")); + } + + return item; + })); + } + + private void Scan_HIRC_ActionSpecificParams(BinInterpNode root, EndianReader bin, uint version, ActionTypeValue actionType) + { + switch (actionType) + { + case ActionTypeValue.Stop: + case ActionTypeValue.Pause: + case ActionTypeValue.Resume: + if (version <= 56) + { + root.Items.Add(MakeBoolIntNode(bin, "IsMaster")); + root.Items.Add(MakeReverseBoolIntNode(bin, "IncludePendingResume")); + root.Items.Add(MakeReverseBoolIntNode(bin, "ApplyToStateTransitions")); + root.Items.Add(MakeReverseBoolIntNode(bin, "ApplyToDynamicSequence")); + } + else + { + root.Items.Add(MakeByteEnumNode(bin, "ActiveFlags")); + } + + break; + + case ActionTypeValue.SetHPF1: + case ActionTypeValue.SetHPF2: + if(version <= 56) root.Items.Add(MakeUInt32EnumNode(bin, "ValueMeaning")); + else root.Items.Add(MakeByteEnumNode(bin, "ValueMeaning")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifier")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMin")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMax")); + break; + case ActionTypeValue.SetGameParameter1: + case ActionTypeValue.SetGameParameter2: + if(version > 89) root.Items.Add(MakeBoolByteNode(bin, "BypassTransition")); + if(version <= 56) root.Items.Add(MakeUInt32EnumNode(bin, "ValueMeaning")); + else root.Items.Add(MakeByteEnumNode(bin, "ValueMeaning")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifier")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMin")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMax")); + break; + case ActionTypeValue.ResetPlaylist: + break; + default: + if (actionType is >= ActionTypeValue.SetPitch1 and <= ActionTypeValue.SetLPF2) + { + // same as SetHPF - I just don't want to write out all the cases. + if(version > 89) root.Items.Add(MakeBoolByteNode(bin, "BypassTransition")); + if(version <= 56) root.Items.Add(MakeUInt32EnumNode(bin, "ValueMeaning")); + else root.Items.Add(MakeByteEnumNode(bin, "ValueMeaning")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifier")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMin")); + root.Items.Add(MakeFloatNode(bin, "RandomizerModifierMax")); + } + else if(version <= 56) + { + root.Items.Add(new BinInterpNode(bin.Position, $"Unk") { Length = 10 }); + bin.Skip(10); + } + + break; + } + } + private void Scan_HIRC_BankSourceData(BinInterpNode root, EndianReader bin, uint version) { - var pluginExists = bin.ReadUInt32(); - bin.Skip(-4); - root.Items.Add(MakeUInt32Node(bin, "PluginID")); + //var pluginExists = bin.ReadUInt32(); + //bin.Skip(-4); + root.Items.Add(MakeUInt32Node(bin, "PluginID", out var pluginId)); + var pluginType = pluginId & 0x0F; var streamTypeNode = new BinInterpNode(bin.Position, ""); var streamType = StreamType.DeserializeStatic(bin.BaseStream, version); @@ -486,15 +762,22 @@ private void Scan_HIRC_BankSourceData(BinInterpNode root, EndianReader bin, uint flags &= ~MediaInformationFlags.Prefetch; } root.Items.Add(new BinInterpNode(bin.Position - 1, $"MediaInformationFlags: {flags}") { Length = 1 }); - + + if (pluginType == 2 || (pluginType == 5 && version <= 126)) + { + root.Items.Add(MakeUInt32Node(bin, "PluginParametersSize", out var paramLength)); + if (paramLength > 0) + { + root.Items.Add(new BinInterpNode(bin.Position, "PluginParameters") { Length = (int)paramLength }); + bin.Skip(paramLength); + } + } } private void Scan_HIRC_NodeBaseParams(BinInterpNode root, EndianReader bin, uint version, bool useFeedback) { root.Items.Add(MakeBoolByteNode(bin, "IsOverrideParentFX")); - var fxCount = bin.ReadByte(); - bin.Skip(-1); - root.Items.Add(MakeByteNode(bin, "FXCount")); + root.Items.Add(MakeByteNode(bin, "FXCount", out var fxCount)); if (fxCount > 0) { root.Items.Add(MakeByteNode(bin, "BitsFXBypass")); @@ -514,7 +797,6 @@ private void Scan_HIRC_NodeBaseParams(BinInterpNode root, EndianReader bin, uint if (version <= 48) { var pLength = bin.ReadUInt32(); - bin.Skip(-4); fx.Items.Add(new BinInterpNode(bin.Position - 4, "FXParameters") { Length = (int)(pLength + 4) }); bin.Skip(pLength); } @@ -559,7 +841,7 @@ private void Scan_HIRC_NodeBaseParams(BinInterpNode root, EndianReader bin, uint Scan_HIRC_AdvSettingsParams(root, bin, version); Scan_HIRC_State(root, bin, version); Scan_HIRC_RTPCParameterNodeBase(root, bin, version); - if(version < 126 && useFeedback) Scan_HIRC_FeedbackInfo(root, bin, version); + if(version < 126 && useFeedback) Scan_HIRC_FeedbackInfo(root, bin); } private void Scan_HIRC_InitialParams(BinInterpNode root, EndianReader bin, uint version) @@ -584,9 +866,7 @@ private void Scan_HIRC_InitialParams(BinInterpNode root, EndianReader bin, uint else { ipNode.IsExpanded = true; - var paramLength = bin.ReadByte(); - bin.Skip(-1); - ipNode.Items.Add(MakeByteNode(bin, "ParamsLength")); + ipNode.Items.Add(MakeByteNode(bin, "ParamsLength", out var paramLength)); if(paramLength > 0) { @@ -613,10 +893,8 @@ private void Scan_HIRC_InitialParams(BinInterpNode root, EndianReader bin, uint } } } - - var rangeLength = bin.ReadByte(); - bin.Skip(-1); - ipNode.Items.Add(MakeByteNode(bin, "RangesLength")); + + ipNode.Items.Add(MakeByteNode(bin, "RangesLength", out var rangeLength)); if(rangeLength > 0) { @@ -685,23 +963,24 @@ private void Scan_HIRC_Positioning(BinInterpNode root, EndianReader bin, uint ve pNode.Items.Add(MakeFloatNode(bin, "PanFR")); } - if(version <= 89) + if (version <= 72) { - if(version < 72) + pNode.Items.Add(MakeBoolByteNode(bin, "Has3DPositioning", out has3dPositioning)); + if (!has3dPositioning) { - pNode.Items.Add(MakeBoolByteNode(bin, "Has2DPositioning")); - bin.Skip(-1); - has2dPositioning = bin.ReadBoolByte(); + pNode.Items.Add(MakeBoolByteNode(bin, "IsPannerEnabled")); } - - pNode.Items.Add(MakeBoolByteNode(bin, "Has3DPositioning")); - bin.Skip(-1); - has3dPositioning = bin.ReadBoolByte(); - if ((!has3dPositioning && version <= 72) || has2dPositioning) + } + else if (version <= 89) + { + pNode.Items.Add(MakeBoolByteNode(bin, "Has2DPositioning", out has2dPositioning)); + pNode.Items.Add(MakeBoolByteNode(bin, "Has3DPositioning", out has3dPositioning)); + if (has2dPositioning) { - pNode.Items.Add(MakeBoolByteNode(bin, "HasPanner")); + pNode.Items.Add(MakeBoolByteNode(bin, "IsPannerEnabled")); } } + } if (has3dPositioning) @@ -785,16 +1064,14 @@ private void Scan_HIRC_AuxParams(BinInterpNode root, EndianReader bin, uint vers { var aNode = new BinInterpNode(bin.Position, "AuxParams"); - bool hasAux = false; + bool hasAux; if(version <= 89) { aNode.Items.Add(MakeBoolByteNode(bin, "OverrideGameAuxSends")); aNode.Items.Add(MakeBoolByteNode(bin, "UseGameAuxSends")); aNode.Items.Add(MakeBoolByteNode(bin, "OverrideUserAuxSends")); - hasAux = bin.ReadBoolByte(); - bin.Skip(-1); - aNode.Items.Add(MakeBoolByteNode(bin, "HasAux")); + aNode.Items.Add(MakeBoolByteNode(bin, "HasAux", out hasAux)); } else { @@ -871,20 +1148,13 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) if(version <= 52) { - ReadStateGroup(sNode, bin, version); - + ReadStateGroup(sNode); } else { - var countPos = bin.Position; - var propsCount = VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(countPos); - sNode.Items.Add(MakeWwiseVarCountNode(bin, "StatePropsCount")); - - if(propsCount > 0) + if (version >= 125) { - var props = new BinInterpNode(bin.Position, "PropertyInfo") { IsExpanded = true }; - for (var i = 0;i < propsCount; i++) + sNode.Items.Add(MakeArrayNodeWwiseVarCount(bin, "StateProperties", i => { var item = new BinInterpNode(bin.Position, i.ToString()); @@ -893,73 +1163,59 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) if (version <= 125) accumType += 1; item.Items.Add(new BinInterpNode(bin.Position - 1, $"AccumType: {Enum.GetName(accumType)}")); if (version > 126) item.Items.Add(MakeBoolByteNode(bin, "InDb")); - - props.Items.Add(item); - } - - sNode.Items.Add(props); + + return item; + })); } - countPos = bin.Position; - var groupsCount = VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(countPos); - sNode.Items.Add(MakeWwiseVarCountNode(bin, "StateGroupsCount")); - - if(groupsCount > 0) + var arrayFunc = new Func(i => { - var groups = new BinInterpNode(bin.Position, "GroupChunks") { IsExpanded = true }; - for (var i = 0; i < propsCount; i++) - { - var item = new BinInterpNode(bin.Position, i.ToString()); + var item = new BinInterpNode(bin.Position, i.ToString()); - item.Items.Add(MakeWwiseIdNode(bin, "StateGroup")); - ReadStateGroup(item, bin, version); - groups.Items.Add(item); - } + item.Items.Add(MakeWwiseIdNode(bin, "StateGroup")); + ReadStateGroup(item); - sNode.Items.Add(groups); - } + return item; + }); + + sNode.Items.Add(version >= 125 + ? MakeArrayNodeWwiseVarCount(bin, "StateGroups", arrayFunc) + : MakeArrayNode(bin, "StateGroups", arrayFunc)); } root.Items.Add(sNode); + return; - void ReadStateGroup(BinInterpNode root, EndianReader bin, uint version) + void ReadStateGroup(BinInterpNode grp) { - root.Items.Add(MakeByteEnumNode(bin, "SyncType")); + grp.Items.Add(MakeByteEnumNode(bin, "SyncType")); var countPos = bin.Position; var stateCount = ReadStateCount(); var length = (int)(bin.Position - countPos); bin.JumpTo(countPos); - root.Items.Add(new BinInterpNode(bin.Position, $"StateCount: {ReadStateCount()}") { Length = length }); + grp.Items.Add(new BinInterpNode(bin.Position, $"StateCount: {ReadStateCount()}") { Length = length }); var states = new BinInterpNode(bin.Position, "States") { IsExpanded = true }; for (var i = 0; i < stateCount; i++) { var state = new BinInterpNode(bin.Position, $"{i}"); - state.Items.Add(MakeWwiseIdNode(bin, "State")); - if (version <= 120) state.Items.Add(MakeWwiseIdRefNode(bin, "StateId")); + state.Items.Add(MakeWwiseIdNode(bin, "StateId")); if (version <= 52) state.Items.Add(MakeBoolByteNode(bin, "IsCustom")); if (version <= 145) state.Items.Add(MakeWwiseIdRefNode(bin, "StateInstanceId")); // bunch of stuff goes right here except its only higher wwise versions! score! states.Items.Add(state); } - root.Items.Add(states); + grp.Items.Add(states); } uint ReadStateCount() { - if (version > 122) + return version switch { - return VarCount.ReadResizingUint(bin.BaseStream); - } - else if (version is > 36 and <= 52) - { - return bin.ReadUInt16(); - } - else - { - return bin.ReadUInt32(); - } + > 122 => VarCount.ReadResizingUint(bin.BaseStream), + > 36 => bin.ReadUInt16(), + _ => bin.ReadUInt32() + }; } } @@ -969,31 +1225,44 @@ private void Scan_HIRC_RTPCParameterNodeBase(BinInterpNode root, EndianReader bi { var rtpc = new BinInterpNode(bin.Position, $"RTPC {i}"); - rtpc.Items.Add(MakeWwiseIdRefNode(bin, "PluginId")); - rtpc.Items.Add(MakeBoolByteNode(bin, "IsRendered")); + if (version <= 48) + { + rtpc.Items.Add(MakeWwiseIdRefNode(bin, "PluginId")); + rtpc.Items.Add(MakeByteNode(bin, "IsRendered")); + } rtpc.Items.Add(MakeWwiseIdRefNode(bin, "RTPCId")); - var rtpcType = bin.ReadByte(); - if (version <= 140 && rtpcType == 0x02) rtpcType = 0x04; - rtpc.Items.Add(new BinInterpNode(bin.Position - 1, $"RTPCType: {Enum.GetName((RtpcTypeInner)rtpcType)}") { Length = 1 }); - var accumType = (AccumTypeInner)bin.ReadByte(); - if (version <= 125) accumType += 1; - rtpc.Items.Add(new BinInterpNode(bin.Position - 1, $"AccumType: {Enum.GetName(accumType)}")); - if(version <= 89) rtpc.Items.Add(MakeUInt32EnumNode(bin, "ParameterId")); - else if(version <= 113) rtpc.Items.Add(MakeByteEnumNode(bin, "ParameterId")); + + if (version > 89) + { + var rtpcType = bin.ReadByte(); + if (version <= 140 && rtpcType == 0x02) rtpcType = 0x04; + rtpc.Items.Add(new BinInterpNode(bin.Position - 1, $"RTPCType: {Enum.GetName((RtpcTypeInner)rtpcType)}") { Length = 1 }); + var accumType = (AccumTypeInner)bin.ReadByte(); + if (version <= 125) accumType += 1; + rtpc.Items.Add(new BinInterpNode(bin.Position - 1, $"AccumType: {Enum.GetName(accumType)}")); + } + + var pidPos = bin.Position; + var (paramId, modParamId, customId) = ParameterId.DeserializeStatic(bin.BaseStream, version, false); // TODO: use modulator not handles on high versions! + var pidLength = (int)(bin.Position - pidPos); + if (customId != null) + { + rtpc.Items.Add(new BinInterpNode(pidPos, $"CustomId: {customId}") { Length = pidLength }); + } else { - var pos = bin.Position; - var parameterId = (RtpcParameterId)VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(pos); - var node = MakeWwiseVarCountNode(bin, $"ParameterId"); - node.Header += $" ({Enum.GetName(parameterId)})"; - rtpc.Items.Add(node); + rtpc.Items.Add(paramId.HasValue + ? new BinInterpNode(pidPos, $"ParameterId: {Enum.GetName(paramId.Value)}") + { Length = pidLength } + : new BinInterpNode(pidPos, $"ModulatorParameterId: {Enum.GetName(modParamId.Value)}") + { Length = pidLength }); } + rtpc.Items.Add(MakeWwiseIdRefNode(bin, "RtpcCurveId")); rtpc.Items.Add(MakeByteEnumNode(bin, "CurveScaling")); - rtpc.Items.Add(MakeArrayNodeInt16Count(bin, "Graph", i => + rtpc.Items.Add(MakeArrayNodeInt16Count(bin, "Graph", j => { - var gItem = new BinInterpNode(bin.Position, $"Graph Item {i}"); + var gItem = new BinInterpNode(bin.Position, $"Graph Item {j}"); gItem.Items.Add(MakeFloatNode(bin, "From")); gItem.Items.Add(MakeFloatNode(bin, "To")); gItem.Items.Add(MakeUInt32EnumNode(bin, "CurveInterpolation")); @@ -1004,7 +1273,7 @@ private void Scan_HIRC_RTPCParameterNodeBase(BinInterpNode root, EndianReader bi })); } - private void Scan_HIRC_FeedbackInfo(BinInterpNode root, EndianReader bin, uint version) + private void Scan_HIRC_FeedbackInfo(BinInterpNode root, EndianReader bin) { root.Items.Add(MakeWwiseIdRefNode(bin, "BusId")); bin.Skip(-4); @@ -1054,464 +1323,111 @@ private void Scan_WwiseBank_INIT(BinInterpNode root, EndianReader bin) })); } - private List Scan_WwiseBankOld(byte[] data) + private void Scan_WwiseBank_STMG(BinInterpNode root, EndianReader bin, uint version) { - var subnodes = new List(); - try + root.Items.Add(MakeFloatNode(bin, "VolumeThreshold")); + if (version > 53) root.Items.Add(MakeUInt16Node(bin, "MaxNumVoicesLimitInternal")); + if (version > 126) root.Items.Add(MakeUInt16Node(bin, "MaxNumDangerousVirtVoicesLimitInternal")); + + root.Items.Add(MakeArrayNode(bin, "StateGroups", i => { - var bin = new EndianReader(new MemoryStream(data)) { Endian = Pcc.Endian }; - bin.JumpTo(CurrentLoadedExport.propsEnd()); - - if (Pcc.Game is MEGame.ME2 or MEGame.LE2) + var sg = new BinInterpNode(bin.Position, $"Group {i}"); + sg.Items.Add(MakeWwiseIdNode(bin, "StateId")); + sg.Items.Add(MakeUInt32Node(bin, "DefaultTransitionTime")); + if(version <= 52) sg.Items.Add(MakeArrayNode(bin, "CustomStates", j => + { + var stateNode = MakeWwiseIdRefNode(bin, "StateId"); + var node = MakeHIRCNode(j, bin, version, false); + node.Items.Insert(0, stateNode); + return node; + })); + sg.Items.Add(MakeArrayNode(bin, "StateTransitions", j => + { + var st = new BinInterpNode(bin.Position, $"{j}"); + st.Items.Add(MakeWwiseIdRefNode(bin, "FromStateId")); + st.Items.Add(MakeWwiseIdRefNode(bin, "ToStateId")); + st.Items.Add(MakeUInt32Node(bin, "TransitionTime")); + return st; + })); + + return sg; + })); + + root.Items.Add(MakeArrayNode(bin, "SwitchGroups", i => + { + var sg = new BinInterpNode(bin.Position, $"Group {i}"); + sg.Items.Add(MakeWwiseIdNode(bin, "GroupId")); + sg.Items.Add(MakeWwiseIdRefNode(bin, "RtpcId?")); + if (version > 89) { - subnodes.Add(MakeUInt32Node(bin, "Unk1")); - subnodes.Add(MakeUInt32Node(bin, "Unk2")); - if (bin.Skip(-8).ReadInt64() == 0) + var rtpcType = bin.ReadByte(); + if (version <= 140 && rtpcType == 0x02) { - return subnodes; + rtpcType = 0x04; } + sg.Items.Add(new BinInterpNode(bin.Position - 1, $"RtpcType: {Enum.GetName((RtpcTypeInner)rtpcType)}") { Length = 1 }); } - subnodes.Add(new BinInterpNode(bin.Position, $"BulkDataFlags: {(EBulkDataFlags)bin.ReadUInt32()}")); - subnodes.Add(MakeInt32Node(bin, "Element Count", out int dataSize)); - subnodes.Add(MakeInt32Node(bin, "BulkDataSizeOnDisk")); - subnodes.Add(MakeUInt32HexNode(bin, "BulkDataOffsetInFile")); - - if (dataSize == 0) + sg.Items.Add(MakeArrayNode(bin, "Graph", j => MakeWwiseGraphItem(bin, j))); + return sg; + })); + + root.Items.Add(MakeArrayNode(bin, "Params", i => + { + var p = new BinInterpNode(bin.Position, $"Param {i}"); + p.Items.Add(MakeWwiseIdRefNode(bin, "RtpcParamId")); + p.Items.Add(MakeFloatNode(bin, "Value")); + if (version > 89) { - // Nothing more - return subnodes; + p.Items.Add(MakeUInt32EnumNode(bin, "RampingType")); + p.Items.Add(MakeFloatNode(bin, "RampUp")); + p.Items.Add(MakeFloatNode(bin, "RampDown")); + p.Items.Add(MakeByteEnumNode(bin, "BuiltInParam")); } + return p; + })); - var chunksNode = new BinInterpNode(bin.Position, "Chunks") - { - IsExpanded = true, - }; - subnodes.Add(chunksNode); - while (bin.Position < bin.Length) + if (version > 118) root.Items.Add(MakeArrayNode(bin, "AcousticTextures", i => + { + var at = new BinInterpNode(bin.Position, $"Acoustic Texture {i}"); + at.Items.Add(MakeWwiseIdNode(bin, "AcousticTexture")); + foreach(var p in (string[])["AbsorptionOffset", "AbsorptionLow", "AbsorptionMidLow", "AbsorptionMidHigh", "AbsorptionHigh", "Scattering"]) { - var start = bin.Position; - string chunkID = bin.BaseStream.ReadStringLatin1(4); //This is not endian swapped! - int size = bin.ReadInt32(); - var chunk = new BinInterpNode(start, $"{chunkID}: {size} bytes") - { - Length = size + 8 - }; - chunksNode.Items.Add(chunk); - - switch (chunkID) - { - case "BKHD": - chunk.Items.Add(MakeUInt32Node(bin, "Version")); - chunk.Items.Add(new BinInterpNode(bin.Position, $"ID: {bin.ReadUInt32():X}") { Length = 4 }); - chunk.Items.Add(MakeUInt32Node(bin, "Unk Zero")); - chunk.Items.Add(MakeUInt32Node(bin, "Unk Zero")); - break; - case "STMG": - if (Pcc.Game.IsGame2()) - { - chunk.Items.Add(new BinInterpNode(bin.Position, "STMG node not parsed for ME2")); - break; - } - chunk.Items.Add(MakeFloatNode(bin, "Volume Threshold")); - if (Pcc.Game == MEGame.ME3) - { - chunk.Items.Add(MakeUInt16Node(bin, "Max Voice Instances")); - } - int numStateGroups; - var stateGroupsNode = new BinInterpNode(bin.Position, $"State Groups ({numStateGroups = bin.ReadInt32()})"); - chunk.Items.Add(stateGroupsNode); - for (int i = 0; i < numStateGroups; i++) - { - stateGroupsNode.Items.Add(MakeUInt32HexNode(bin, "ID")); - stateGroupsNode.Items.Add(MakeUInt32Node(bin, "Default Transition Time (ms)")); - int numTransitionTimes; - var transitionTimesNode = new BinInterpNode(bin.Position, $"Custom Transition Times ({numTransitionTimes = bin.ReadInt32()})"); - stateGroupsNode.Items.Add(transitionTimesNode); - for (int j = 0; j < numTransitionTimes; j++) - { - transitionTimesNode.Items.Add(MakeUInt32HexNode(bin, "'From' state ID")); - transitionTimesNode.Items.Add(MakeUInt32HexNode(bin, "'To' state ID")); - transitionTimesNode.Items.Add(MakeUInt32Node(bin, "Transition Time (ms)")); - } - } - int numSwitchGroups; - var switchGroupsNode = new BinInterpNode(bin.Position, $"Switch Groups ({numSwitchGroups = bin.ReadInt32()})"); - chunk.Items.Add(switchGroupsNode); - for (int i = 0; i < numSwitchGroups; i++) - { - switchGroupsNode.Items.Add(MakeUInt32HexNode(bin, "ID")); - switchGroupsNode.Items.Add(MakeUInt32HexNode(bin, "Game Parameter ID")); - int numPoints; - var pointNode = new BinInterpNode(bin.Position, $"Points ({numPoints = bin.ReadInt32()})"); - switchGroupsNode.Items.Add(pointNode); - for (int j = 0; j < numPoints; j++) - { - pointNode.Items.Add(MakeFloatNode(bin, "Game Parameter value")); - pointNode.Items.Add(MakeUInt32HexNode(bin, "ID of Switch set when Game Parameter >= given value")); - pointNode.Items.Add(MakeUInt32Node(bin, "Curve shape. (9 = constant)")); - } - } - int numGameParams; - var gameParamNode = new BinInterpNode(bin.Position, $"Game Parameters ({numGameParams = bin.ReadInt32()})"); - chunk.Items.Add(gameParamNode); - for (int i = 0; i < numGameParams; i++) - { - gameParamNode.Items.Add(MakeUInt32HexNode(bin, "ID")); - gameParamNode.Items.Add(MakeFloatNode(bin, "default value")); - } - break; - case "DIDX": - for (int i = 0; i < size / 12; i++) - { - BinInterpNode item = new BinInterpNode(bin.Position, $"{i}: Embedded File Info") - { - Length = 12, - IsExpanded = true - }; - chunk.Items.Add(item); - item.Items.Add(new BinInterpNode(bin.Position, $"ID: {bin.ReadUInt32():X}") { Length = 4 }); - item.Items.Add(MakeUInt32Node(bin, "Offset")); - item.Items.Add(MakeUInt32Node(bin, "Length")); - - // //todo: remove testing code - // bin.Skip(-8); - // int off = bin.ReadInt32(); - // int len = bin.ReadInt32(); - // item.Items.Add(new BinInterpNode(0xf4 + off, "file start")); - // item.Items.Add(new BinInterpNode(0xf4 + off + len, "file end")); - } - break; - case "DATA": - chunk.Items.Add(new BinInterpNode(bin.Position, "Start of DATA section. Embedded file offsets are relative to here")); - break; - case "HIRC": - chunk.Items.Add(MakeUInt32Node(bin, "HIRC object count")); - uint hircCount = bin.Skip(-4).ReadUInt32(); - for (int i = 0; i < hircCount; i++) - { - chunk.Items.Add(ScanHircObject(bin, i)); - } - break; - case "STID": - chunk.Items.Add(MakeUInt32Node(bin, "Unk One")); - chunk.Items.Add(MakeUInt32Node(bin, "WwiseBanks referenced in this bank")); - uint soundBankCount = bin.Skip(-4).ReadUInt32(); - for (int i = 0; i < soundBankCount; i++) - { - BinInterpNode item = new BinInterpNode(bin.Position, $"{i}: Referenced WwiseBank") - { - Length = 12, - IsExpanded = true - }; - item.Items.Add(new BinInterpNode(bin.Position, $"ID: {bin.ReadUInt32():X}") { Length = 4 }); - int strLen = bin.ReadByte(); - item.Items.Add(new BinInterpNode(bin.Position, $"Name: {bin.ReadStringASCII(strLen)}") { Length = strLen }); - chunk.Items.Add(item); - } - break; - default: - chunk.Items.Add(new BinInterpNode(bin.Position, "UNPARSED CHUNK!")); - break; - } - - bin.JumpTo(start + size + 8); + at.Items.Add(MakeFloatNode(bin, p)); } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } - return subnodes; + return at; + })); + } - BinInterpNode ScanHircObject(EndianReader bin, int idx) + private void Scan_WwiseBank_ENVS(BinInterpNode root, EndianReader bin, uint version) + { + var x = version <= 150 ? 2 : 4; + var y = version <= 89 ? 2 : 3; + string[] xLabels = ["Obs", "Occ", "Diff", "Trans"]; + string[] yLabels = ["Volume", "LPF", "HPF"]; + + for(var i = 0; i < x; i++) { - var startPos = bin.Position; - HIRCType hircType = (HIRCType)(Pcc.Game == MEGame.ME2 ? bin.ReadUInt32() : bin.ReadByte()); - int len = bin.ReadInt32(); - uint id = bin.ReadUInt32(); - var node = new BinInterpNode(startPos, $"{idx}: Type: {AudioStreamHelper.GetHircObjTypeString(hircType)} | Length:{len} | ID:{id:X8}") + for(var j = 0; j < y; j++) { - Length = len + 4 + (Pcc.Game == MEGame.ME2 ? 4 : 1) - }; - bin.JumpTo(startPos); - node.Items.Add(Pcc.Game == MEGame.ME2 ? MakeUInt32Node(bin, "Type") : MakeByteNode(bin, "Type")); - node.Items.Add(MakeInt32Node(bin, "Length")); - node.Items.Add(MakeUInt32HexNode(bin, "ID")); - var endPos = startPos + node.Length; - switch (hircType) - { - case HIRCType.Event: - if (Pcc.Game.IsLEGame()) - { - MakeArrayNodeByteCount(bin, "Event Actions", i => MakeUInt32HexNode(bin, $"{i}")); - } - else - { - MakeArrayNode(bin, "Event Actions", i => MakeUInt32HexNode(bin, $"{i}")); - } - break; - case HIRCType.EventAction: - { - node.Items.Add(new BinInterpNode(bin.Position, $"Scope: {(WwiseBankParsed.EventActionScope)bin.ReadByte()}", NodeType.StructLeafByte) { Length = 1 }); - WwiseBankParsed.EventActionType actType; - node.Items.Add(new BinInterpNode(bin.Position, $"Action Type: {actType = (WwiseBankParsed.EventActionType)bin.ReadByte()}", NodeType.StructLeafByte) { Length = 1 }); - if (Pcc.Game.IsOTGame()) - node.Items.Add(MakeUInt16Node(bin, "Unknown1")); - node.Items.Add(MakeUInt32HexNode(bin, "Referenced Object ID")); - switch (actType) - { - case WwiseBankParsed.EventActionType.Play: - node.Items.Add(MakeUInt32Node(bin, "Delay (ms)")); - node.Items.Add(MakeInt32Node(bin, "Delay Randomization Range lower bound (ms)")); - node.Items.Add(MakeInt32Node(bin, "Delay Randomization Range upper bound (ms)")); - node.Items.Add(MakeUInt32Node(bin, "Unknown2")); - node.Items.Add(MakeUInt32Node(bin, "Fade-in (ms)")); - node.Items.Add(MakeInt32Node(bin, "Fade-in Randomization Range lower bound (ms)")); - node.Items.Add(MakeInt32Node(bin, "Fade-in Randomization Range upper bound (ms)")); - node.Items.Add(MakeByteNode(bin, "Fade-in curve Shape")); - break; - case WwiseBankParsed.EventActionType.Stop: - node.Items.Add(MakeUInt32Node(bin, "Delay (ms)")); - node.Items.Add(MakeInt32Node(bin, "Delay Randomization Range lower bound (ms)")); - node.Items.Add(MakeInt32Node(bin, "Delay Randomization Range upper bound (ms)")); - node.Items.Add(MakeUInt32Node(bin, "Unknown2")); - node.Items.Add(MakeUInt32Node(bin, "Fade-out (ms)")); - node.Items.Add(MakeInt32Node(bin, "Fade-out Randomization Range lower bound (ms)")); - node.Items.Add(MakeInt32Node(bin, "Fade-out Randomization Range upper bound (ms)")); - node.Items.Add(MakeByteNode(bin, "Fade-out curve Shape")); - break; - case WwiseBankParsed.EventActionType.Play_LE: - node.Items.Add(MakeByteNode(bin, "Unknown1")); - bool playHasFadeIn; - node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {playHasFadeIn = (bool)bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); - if (playHasFadeIn) - { - node.Items.Add(MakeByteNode(bin, "Unknown byte")); - node.Items.Add(MakeUInt32Node(bin, "Fade-in (ms)")); - } - bool RandomFade; - node.Items.Add(new BinInterpNode(bin.Position, $"Unknown 2: {RandomFade = (bool)bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); - if (RandomFade) - { - node.Items.Add(MakeByteNode(bin, "Enabled?")); - node.Items.Add(MakeUInt32Node(bin, "MinOffset")); - node.Items.Add(MakeUInt32Node(bin, "MaxOffset")); - } - node.Items.Add(new BinInterpNode(bin.Position, $"Fade-out curve Shape: {(WwiseBankParsed.EventActionFadeCurve)bin.ReadByte()}", NodeType.StructLeafByte) { Length = 1 }); - node.Items.Add(MakeUInt32HexNode(bin, "Bank ID")); - break; - case WwiseBankParsed.EventActionType.Stop_LE: - node.Items.Add(MakeByteNode(bin, "Unknown1")); - bool stopHasFadeOut; - node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {stopHasFadeOut = (bool)bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); - if (stopHasFadeOut) - { - node.Items.Add(MakeByteNode(bin, "Unknown byte")); - node.Items.Add(MakeUInt32Node(bin, "Fade-in (ms)")); - } - node.Items.Add(MakeByteNode(bin, "Unknown2")); - node.Items.Add(new BinInterpNode(bin.Position, $"Fade-out curve Shape: {(WwiseBankParsed.EventActionFadeCurve)bin.ReadByte()}", NodeType.StructLeafByte) { Length = 1 }); - node.Items.Add(MakeByteNode(bin, "Unknown3")); - node.Items.Add(MakeByteNode(bin, "Unknown4")); - break; - case WwiseBankParsed.EventActionType.SetLPF_LE: - case WwiseBankParsed.EventActionType.SetVolume_LE: - case WwiseBankParsed.EventActionType.ResetLPF_LE: - case WwiseBankParsed.EventActionType.ResetVolume_LE: - node.Items.Add(MakeByteNode(bin, "Unknown1")); - bool HasFade; - node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {HasFade = (bool)bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); - if (HasFade) - { - node.Items.Add(MakeByteNode(bin, "Unknown byte")); - node.Items.Add(MakeUInt32Node(bin, "Fade-in (ms)")); - } - node.Items.Add(MakeByteNode(bin, "Unknown2")); - node.Items.Add(new BinInterpNode(bin.Position, $"Fade-out curve Shape: {(WwiseBankParsed.EventActionFadeCurve)bin.ReadByte()}", NodeType.StructLeafByte) { Length = 1 }); - node.Items.Add(MakeByteNode(bin, "Unknown3")); - node.Items.Add(MakeFloatNode(bin, "Unknown float A")); - node.Items.Add(MakeInt32Node(bin, "Unknown int/float B")); - node.Items.Add(MakeInt32Node(bin, "Unknown int/float C")); - node.Items.Add(MakeByteNode(bin, "Unknown4")); - break; - } - goto default; - } - case HIRCType.SoundSXFSoundVoice: - //node.Items.Add(MakeUInt32Node(bin, "Unknown1")); - //WwiseBank.SoundState soundState; - //node.Items.Add(new BinInterpNode(bin.Position, $"State: {soundState = (WwiseBank.SoundState)bin.ReadUInt32()}", NodeType.StructLeafInt) { Length = 4 }); - //switch (soundState) - //{ - // case WwiseBank.SoundState.Embed: - // node.Items.Add(MakeUInt32HexNode(bin, "Audio ID")); - // node.Items.Add(MakeUInt32HexNode(bin, "Source ID")); - // node.Items.Add(MakeInt32Node(bin, "Type?")); - // node.Items.Add(MakeInt32Node(bin, "Prefetch length?")); - // break; - // case WwiseBank.SoundState.Streamed: - // node.Items.Add(MakeUInt32HexNode(bin, "Audio ID")); - // node.Items.Add(MakeUInt32HexNode(bin, "Source ID")); - // break; - // case WwiseBank.SoundState.StreamPrefetched: - // node.Items.Add(MakeUInt32HexNode(bin, "Audio ID")); - // node.Items.Add(MakeUInt32HexNode(bin, "Source ID")); - // node.Items.Add(MakeInt32Node(bin, "Type?")); - // node.Items.Add(MakeInt32Node(bin, "Prefetch length?")); - // break; - - //} - - //WwiseBank.SoundType soundType; - //node.Items.Add(new BinInterpNode(bin.Position, $"SoundType: {soundType = (WwiseBank.SoundType)bin.ReadUInt32()}", NodeType.StructLeafInt) { Length = 4 }); - //switch (soundType) - //{ - // case WwiseBank.SoundType.SFX: - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeUInt32HexNode(bin, "Mixer Out Reference ID")); - // break; - // case WwiseBank.SoundType.Voice: - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeUInt32HexNode(bin, "Mixer Out Reference ID")); - // break; - // default: - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeUInt32HexNode(bin, "Mixer Out Reference ID")); - // break; - //} - //node.Items.Add(MakeByteNode(bin, "Unknown_hex32")); //Maybe standard mixer package (shared with actor/mixer) - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "PreVolume-UnknownF6")); - //node.Items.Add(MakeFloatNode(bin, "Volume (-db)")); - //node.Items.Add(MakeInt32Node(bin, "Unknown_A_4bytes")); //These maybe link or RTPC or randomizer? - //node.Items.Add(MakeInt32Node(bin, "Unknown_B_4bytes")); - //node.Items.Add(MakeFloatNode(bin, "Low Frequency Effect (LFE)")); - //node.Items.Add(MakeInt32Node(bin, "Unknown_C_4bytes")); - //node.Items.Add(MakeInt32Node(bin, "Unknown_D_4bytes")); - //node.Items.Add(MakeFloatNode(bin, "Pitch")); - //node.Items.Add(MakeFloatNode(bin, "Unknown_E_float")); - //node.Items.Add(MakeFloatNode(bin, "Unknown_F_float")); - //node.Items.Add(MakeFloatNode(bin, "Low Pass Filter")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_G_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_H_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_I_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_J_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_K_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_L_4bytes")); //In v56 banks? - // //node.Items.Add(MakeByteNode(bin, "Unknown")); In imported v53 banks? - // //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeInt32Node(bin, "Loop Count (0=infinite)")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - - goto default; - case HIRCType.RandomOrSequenceContainer: - case HIRCType.SwitchContainer: - case HIRCType.ActorMixer: - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //int nEffects = 0; - //node.Items.Add(new BinInterpNode(bin.Position, $"Count of Effects (?Aux Bus?): {nEffects = bin.ReadByte()}") { Length = 1 }); - //for (int b = 0; b < nEffects; b++) - //{ - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - // node.Items.Add(MakeUInt32HexNode(bin, "Effect Reference ID")); - // node.Items.Add(MakeByteNode(bin, "Unknown")); - //} - //if (nEffects > 0) - //{ - // node.Items.Add(MakeByteNode(bin, "Unknown")); - //} - //node.Items.Add(MakeUInt32HexNode(bin, "Master Audio Bus Reference ID")); - //node.Items.Add(MakeUInt32HexNode(bin, "Audio Out link")); - //node.Items.Add(MakeByteNode(bin, "Unknown_hex32")); //Is here on a standard mixer? Appears in SoundSFX/voice - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "PreVolume-Unknown_hexF6")); - //node.Items.Add(MakeFloatNode(bin, "Volume (-db)")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_A_4bytes")); //These maybe link or RTPC or randomizer? - //node.Items.Add(MakeUInt32Node(bin, "Unknown_B_4bytes")); - //node.Items.Add(MakeFloatNode(bin, "Low Frequency Effect (LFE)")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_C_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_D_4bytes")); - //node.Items.Add(MakeFloatNode(bin, "Pitch")); - //node.Items.Add(MakeFloatNode(bin, "Unknown_E_float")); - //node.Items.Add(MakeFloatNode(bin, "Unknown_F_float")); - //node.Items.Add(MakeFloatNode(bin, "Low Pass Filter")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_G_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_H_4bytes")); //Mixer end - - ////Minimum is 4 x 4bytes but can expanded - //bool hasEffects = false; //Maybe something else - //node.Items.Add(new BinInterpNode(bin.Position, $"Unknown_Byte->Int + Unk4 + Float: {hasEffects = bin.ReadBoolByte()}") { Length = 1 }); //Count of something? effects? - //if (hasEffects) - //{ - // node.Items.Add(MakeInt32Node(bin, "Unknown_Int")); - // node.Items.Add(MakeUInt32Node(bin, "Unknown_I_4bytes")); - // node.Items.Add(MakeFloatNode(bin, "Unknown Float")); - //} - //bool hasAttenuation = false; - //node.Items.Add(new BinInterpNode(bin.Position, $"Attenuations?: {hasAttenuation = bin.ReadBoolByte()}") { Length = 1 }); //RPTCs? Count? Attenuations? - //if (hasAttenuation) - //{ - // node.Items.Add(MakeInt32Node(bin, "Unknown_J_Int")); //<= extra if byte? - // node.Items.Add(MakeUInt32HexNode(bin, "Attenuation? Reference")); //<= extra if byte? - // node.Items.Add(MakeUInt32Node(bin, "Unknown_L_4bytes")); - // node.Items.Add(MakeUInt32Node(bin, "Unknown_M_4bytes")); - // node.Items.Add(MakeByteNode(bin, "UnknownByte")); - //} - //else - //{ - // node.Items.Add(MakeInt32Node(bin, "Unknown_J_Int")); - // node.Items.Add(MakeUInt32Node(bin, "Unknown_L_4bytes")); - //} - - //node.Items.Add(MakeUInt32Node(bin, "Unknown_N_4bytes")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_O_4bytes")); - //goto default; - //node.Items.Add(MakeArrayNode(bin, "Input References", i => MakeUInt32HexNode(bin, $"{i}"))); - goto default; - case HIRCType.AudioBus: - case HIRCType.BlendContainer: - case HIRCType.MusicSegment: - case HIRCType.MusicTrack: - case HIRCType.MusicSwitchContainer: - case HIRCType.MusicPlaylistContainer: - case HIRCType.Attenuation: - case HIRCType.DialogueEvent: - case HIRCType.MotionBus: - case HIRCType.MotionFX: - case HIRCType.Effect: - case HIRCType.AuxiliaryBus: - //node.Items.Add(MakeByteNode(bin, "Count of something?")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeByteNode(bin, "Unknown")); - //node.Items.Add(MakeUInt32Node(bin, "Unknown_Int")); - //node.Items.Add(MakeFloatNode(bin, "Unknown Float")); - //break; - case HIRCType.Settings: - default: - if (bin.Position < endPos) - { - node.Items.Add(new BinInterpNode(bin.Position, "Unknown bytes") - { - Length = (int)(endPos - bin.Position) - }); - } - break; + var curve = new BinInterpNode(bin.Position, $"Curve {xLabels[i]}[{yLabels[j]}]"); + ScanCurve(curve); + curve.Length = (int)bin.Position - curve.Offset; + root.Items.Add(curve); } - - bin.JumpTo(endPos); - return node; + } + + void ScanCurve(BinInterpNode parent) + { + parent.Items.Add(MakeBoolByteNode(bin, "CurveEnabled")); + parent.Items.Add(MakeByteEnumNode(bin, "CurveScaling")); + parent.Items.Add(MakeArrayNodeInt16Count(bin, "Graph", j => + { + var gItem = new BinInterpNode(bin.Position, $"Graph Item {j}"); + gItem.Items.Add(MakeFloatNode(bin, "From")); + gItem.Items.Add(MakeFloatNode(bin, "To")); + gItem.Items.Add(MakeUInt32EnumNode(bin, "CurveInterpolation")); + return gItem; + })); } } - } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml index d7da47bdd4..4431b4338a 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml @@ -7,6 +7,7 @@ xmlns:sharedToolControls="clr-namespace:LegendaryExplorer.UserControls.SharedToolControls" xmlns:Converters="clr-namespace:LegendaryExplorer.SharedUI.Converters" xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" + xmlns:soundpanel="clr-namespace:LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel" mc:Ignorable="d" Loaded="FaceFXAnimSetEditorControl_OnLoaded" Unloaded="FaceFXAnimSetEditorControl_OnUnloaded" @@ -164,6 +165,6 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs index 270cddd8e0..c86b567479 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs @@ -25,6 +25,7 @@ using System.Windows.Input; using System.Windows.Media; using System.Xml.Linq; +using LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel; using Point = System.Windows.Point; namespace LegendaryExplorer.UserControls.ExportLoaderControls diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs index 62bc7350bf..4a7c9d247c 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs @@ -1,46 +1,40 @@ using System.Collections.Generic; +using System.IO; using System.Linq; +using BinarySerialization; using LegendaryExplorer.Misc; using LegendaryExplorerCore.Helpers; -using LegendaryExplorerCore.Packages; -using LegendaryExplorerCore.Unreal.BinaryConverters; +using ME3Tweaks.Wwiser; +using ME3Tweaks.Wwiser.Model.Hierarchy; +using WwiseBank = ME3Tweaks.Wwiser.WwiseBank; -namespace LegendaryExplorer.UserControls.ExportLoaderControls +namespace LegendaryExplorer.UserControls.ExportLoaderControls.Soundpanel { public class HIRCDisplayObject : NotifyPropertyChangedBase { + public HIRCDisplayObject(int i, HircItemContainer item, byte[] data, BankSerializationContext context) + { + Index = i; + Item = item; + Data = data; + Context = context; + } + public int Index { get; set; } + + private HircItemContainer _item; - public byte ObjType { get; set; } - - public uint ID { get; set; } - - public byte SoundType { get; set; } - - public uint State { get; set; } - - //typeinfo - public uint unk1, AudioID, SourceID;//scope,atype; - public List EventIDs { get; set; } + public HircItemContainer Item + { + get => _item; + set => SetProperty(ref _item, value); + } private byte[] _data; public byte[] Data { get => _data; - internal set - { - if (_data != null && value != null && _data.SequenceEqual(value)) - { - return; //if the data is the same don't write it and trigger the side effects - } - - bool isFirstLoad = _data == null; - _data = value; - if (!isFirstLoad) - { - DataChanged = true; - } - } + internal set => _data = value; } private bool _dataChanged; @@ -50,33 +44,67 @@ public bool DataChanged get => _dataChanged; internal set => SetProperty(ref _dataChanged, value); } + + internal BankSerializationContext Context { get; set; } public HIRCDisplayObject Clone() { - HIRCDisplayObject clone = (HIRCDisplayObject)MemberwiseClone(); - clone.EventIDs = EventIDs?.Clone(); - clone.Data = Data?.ArrayClone(); + var clone = new HIRCDisplayObject(Index, Item, Data?.ArrayClone(), Context); + + if (Data != null) + { + // Effectively a deep clone of the Item + var serializer = new BinarySerializer(); + using var stream = new MemoryStream(clone.Data); + clone.Item = serializer.Deserialize(stream, Context); + + // Increment the ID to ensure uniqueness + clone.Item.Item.Id++; + + // Serialize the data back - this is literally just for the updated ID lmao + stream.SetLength(0); + serializer.Serialize(stream, clone.Item, clone.Context); + clone.Data = stream.ToArray(); + clone.DataChanged = true; + } return clone; } - public HIRCDisplayObject(int i, WwiseBankParsed.HIRCObject src, MEGame game) + /// + /// Commit the hex data back to the HircItemContainer + /// + public void CommitHex(byte[] data) { - Data = src.ToBytes(game); - Index = i; - ObjType = (byte)src.Type; - ID = src.ID; - switch (src) + if(data is null || data.Length == 0) + { + return; + } + + if (_data != null && _data.SequenceEqual(data)) + { + return; //if the data is the same don't write it and trigger the side effects + } + + Data = data; + var serializer = new BinarySerializer(); + using var stream = new MemoryStream(Data); + Item = serializer.Deserialize(stream, Context); + DataChanged = true; + } + + public static IEnumerable CreateFromBank(WwiseBank bank) + { + if (bank.HIRC is null || !bank.HIRC.Items.Any()) yield break; + + var context = new BankSerializationContext(bank.BKHD.BankGeneratorVersion, false, bank.BKHD.FeedbackInBank); + var serializer = new BinarySerializer(); + using var stream = new MemoryStream(); + for (int i = 0; i < bank.HIRC.Items.Count; i++) { - case WwiseBankParsed.SoundSFXVoice sfxVoice: - unk1 = sfxVoice.Unk1; - State = (uint)sfxVoice.State; - SourceID = sfxVoice.SourceID; - AudioID = sfxVoice.AudioID; - SoundType = (byte)sfxVoice.SoundType; - break; - case WwiseBankParsed.Event eventHIRC: - EventIDs = eventHIRC.EventActions.Clone(); - break; + var item = bank.HIRC.Items[i]; + serializer.Serialize(stream, item, context); + yield return new HIRCDisplayObject(i, item, stream.ToArray(), context); + stream.SetLength(0); // Reset the stream for the next item } } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml new file mode 100644 index 0000000000..b398fc320a --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + +