From 2f910fc6a558e4b11bb9bc13c71ebf92b454cfd3 Mon Sep 17 00:00:00 2001 From: henbagle Date: Wed, 5 Feb 2025 20:40:36 -0600 Subject: [PATCH 01/21] BinInterp: Parse WwiseBank action HIRC type --- .../BinaryInterpreterScans.cs | 2 + .../BinaryInterpreter/WwiseBankScans.cs | 227 +++++++++++++++++- 2 files changed, 217 insertions(+), 12 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs index 2d1237827a..79af8d0f48 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs @@ -4747,6 +4747,8 @@ private List StartLevelScan(byte[] data, ref int binarystart) private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; + private static BinInterpNode MakeReverseBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32() != 1}", 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 }; diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index d46b9fd794..5e07777b7d 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -1,5 +1,4 @@ -using DocumentFormat.OpenXml.Math; -using LegendaryExplorer.SharedUI.Interfaces; +using LegendaryExplorer.SharedUI.Interfaces; using LegendaryExplorer.UnrealExtensions; using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Helpers; @@ -14,7 +13,10 @@ using ME3Tweaks.Wwiser.Model.RTPC; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; +using ME3Tweaks.Wwiser.Model.Action; +using ME3Tweaks.Wwiser.Model.Action.Specific; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.AccumType; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.CurveScaling; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.GroupType; @@ -28,6 +30,7 @@ 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; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -73,7 +76,7 @@ private BinInterpNode MakeWwiseUniNode(EndianReader bin, string name) { // float var f = BitConverter.ToSingle(span); - node.Header += f.ToString(); + node.Header += f.ToString(CultureInfo.InvariantCulture); } else { @@ -289,6 +292,118 @@ 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); + 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(MakeWwiseIdNode(bin, "Target", "Target ID")); + if (version <= 56) + { + root.Items.Add(MakeInt32Node(bin, "Delay")); + root.Items.Add(MakeInt32Node(bin, "DelayModMin")); + root.Items.Add(MakeInt32Node(bin, "DelayModMax")); + } + + 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(MakeUInt32Node(bin, "SubsectionSize")); + 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(MakeWwiseIdNode(bin, "BankId")); + break; + case ActionTypeValue.SetState: + case ActionTypeValue.SetSwitch: + if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + root.Items.Add(MakeWwiseIdNode(bin, "GroupId")); + root.Items.Add(MakeWwiseIdNode(bin, "TargetStateId")); + break; + case ActionTypeValue.SetRTPC: + if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + root.Items.Add(MakeWwiseIdNode(bin, "RTPCId")); + root.Items.Add(MakeFloatNode(bin, "RTPCValue")); + break; + case ActionTypeValue.SetFX1: + case ActionTypeValue.SetFX2: + if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + 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: + if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + root.Items.Add(MakeBoolByteNode(bin, "IsBypass")); + root.Items.Add(MakeByteNode(bin, "TargetMask")); + Scan_HIRC_ActionExceptParams(root, bin, version); + break; + case ActionTypeValue.Seek: + if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); + 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(MakeUInt32Node(bin, "SubsectionSize")); + 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)); break; @@ -338,7 +453,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 => @@ -445,6 +560,93 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo return root; } + private void Scan_HIRC_ActionExceptParams(BinInterpNode root, EndianReader bin, uint version) + { + var countPos = bin.Position; + var propsCount = VarCount.ReadResizingUint(bin.BaseStream); + bin.JumpTo(countPos); + root.Items.Add(MakeWwiseVarCountNode(bin, "ExceptionCount")); + + if(propsCount > 0) + { + var props = new BinInterpNode(bin.Position, "Exceptions") { IsExpanded = true }; + for (var i = 0;i < propsCount; i++) + { + var item = MakeWwiseIdNode(bin, $"Exception {i}", $"{i}: ID"); + + if (version > 65) + { + item.Items.Add(MakeBoolByteNode(bin, "IsBus")); + } + + props.Items.Add(item); + } + + root.Items.Add(props); + } + } + + 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(); @@ -871,7 +1073,7 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) if(version <= 52) { - ReadStateGroup(sNode, bin, version); + ReadStateGroup(sNode); } else @@ -913,7 +1115,7 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) var item = new BinInterpNode(bin.Position, i.ToString()); item.Items.Add(MakeWwiseIdNode(bin, "StateGroup")); - ReadStateGroup(item, bin, version); + ReadStateGroup(item); groups.Items.Add(item); } @@ -922,15 +1124,16 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) } 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++) { @@ -943,7 +1146,7 @@ void ReadStateGroup(BinInterpNode root, EndianReader bin, uint version) // 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() @@ -991,9 +1194,9 @@ private void Scan_HIRC_RTPCParameterNodeBase(BinInterpNode root, EndianReader bi } 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")); From 9dc3f55563fbc7751950c3470b0db8dfd456e801 Mon Sep 17 00:00:00 2001 From: henbagle Date: Tue, 13 May 2025 19:04:22 -0500 Subject: [PATCH 02/21] BinInterp: Fix a bunch of WwiseBank issues --- .../BinaryInterpreter/WwiseBankScans.cs | 151 ++++++++---------- 1 file changed, 67 insertions(+), 84 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index 5e07777b7d..86fbbdee3f 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -71,6 +71,11 @@ 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) { @@ -113,6 +118,20 @@ private BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T if (string.IsNullOrEmpty(parsedValue)) parsedValue = "None"; return new BinInterpNode(bin.Position - 1, $"{name}: {parsedValue}") { Length = 1 }; } + + private static BinInterpNode MakeArrayNodeWwiseVarCount(EndianReader bin, string name, Func selector, bool IsExpanded = false, + BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) + { + 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) { @@ -260,15 +279,8 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo 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 typeLen = (version <= 48) ? 4 : 1; + root.Items.Add(new BinInterpNode(bin.Position - typeLen, $"Type: {type}") { Length = typeLen }); var fullSize = bin.ReadInt32() + (version <= 48 ? 8 : 5); @@ -294,10 +306,11 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo 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(MakeWwiseIdNode(bin, "Target", "Target ID")); + root.Items.Add(MakeWwiseIdRefNode(bin, "Target ID")); if (version <= 56) { root.Items.Add(MakeInt32Node(bin, "Delay")); @@ -326,17 +339,17 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo Scan_HIRC_ActionSpecificParams(root, bin, version, actionType); Scan_HIRC_ActionExceptParams(root, bin, version); } - root.Items.Add(MakeWwiseIdNode(bin, "BankId")); + root.Items.Add(MakeWwiseIdRefNode(bin, "BankId")); break; case ActionTypeValue.SetState: case ActionTypeValue.SetSwitch: if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); - root.Items.Add(MakeWwiseIdNode(bin, "GroupId")); - root.Items.Add(MakeWwiseIdNode(bin, "TargetStateId")); + root.Items.Add(MakeWwiseIdRefNode(bin, "GroupId")); + root.Items.Add(MakeWwiseIdRefNode(bin, "TargetStateId")); break; case ActionTypeValue.SetRTPC: if (version <= 56) root.Items.Add(MakeUInt32Node(bin, "SubsectionSize")); - root.Items.Add(MakeWwiseIdNode(bin, "RTPCId")); + root.Items.Add(MakeWwiseIdRefNode(bin, "RTPCId")); root.Items.Add(MakeFloatNode(bin, "RTPCValue")); break; case ActionTypeValue.SetFX1: @@ -405,7 +418,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo } break; case HircType.Event: - root.Items.Add(MakeArrayNode(bin, "Actions", i => MakeWwiseIdRefNode(bin, i.ToString()), IsExpanded:true)); + root.Items.Add(MakeArrayNodeWwiseVarCount(bin, "Actions", i => MakeWwiseIdRefNode(bin, i.ToString()))); break; case HircType.RandomSequenceContainer: Scan_HIRC_NodeBaseParams(root, bin, version, useFeedback); @@ -435,7 +448,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo root.Items.Add(MakeArrayNodeInt16Count(bin, "Playlist", i => { var n = new BinInterpNode(bin.Position, $"Item {i}"); - n.Items.Add(MakeWwiseIdNode(bin, "PlaylistItemId")); + n.Items.Add(MakeWwiseIdRefNode(bin, "PlaylistItemId")); if (version <= 56) n.Items.Add(MakeByteNode(bin, "Weight")); else n.Items.Add(MakeInt32Node(bin, "Weight")); return n; @@ -562,28 +575,16 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo private void Scan_HIRC_ActionExceptParams(BinInterpNode root, EndianReader bin, uint version) { - var countPos = bin.Position; - var propsCount = VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(countPos); - root.Items.Add(MakeWwiseVarCountNode(bin, "ExceptionCount")); - - if(propsCount > 0) + root.Items.Add(MakeArrayNodeWwiseVarCount(bin, "Exceptions", i => { - var props = new BinInterpNode(bin.Position, "Exceptions") { IsExpanded = true }; - for (var i = 0;i < propsCount; i++) + var item = MakeWwiseIdNode(bin, $"Exception {i}", $"{i}: ID"); + if (version > 65) { - var item = MakeWwiseIdNode(bin, $"Exception {i}", $"{i}: ID"); - - if (version > 65) - { - item.Items.Add(MakeBoolByteNode(bin, "IsBus")); - } - - props.Items.Add(item); + item.Items.Add(MakeBoolByteNode(bin, "IsBus")); } - root.Items.Add(props); - } + return item; + })); } private void Scan_HIRC_ActionSpecificParams(BinInterpNode root, EndianReader bin, uint version, ActionTypeValue actionType) @@ -649,8 +650,8 @@ private void Scan_HIRC_ActionSpecificParams(BinInterpNode root, EndianReader bin private void Scan_HIRC_BankSourceData(BinInterpNode root, EndianReader bin, uint version) { - var pluginExists = bin.ReadUInt32(); - bin.Skip(-4); + //var pluginExists = bin.ReadUInt32(); + //bin.Skip(-4); root.Items.Add(MakeUInt32Node(bin, "PluginID")); var streamTypeNode = new BinInterpNode(bin.Position, ""); @@ -761,7 +762,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) @@ -987,7 +988,7 @@ 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) { @@ -1078,49 +1079,28 @@ private void Scan_HIRC_State(BinInterpNode root, EndianReader bin, uint version) } else { - var countPos = bin.Position; - var propsCount = VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(countPos); - sNode.Items.Add(MakeWwiseVarCountNode(bin, "StatePropsCount")); - - if(propsCount > 0) + sNode.Items.Add(MakeArrayNodeWwiseVarCount(bin, "StateProperties", i => { - var props = new BinInterpNode(bin.Position, "PropertyInfo") { IsExpanded = true }; - for (var i = 0;i < propsCount; i++) - { - var item = new BinInterpNode(bin.Position, i.ToString()); - - item.Items.Add(MakeWwiseVarCountNode(bin, "PropertyId")); - var accumType = (AccumTypeInner)bin.ReadByte(); - 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); - } + var item = new BinInterpNode(bin.Position, i.ToString()); - sNode.Items.Add(props); - } - - countPos = bin.Position; - var groupsCount = VarCount.ReadResizingUint(bin.BaseStream); - bin.JumpTo(countPos); - sNode.Items.Add(MakeWwiseVarCountNode(bin, "StateGroupsCount")); - - if(groupsCount > 0) + item.Items.Add(MakeWwiseVarCountNode(bin, "PropertyId")); + var accumType = (AccumTypeInner)bin.ReadByte(); + 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")); + + return item; + })); + + sNode.Items.Add(MakeArrayNodeWwiseVarCount(bin, "StateGroups", 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()); - - item.Items.Add(MakeWwiseIdNode(bin, "StateGroup")); - ReadStateGroup(item); - groups.Items.Add(item); - } + var item = new BinInterpNode(bin.Position, i.ToString()); - sNode.Items.Add(groups); - } + item.Items.Add(MakeWwiseIdNode(bin, "StateGroup")); + ReadStateGroup(item); + + return item; + })); } root.Items.Add(sNode); @@ -1172,8 +1152,11 @@ 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(MakeBoolByteNode(bin, "IsRendered")); + } rtpc.Items.Add(MakeWwiseIdRefNode(bin, "RTPCId")); var rtpcType = bin.ReadByte(); if (version <= 140 && rtpcType == 0x02) rtpcType = 0x04; @@ -1207,7 +1190,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); @@ -1486,14 +1469,14 @@ BinInterpNode ScanHircObject(EndianReader bin, int idx) 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 }); + node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {playHasFadeIn = 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 }); + node.Items.Add(new BinInterpNode(bin.Position, $"Unknown 2: {RandomFade = bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); if (RandomFade) { node.Items.Add(MakeByteNode(bin, "Enabled?")); @@ -1506,7 +1489,7 @@ BinInterpNode ScanHircObject(EndianReader bin, int idx) 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 }); + node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {stopHasFadeOut = bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); if (stopHasFadeOut) { node.Items.Add(MakeByteNode(bin, "Unknown byte")); @@ -1523,7 +1506,7 @@ BinInterpNode ScanHircObject(EndianReader bin, int idx) 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 }); + node.Items.Add(new BinInterpNode(bin.Position, $"Has Fade In: {HasFade = bin.ReadBoolByte()}", NodeType.StructLeafByte) { Length = 1 }); if (HasFade) { node.Items.Add(MakeByteNode(bin, "Unknown byte")); From 72ef107f146867b98d0fd0eef7f9aea06c1a31eb Mon Sep 17 00:00:00 2001 From: henbagle Date: Wed, 14 May 2025 18:07:39 -0500 Subject: [PATCH 03/21] BinInterp: Add out var overloads for basic MakeNode methods --- .../BinaryInterpreterScans.cs | 62 +++++++++---- .../BinaryInterpreter/WwiseBankScans.cs | 91 ++++++++++--------- 2 files changed, 95 insertions(+), 58 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs index 79af8d0f48..4eb009e9bc 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs @@ -4755,9 +4755,17 @@ private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name, out } private static BinInterpNode MakeBoolByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolByte()}") { Length = 1 }; + + private static BinInterpNode MakeBoolByteNode(EndianReader bin, string name, out bool value) => new BinInterpNode(bin.Position, $"{name}: {value = 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 MakeFloatNode(EndianReader bin, string name, out float value) + { + return new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadFloat()}", NodeType.StructLeafFloat) + { Length = 4 }; + } + private static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string name, bool create) { if (create) @@ -4767,29 +4775,49 @@ private static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string n 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 MakeByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; + + private static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadByte()}") { Length = 1 }; - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; + private static BinInterpNode MakeSByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; + + private static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadSByte()}") { Length = 1 }; private static BinInterpNode MakeInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; + + private static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt16()}") { Length = 2 }; + + private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; + + private static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt16()}") { Length = 2 }; + + private static BinInterpNode MakeInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; - 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 static BinInterpNode MakeInt32Node(EndianReader bin, string name, out int value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; + + private static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; + + private static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt32()}") { Length = 4 }; + + private static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; + private static BinInterpNode MakeInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; + + private static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt64()}") { Length = 8 }; + + private static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; + + private static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt64()}") { Length = 8 }; + 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) => diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index 86fbbdee3f..7b6cd801f6 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -42,11 +42,9 @@ private record WwiseItem(uint Id, string Name, long Position); private BinInterpNode MakeWwiseIdNode(EndianReader bin, string refName, string nodeName = "ID") { - var id = bin.ReadUInt32(); - bin.Skip(-4); + var node = MakeUInt32Node(bin, nodeName, out var id); var item = new WwiseItem(id, refName, bin.Position); WwiseIdMap.Add(id, item); - var node = MakeUInt32Node(bin, nodeName); return node; } @@ -203,11 +201,10 @@ private List Scan_WwiseBank(byte[] data) 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) @@ -219,11 +216,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) @@ -281,16 +276,15 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo var type = HircSmartType.DeserializeStatic(bin.BaseStream, version); var typeLen = (version <= 48) ? 4 : 1; root.Items.Add(new BinInterpNode(bin.Position - typeLen, $"Type: {type}") { Length = typeLen }); - - var fullSize = bin.ReadInt32() + (version <= 48 ? 8 : 5); - - bin.Skip(-4); - root.Items.Add(MakeUInt32Node(bin, "Size")); + + + 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) { @@ -449,8 +443,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo { var n = new BinInterpNode(bin.Position, $"Item {i}"); n.Items.Add(MakeWwiseIdRefNode(bin, "PlaylistItemId")); - if (version <= 56) n.Items.Add(MakeByteNode(bin, "Weight")); - else n.Items.Add(MakeInt32Node(bin, "Weight")); + n.Items.Add(version <= 56 ? MakeByteNode(bin, "Weight") : MakeInt32Node(bin, "Weight")); return n; })); break; @@ -532,9 +525,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")); @@ -566,6 +557,36 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo }, 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 @@ -695,9 +716,7 @@ private void Scan_HIRC_BankSourceData(BinInterpNode root, EndianReader bin, uint 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")); @@ -787,9 +806,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) { @@ -816,10 +833,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) { @@ -892,14 +907,10 @@ private void Scan_HIRC_Positioning(BinInterpNode root, EndianReader bin, uint ve { if(version < 72) { - pNode.Items.Add(MakeBoolByteNode(bin, "Has2DPositioning")); - bin.Skip(-1); - has2dPositioning = bin.ReadBoolByte(); + pNode.Items.Add(MakeBoolByteNode(bin, "Has2DPositioning", out has2dPositioning)); } - pNode.Items.Add(MakeBoolByteNode(bin, "Has3DPositioning")); - bin.Skip(-1); - has3dPositioning = bin.ReadBoolByte(); + pNode.Items.Add(MakeBoolByteNode(bin, "Has3DPositioning", out has3dPositioning)); if ((!has3dPositioning && version <= 72) || has2dPositioning) { pNode.Items.Add(MakeBoolByteNode(bin, "HasPanner")); @@ -995,9 +1006,7 @@ private void Scan_HIRC_AuxParams(BinInterpNode root, EndianReader bin, uint vers 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 { From d7bafcfca6e6ee15e3d71ba1cf8c59fa4fc249c3 Mon Sep 17 00:00:00 2001 From: henbagle Date: Wed, 14 May 2025 21:38:50 -0500 Subject: [PATCH 04/21] BinInterp: Add go to referenced offset in bin interp treeview --- .../BinaryInterpreter/BinInterpNode.cs | 12 ++++ .../BinaryInterpreterWPF.xaml | 1 + .../BinaryInterpreterWPF.xaml.cs | 60 ++++++++++++++++++- .../BinaryInterpreter/WwiseBankScans.cs | 36 +++++++---- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs index a94599acfc..707d5b8654 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinInterpNode.cs @@ -234,4 +234,16 @@ public override bool IsExpanded } } } + + public class BinInterpNodeOffsetReference : BinInterpNode + { + public int OffsetTarget { get; set; } = -1; + + public BinInterpNodeOffsetReference(long pos, string text, + BinaryInterpreterWPF.NodeType nodeType = BinaryInterpreterWPF.NodeType.ReferenceToOffset) : base(pos, text, + nodeType) + { + + } + } } \ No newline at end of file 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..1e1fa8c89e 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() { @@ -509,6 +524,7 @@ public enum NodeType : sbyte Guid, Root, + ReferenceToOffset } #endregion @@ -932,6 +948,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/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index 7b6cd801f6..c955cd5714 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -40,10 +40,12 @@ 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 node = MakeUInt32Node(bin, nodeName, out var id); - var item = new WwiseItem(id, refName, bin.Position); + var item = new WwiseItem(id, refName, bin.Position - 4); WwiseIdMap.Add(id, item); return node; } @@ -52,15 +54,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; } @@ -196,6 +191,20 @@ private List Scan_WwiseBank(byte[] data) // 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 + 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)"; + } + } return subnodes; } @@ -514,10 +523,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; From a25cf995dfaf6037f00c6ff4c6055111f41ecf2a Mon Sep 17 00:00:00 2001 From: henbagle Date: Wed, 14 May 2025 22:13:46 -0500 Subject: [PATCH 05/21] BinInterp: Break up BinaryInterpreterScans.cs into several more files --- .../BinaryInterpreter/ActorScans.cs | 358 ++ .../BinaryInterpreterScans.cs | 5640 +++-------------- .../BinaryInterpreter/BioScans.cs | 2022 ++++++ .../BinaryInterpreter/ComponentScans.cs | 503 ++ .../BinaryInterpreter/FaceFXScans.cs | 668 ++ .../BinaryInterpreter/MaterialScans.cs | 117 + .../BinaryInterpreter/MeshScans.cs | 248 + .../BinaryInterpreter/NodeHelpers.cs | 392 ++ .../BinaryInterpreter/WwiseBankScans.cs | 30 +- 9 files changed, 5035 insertions(+), 4943 deletions(-) create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs new file mode 100644 index 0000000000..822203e855 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs @@ -0,0 +1,358 @@ +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; + +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}"))); + 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")); + 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/BinaryInterpreterScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs index 4eb009e9bc..6280a4ec0a 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs @@ -1,240 +1,19 @@ 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; 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 +49,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 +86,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 +110,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 +118,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 +143,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 +151,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 +177,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") + } + })); binarystart = (int)bin.Position; } @@ -654,272 +220,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()})")); } @@ -1004,12 +312,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(); @@ -1218,7 +520,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 +528,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")); + if (Pcc.Game == MEGame.ME3 || Pcc.Game.IsLEGame()) { - IsExpanded = true - }; - item.Items = ReadList(numStaticRecievers, i => + subnodes.Add(MakeEntryNode(bin, "PersistentFaceFXAnimSet")); + } + 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()})"), @@ -1470,4125 +665,819 @@ 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 rotOff = TrackOffsets[i * 2 + 1]; - 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); + 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); - 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 = 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: {getW(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: {getW(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: {getW(x, y, z)})") - { - Length = 12 - }; - break; - } - case AnimationCompressionFormat.ACF_BioFixed48: - { - const float shift = 0.70710678118f; - const float scale = 1.41421356237f; - const float precisionMult = 32767.0f; - var pos = bin.Position; - ushort a = bin.ReadUInt16(); - ushort b = bin.ReadUInt16(); - ushort c = bin.ReadUInt16(); - float x = (a & 0x7FFF) / precisionMult * scale - shift; - float y = (b & 0x7FFF) / precisionMult * scale - shift; - float z = (c & 0x7FFF) / precisionMult * scale - shift; - float w = getW(x, y, z); - int wPos = ((a >> 14) & 2) | ((b >> 15) & 1); - var rot = wPos switch - { - 0 => new Quaternion(w, x, y, z), - 1 => new Quaternion(x, w, y, z), - 2 => new Quaternion(x, y, w, z), - _ => new Quaternion(x, y, z, w) - }; - 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: - 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; - - static float getW(float x, float y, float z) - { - float wSquared = 1.0f - (x * x + y * y + z * z); - return (float)(wSquared > 0 ? Math.Sqrt(wSquared) : 0); - } - } - - /// - /// 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")) - }); - - 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; - }) + 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: {getW(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: {getW(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!"); } } - }); - 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) + catch (Exception ex) { - subnodes.Add(MakeEntryNode(bin, "PylonListStart")); - subnodes.Add(MakeEntryNode(bin, "PylonListEnd")); + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } - 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()}")) - }); + return subnodes; + } - int navListCount; - subnodes.Add(new BinInterpNode(bin.Position, $"NavRefs: ({navListCount = bin.ReadInt32()})") - { - Items = ReadList(navListCount, i => MakeEntryNode(bin, $"{i}")) - }); + #endregion - int numbersCount; - subnodes.Add(new BinInterpNode(bin.Position, - $"NavRefIndices: ({numbersCount = bin.ReadInt32()})") - { - Items = ReadList(numbersCount, i => MakeInt32Node(bin, $"{i}")) - }); - } - } + try + { + var TrackOffsets = CurrentLoadedExport.GetProperty>("CompressedTrackOffsets"); + var numFrames = CurrentLoadedExport.GetProperty("NumFrames")?.Value ?? 0; - int crossLevelActorsCount; - subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelActors?: ({crossLevelActorsCount = bin.ReadInt32()})") + List boneList; + if (Pcc.Game == MEGame.UDK) { - Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}")) - }); - - if (Pcc.Game is MEGame.ME1 or MEGame.LE1) + boneList = ((ExportEntry)CurrentLoadedExport.Parent)?.GetProperty>("TrackBoneNames")?.Select(np => $"{np}").ToList(); + } + else { - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?")); - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?")); + 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; } - if (Pcc.Game >= MEGame.ME3) + 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) { - 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}") - { - 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} - }) - } - }) - } - }) - } - }); + bin.Skip(12); + subnodes.Add(MakeInt32Node(bin, "AnimBinary Offset")); } - if (Pcc.Game == MEGame.UDK) + else 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")); + int numTracks = bin.ReadInt32() * 2; + bin.Skip(-4); - item = new BinInterpNode(bin.Position, "PrecomputedVolumeDistanceField") + BinInterpNode rawAnimDataNode = MakeInt32Node(bin, "RawAnimationData: NumTracks"); + subnodes.Add(rawAnimDataNode); + for (int i = 0; i < numTracks; i++) { - 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")); - - } - - 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 MakeReverseBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32() != 1}", 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 MakeBoolByteNode(EndianReader bin, string name, out bool value) => new BinInterpNode(bin.Position, $"{name}: {value = 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 MakeFloatNode(EndianReader bin, string name, out float value) - { - return new BinInterpNode(bin.Position, $"{name}: {value = 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; - } + 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); - private static BinInterpNode MakeByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; - - private static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadByte()}") { Length = 1 }; - - private static BinInterpNode MakeSByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; - - private static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadSByte()}") { Length = 1 }; - - private static BinInterpNode MakeInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; - - private static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt16()}") { Length = 2 }; - - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; - - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt16()}") { Length = 2 }; - - 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 value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; - - private static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; - - private static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt32()}") { Length = 4 }; - - private static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; - - private static BinInterpNode MakeInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; - - private static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt64()}") { Length = 8 }; - - private static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; - - private static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => - new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt64()}") { Length = 8 }; - - 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 }; - } + int posOff = TrackOffsets[i * 4]; + int posKeys = TrackOffsets[i * 4 + 1]; + int rotOff = TrackOffsets[i * 4 + 2]; + int rotKeys = TrackOffsets[i * 4 + 3]; - 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})") - { - Length = 4 - }; + if (posKeys > 0) + { + bin.JumpTo(startOffset + posOff); - 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; - } + AnimationCompressionFormat compressionFormat = posCompression; - 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; - } + 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 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; - } + readTrackTable(posKeys); + } - 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; - } + if (rotKeys > 0) + { + bin.JumpTo(startOffset + rotOff); - 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; - } + AnimationCompressionFormat compressionFormat = rotCompression; - private 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 - }; + 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")); + } - private static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; + 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: {getW(x, y, z)})") + { + Length = 12 + }; + break; + } + case AnimationCompressionFormat.ACF_BioFixed48: + { + const float shift = 0.70710678118f; + const float scale = 1.41421356237f; + const float precisionMult = 32767.0f; + var pos = bin.Position; + ushort a = bin.ReadUInt16(); + ushort b = bin.ReadUInt16(); + ushort c = bin.ReadUInt16(); + float x = (a & 0x7FFF) / precisionMult * scale - shift; + float y = (b & 0x7FFF) / precisionMult * scale - shift; + float z = (c & 0x7FFF) / precisionMult * scale - shift; + float w = getW(x, y, z); + int wPos = ((a >> 14) & 2) | ((b >> 15) & 1); + var rot = wPos switch + { + 0 => new Quaternion(w, x, y, z), + 1 => new Quaternion(x, w, y, z), + 2 => new Quaternion(x, y, w, z), + _ => new Quaternion(x, y, z, w) + }; + 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: + 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); + } - private static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat16()}, Y: {bin.ReadFloat16()})") { Length = 4 }; + readTrackTable(rotKeys); + } - private 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()}"), - } - }; - } + 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); - private static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) - { - return new BinInterpNode(bin.Position, $"{name}") - { - Items = - { - MakeVectorNode(bin, "Origin"), - MakeVectorNode(bin, "BoxExtent"), - MakeFloatNode(bin, "SphereRadius") + for (int j = 0; j < numKeys; j++) + { + trackTable.Items.Add(numFrames > 0xFF ? MakeUInt16Node(bin, $"{j}") : MakeByteNode(bin, $"{j}")); + } + } + } } - }; - } - - 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 }; - -#if DEBUG - if (materialGuidMap != null && materialGuidMap.TryGetValue(guid, out var matName)) - { - node.Header += " " + matName; } -#endif - - node.Tag = NodeType.Guid; - return node; - } - - private static BinInterpNode MakeGuidNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadGuid()}", NodeType.Guid) { Length = 16 }; - - 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 - }; - } - - 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 - }; - } - - 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()})") + catch (Exception ex) { - IsExpanded = IsExpanded, - Items = ReadList(count, selector), - ArrayAddAlgorithm = arrayAddAlgo, - Length = 1 - }; - } - - 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)"); - } + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + return subnodes; - private static BinInterpNode MakeArrayNode(int count, EndianReader bin, string name, Func selector, bool IsExpanded = false) - { - return new BinInterpNode(bin.Position, $"{name} ({count})") + static float getW(float x, float y, float z) { - IsExpanded = IsExpanded, - Items = ReadList(count, selector) - }; + float wSquared = 1.0f - (x * x + y * y + z * z); + return (float)(wSquared > 0 ? Math.Sqrt(wSquared) : 0); + } } - - - - [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")); + 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")); + 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") { - 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"), + MakeStringNode(bin, "Host"), + MakeStringNode(bin, "Map"), + MakeStringNode(bin, "Portal"), + 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}")) + }, + 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 = { - 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}: {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) { - matNode.Items.Add(new BinInterpNode("Error reading Material!")); + subnodes.Add(new BinInterpNode(bin.Position, $"APEX mesh?: {apexSize} bytes") { Length = apexSize }); + bin.Skip(apexSize); } + } - 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 cachedPhysBSPDataSize; + subnodes.Add(MakeInt32Node(bin, "size of byte")); + subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysBSPData Size: {cachedPhysBSPDataSize = bin.ReadInt32()}")); + if (cachedPhysBSPDataSize > 0) { - Items = + 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)}") { - 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, $"{entryRefString(bin)}") + { + 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: {entryRefString(bin)} | 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")); + subnodes.Add(MakeEntryNode(bin, "NavListEnd")); + subnodes.Add(MakeEntryNode(bin, "CoverListStart")); + subnodes.Add(MakeEntryNode(bin, "CoverListEnd")); 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")); + subnodes.Add(MakeEntryNode(bin, "PylonListEnd")); } - } - catch (Exception ex) - { - subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); - } + 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()}")) + }); - return subnodes; - } + int coverListCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CoverLinkRefs: ({coverListCount = bin.ReadInt32()})") + { + Items = ReadList(coverListCount, i => MakeEntryNode(bin, $"{i}")) + }); - 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"); + 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}")) + }); - foreach (var prop in props) - { - if (prop.Value > 0) - { - smacitems.Add(Pcc.GetUExport(prop.Value)); - } - else - { - smacitems.Add(null); + int numbersCount; + subnodes.Add(new BinInterpNode(bin.Position, + $"NavRefIndices: ({numbersCount = bin.ReadInt32()})") + { + Items = ReadList(numbersCount, i => MakeInt32Node(bin, $"{i}")) + }); } } - //find start of class binary (end of props) - int start = binarystart; + int crossLevelActorsCount; + subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelActors?: ({crossLevelActorsCount = bin.ReadInt32()})") + { + Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}")) + }); - //Lets make sure this binary is divisible by 64. - if ((data.Length - start) % 64 != 0) + if (Pcc.Game is MEGame.ME1 or MEGame.LE1) { - 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; + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?")); + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?")); } - int smcaindex = 0; - while (start < data.Length && smcaindex < smacitems.Count) + if (Pcc.Game >= MEGame.ME3) { - BinInterpNode smcanode = new BinInterpNode - { - Tag = NodeType.Unknown - }; - ExportEntry associatedData = smacitems[smcaindex]; - string staticmesh = ""; - string objtext = "Null - unused data"; - if (associatedData != null) + bool bInitialized; + int samplesCount; + subnodes.Add(new BinInterpNode(bin.Position, "PrecomputedLightVolume") { - 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) + Items = { - int staticmeshexp = EndianReader.ToInt32(smc_data, staticmeshstart + 0x18, Pcc.Endian); - if (staticmeshexp > 0 && staticmeshexp < CurrentLoadedExport.FileRef.ExportCount) + new BinInterpNode(bin.Position, $"bInitialized: ({bInitialized = bin.ReadBoolInt()})"), + ListInitHelper.ConditionalAdd(bInitialized, () => new ITreeItem[] { - staticmesh = Pcc.GetEntry(staticmeshexp).ObjectName.Instanced; - } + 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} + }) + } + }) + } + }) } - } - - smcanode.Header = $"{start:X4} [{smcaindex}] {objtext} {staticmesh}"; - smcanode.Offset = start; - subnodes.Add(smcanode); - - //Read nodes - for (int i = 0; i < 16; i++) + }); + } + if (Pcc.Game == MEGame.UDK) + { + BinInterpNode item = new BinInterpNode(bin.Position, "PrecomputedVisibilityHandler") { - 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}"; + 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")); - //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"), + MakeEntryNode(bin, "Instance") } - } - - //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:"), + 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; } @@ -5609,52 +1498,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; @@ -5746,48 +1589,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/BioScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs new file mode 100644 index 0000000000..46769e5a9c --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs @@ -0,0 +1,2022 @@ +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; + +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)}: {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 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 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"), + 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 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 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 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..d3e2c9230a --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs @@ -0,0 +1,503 @@ +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; + +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}")) + }); + 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 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")); + 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"), + 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 = 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")); + 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 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; + } +} \ 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..d58fdc1036 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs @@ -0,0 +1,668 @@ +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; + +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..c37ca71d22 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs @@ -281,5 +281,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")); + } + 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; + } + + 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..c25221ebe3 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs @@ -6,6 +6,7 @@ using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.Classes; @@ -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}"); + 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}"))); + } + } + catch (Exception ex) + { + subnodes.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); + } + + return subnodes; + } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs new file mode 100644 index 0000000000..3e7b12d3bb --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs @@ -0,0 +1,392 @@ +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 partial class BinaryInterpreterWPF +{ + private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; + + private static BinInterpNode MakeReverseBoolIntNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32() != 1}", 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 MakeBoolByteNode(EndianReader bin, string name, out bool value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadBoolByte()}") { Length = 1 }; + + private static BinInterpNode MakeByteNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; + + private static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadByte()}") { Length = 1 }; + + private static BinInterpNode MakeSByteNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; + + private static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadSByte()}") { Length = 1 }; + + private static BinInterpNode MakeInt16Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; + + private static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt16()}") { Length = 2 }; + + private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; + + private static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt16()}") { Length = 2 }; + + 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 value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; + + private static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; + + private static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt32()}") { Length = 4 }; + + private static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; + + private static BinInterpNode MakeInt64Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; + + private static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt64()}") { Length = 8 }; + + private static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; + + private static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => + new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt64()}") { Length = 8 }; + + private static BinInterpNode MakeFloatNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; + + private static BinInterpNode MakeFloatNode(EndianReader bin, string name, out float value) + { + return new BinInterpNode(bin.Position, $"{name}: {value = 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 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 }; + } + + 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) + }; + } + + 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 + }; + } + + 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 + }; + } + + 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 + }; + } + + 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)"); + } + + 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})") + { + 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; + } + + private 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 + }; + + private static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; + + private static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat16()}, Y: {bin.ReadFloat16()})") { Length = 4 }; + + private 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()}"), + } + }; + } + + private static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) + { + return new BinInterpNode(bin.Position, $"{name}") + { + Items = + { + MakeVectorNode(bin, "Origin"), + MakeVectorNode(bin, "BoxExtent"), + MakeFloatNode(bin, "SphereRadius") + } + }; + } + + private static BinInterpNode MakeGuidNode(EndianReader bin, string name) => + new BinInterpNode(bin.Position, $"{name}: {bin.ReadGuid()}", NodeType.Guid) { Length = 16 }; + + 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 }; + +#if DEBUG + if (materialGuidMap != null && materialGuidMap.TryGetValue(guid, out var matName)) + { + node.Header += " " + matName; + } +#endif + + node.Tag = NodeType.Guid; + return node; + } + + 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 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 }; + } + + 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 }; + } + + 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 string entryRefString(EndianReader bin) + { + int n = bin.ReadInt32(); + return $"#{n} {CurrentLoadedExport.FileRef.GetEntryString(n)}"; + } + + private 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 }; + } + + 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 }; + } +} \ No newline at end of file diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index c955cd5714..0504cefb26 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -1,4 +1,8 @@ -using LegendaryExplorer.SharedUI.Interfaces; +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; @@ -6,17 +10,13 @@ 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.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.Globalization; -using System.IO; -using ME3Tweaks.Wwiser.Model.Action; -using ME3Tweaks.Wwiser.Model.Action.Specific; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.AccumType; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.CurveScaling; using static ME3Tweaks.Wwiser.Model.Hierarchy.Enums.GroupType; @@ -96,22 +96,6 @@ 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 - { - 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 }; - } - private static BinInterpNode MakeArrayNodeWwiseVarCount(EndianReader bin, string name, Func selector, bool IsExpanded = false, BinInterpNode.ArrayPropertyChildAddAlgorithm arrayAddAlgo = BinInterpNode.ArrayPropertyChildAddAlgorithm.None) { From 56b828643c1e519dd3c0b32646146c6f317bcc9f Mon Sep 17 00:00:00 2001 From: henbagle Date: Sun, 18 May 2025 15:14:53 -0500 Subject: [PATCH 06/21] BinInterp: Refactor node creation methods into BinaryNodeFactory static class --- .../Tools/PackageDumper/TreeNode.cs | 10 +- .../BinaryInterpreter/ActorScans.cs | 5 +- .../BinaryInterpreter/AudioScans.cs | 3 +- .../BinaryInterpreter/BinInterpNode.cs | 8 +- .../BinaryInterpreterScans.cs | 105 ++++++------ .../BinaryInterpreterWPF.xaml.cs | 45 ------ .../{NodeHelpers.cs => BinaryNodeFactory.cs} | 152 +++++++++--------- .../BinaryInterpreter/BioScans.cs | 47 +++--- .../BinaryInterpreter/ComponentScans.cs | 33 ++-- .../BinaryInterpreter/FaceFXScans.cs | 1 + .../BinaryInterpreter/MaterialScans.cs | 14 +- .../BinaryInterpreter/MeshScans.cs | 16 +- .../BinaryInterpreter/NodeType.cs | 47 ++++++ .../BinaryInterpreter/ScriptObjectScans.cs | 63 ++++---- .../BinaryInterpreter/ShaderCacheScans.cs | 11 +- .../BinaryInterpreter/TextureScans.cs | 2 +- .../BinaryInterpreter/WwiseBankScans.cs | 3 +- 17 files changed, 289 insertions(+), 276 deletions(-) rename LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/{NodeHelpers.cs => BinaryNodeFactory.cs} (65%) create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeType.cs 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/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs index 822203e855..6415cfe107 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ActorScans.cs @@ -7,6 +7,7 @@ using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; using Newtonsoft.Json; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -197,14 +198,14 @@ private List StartTerrainScan(byte[] data, ref int 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}"))); + 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")); + node.Items.Add(MakeEntryNode(bin, "Terrain", Pcc)); node.Items.Add(new BinInterpNode(bin.Position, "Mask") { IsExpanded = true, 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 707d5b8654..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); } @@ -240,7 +240,7 @@ public class BinInterpNodeOffsetReference : BinInterpNode public int OffsetTarget { get; set; } = -1; public BinInterpNodeOffsetReference(long pos, string text, - BinaryInterpreterWPF.NodeType nodeType = BinaryInterpreterWPF.NodeType.ReferenceToOffset) : base(pos, text, + NodeType nodeType = NodeType.ReferenceToOffset) : base(pos, text, nodeType) { diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs index 6280a4ec0a..947ecc65a1 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterScans.cs @@ -9,6 +9,7 @@ using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Helpers; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls { @@ -206,7 +207,7 @@ private List StartMorphTargetScan(byte[] data, ref int binarystart) Items = { MakeVectorNode(bin, "Offset"), - MakeNameNode(bin, "Bone") + MakeNameNode(bin, "Bone", Pcc) } })); @@ -258,7 +259,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})") @@ -277,9 +278,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"), @@ -295,7 +296,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) }), } }) @@ -366,7 +367,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})") { @@ -374,14 +375,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)"), @@ -415,7 +416,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')}"), @@ -423,7 +424,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})") @@ -528,10 +529,10 @@ private List StartWorldScan(byte[] data, ref int binarystart) var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - subnodes.Add(MakeEntryNode(bin, "PersistentLevel")); + subnodes.Add(MakeEntryNode(bin, "PersistentLevel", Pcc)); if (Pcc.Game == MEGame.ME3 || Pcc.Game.IsLEGame()) { - subnodes.Add(MakeEntryNode(bin, "PersistentFaceFXAnimSet")); + subnodes.Add(MakeEntryNode(bin, "PersistentFaceFXAnimSet", Pcc)); } subnodes.AddRange(ReadList(4, i => new BinInterpNode(bin.Position, $"EditorView {i}") { @@ -546,17 +547,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; @@ -582,10 +583,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) @@ -600,8 +601,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") } })); @@ -629,13 +630,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)); @@ -656,7 +657,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) { @@ -1044,7 +1045,7 @@ private List StartObjectRedirectorScan(byte[] data, ref int binarySta 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")); + subnodes.Add(MakeEntryNode(bin, "Redirect references to this export to", Pcc)); return subnodes; } @@ -1100,7 +1101,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) var bin = new EndianReader(new MemoryStream(data)) { Endian = CurrentLoadedExport.FileRef.Endian }; bin.JumpTo(binarystart); - subnodes.Add(MakeEntryNode(bin, "Self")); + subnodes.Add(MakeEntryNode(bin, "Self", Pcc)); int actorsCount; BinInterpNode levelActorsNode; subnodes.Add(levelActorsNode = new BinInterpNode(bin.Position, $"Level Actors: ({actorsCount = bin.ReadInt32()})", NodeType.StructLeafInt) @@ -1108,7 +1109,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, IsExpanded = true }); - levelActorsNode.Items = ReadList(actorsCount, i => new BinInterpNode(bin.Position, $"{i}: {entryRefString(bin)}", NodeType.ArrayLeafObject) + levelActorsNode.Items = ReadList(actorsCount, i => new BinInterpNode(bin.Position, $"{i}: {MakeEntryNodeString(bin, Pcc)}", NodeType.ArrayLeafObject) { ArrayAddAlgorithm = BinInterpNode.ArrayPropertyChildAddAlgorithm.FourBytes, Parent = levelActorsNode, @@ -1118,35 +1119,35 @@ private List StartLevelScan(byte[] data, ref int binarystart) { Items = { - MakeStringNode(bin, "Protocol"), - MakeStringNode(bin, "Host"), - MakeStringNode(bin, "Map"), - MakeStringNode(bin, "Portal"), + 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)") { - Items = ReadList(bin.Skip(-4).ReadInt32(), i => MakeStringNode(bin, $"{i}")) + 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")); + 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}")) + 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}")) + 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, $"{entryRefString(bin)}: ({streamableTexInstCount = bin.ReadInt32()} StreamableTextureInstances)") + new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}: ({streamableTexInstCount = bin.ReadInt32()} StreamableTextureInstances)") { Items = ReadList(streamableTexInstCount, j => new BinInterpNode(bin.Position, $"{j}") { @@ -1170,7 +1171,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) if (Pcc.Game == MEGame.UDK) { subnodes.Add(MakeArrayNode(bin, "MeshesComponentsWithDynamicLighting?", - i => new BinInterpNode(bin.Position, $"{i}: {entryRefString(bin)}, {bin.ReadInt32()}"))); + i => new BinInterpNode(bin.Position, $"{i}: {MakeEntryNodeString(bin, Pcc)}, {bin.ReadInt32()}"))); } if (Pcc.Game >= MEGame.ME3) @@ -1197,7 +1198,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) int cachedPhysSMDataMapCount; subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysSMDataMap: ({cachedPhysSMDataMapCount = bin.ReadInt32()})") { - Items = ReadList(cachedPhysSMDataMapCount, i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}") + Items = ReadList(cachedPhysSMDataMapCount, i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}") { Items = { @@ -1229,7 +1230,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) int cachedPhysPerTriSMDataMapCount; subnodes.Add(new BinInterpNode(bin.Position, $"CachedPhysPerTriSMDataMap: ({cachedPhysPerTriSMDataMapCount = bin.ReadInt32()})") { - Items = ReadList(cachedPhysPerTriSMDataMapCount, i => new BinInterpNode(bin.Position, $"{entryRefString(bin)}") + Items = ReadList(cachedPhysPerTriSMDataMapCount, i => new BinInterpNode(bin.Position, $"{MakeEntryNodeString(bin, Pcc)}") { Items = { @@ -1260,7 +1261,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) int forceStreamTexturesCount; subnodes.Add(new BinInterpNode(bin.Position, $"ForceStreamTextures: ({forceStreamTexturesCount = bin.ReadInt32()})") { - Items = ReadList(forceStreamTexturesCount, i => MakeBoolIntNode(bin, $"Texture: {entryRefString(bin)} | ForceStream")) + Items = ReadList(forceStreamTexturesCount, i => MakeBoolIntNode(bin, $"Texture: {MakeEntryNodeString(bin, Pcc)} | ForceStream")) }); if (Pcc.Game == MEGame.UDK) @@ -1287,14 +1288,14 @@ private List StartLevelScan(byte[] data, ref int binarystart) 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")); + 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(MakeEntryNode(bin, "PylonListStart")); - subnodes.Add(MakeEntryNode(bin, "PylonListEnd")); + subnodes.Add(MakeEntryNode(bin, "PylonListStart", Pcc)); + subnodes.Add(MakeEntryNode(bin, "PylonListEnd", Pcc)); } if (Pcc.Game is MEGame.ME3 or MEGame.LE3 or MEGame.UDK) { @@ -1307,7 +1308,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) int coverListCount; subnodes.Add(new BinInterpNode(bin.Position, $"CoverLinkRefs: ({coverListCount = bin.ReadInt32()})") { - Items = ReadList(coverListCount, i => MakeEntryNode(bin, $"{i}")) + Items = ReadList(coverListCount, i => MakeEntryNode(bin, $"{i}", Pcc)) }); int intToByteMapCount; @@ -1329,7 +1330,7 @@ private List StartLevelScan(byte[] data, ref int binarystart) int navListCount; subnodes.Add(new BinInterpNode(bin.Position, $"NavRefs: ({navListCount = bin.ReadInt32()})") { - Items = ReadList(navListCount, i => MakeEntryNode(bin, $"{i}")) + Items = ReadList(navListCount, i => MakeEntryNode(bin, $"{i}", Pcc)) }); int numbersCount; @@ -1344,13 +1345,13 @@ private List StartLevelScan(byte[] data, ref int binarystart) int crossLevelActorsCount; subnodes.Add(new BinInterpNode(bin.Position, $"CrossLevelActors?: ({crossLevelActorsCount = bin.ReadInt32()})") { - Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}")) + Items = ReadList(crossLevelActorsCount, i => MakeEntryNode(bin, $"{i}", Pcc)) }); if (Pcc.Game is MEGame.ME1 or MEGame.LE1) { - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?")); - subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?")); + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 1?", Pcc)); + subnodes.Add(MakeEntryNode(bin, "BioArtPlaceable 2?", Pcc)); } if (Pcc.Game >= MEGame.ME3) @@ -1461,8 +1462,8 @@ private List StartPrefabInstanceScan(byte[] data, ref int binarystart IsExpanded = true, Items = { - MakeEntryNode(bin, "Archetype"), - MakeEntryNode(bin, "Instance") + MakeEntryNode(bin, "Archetype", Pcc), + MakeEntryNode(bin, "Instance", Pcc) } }, true)); subnodes.Add(MakeArrayNode(bin, "PrefabInstance_ObjectMap", i => new BinInterpNode(bin.Position, $"{i}") @@ -1470,7 +1471,7 @@ private List StartPrefabInstanceScan(byte[] data, ref int binarystart IsExpanded = true, Items = { - MakeEntryNode(bin, "Object:"), + MakeEntryNode(bin, "Object:", Pcc), MakeInt32Node(bin, "int") } }, true)); diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs index 1e1fa8c89e..22681dda48 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryInterpreterWPF.xaml.cs @@ -481,51 +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, - ReferenceToOffset - } #endregion diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs similarity index 65% rename from LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs rename to LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs index 3e7b12d3bb..61f32b22e5 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BinaryNodeFactory.cs @@ -9,87 +9,87 @@ namespace LegendaryExplorer.UserControls.ExportLoaderControls; -public partial class BinaryInterpreterWPF +public static class BinaryNodeFactory { - private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => + public static BinInterpNode MakeBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolInt()}", NodeType.StructLeafBool) { Length = 4 }; - private static BinInterpNode MakeReverseBoolIntNode(EndianReader bin, string name) => + public static BinInterpNode MakeReverseBoolIntNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32() != 1}", NodeType.StructLeafBool) { Length = 4 }; - private static BinInterpNode MakeBoolIntNode(EndianReader bin, string name, out bool boolVal) + public 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) => + public static BinInterpNode MakeBoolByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadBoolByte()}") { Length = 1 }; - private static BinInterpNode MakeBoolByteNode(EndianReader bin, string name, out bool value) => + public static BinInterpNode MakeBoolByteNode(EndianReader bin, string name, out bool value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadBoolByte()}") { Length = 1 }; - private static BinInterpNode MakeByteNode(EndianReader bin, string name) => + public static BinInterpNode MakeByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadByte()}") { Length = 1 }; - private static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => + public static BinInterpNode MakeByteNode(EndianReader bin, string name, out byte value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadByte()}") { Length = 1 }; - private static BinInterpNode MakeSByteNode(EndianReader bin, string name) => + public static BinInterpNode MakeSByteNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadSByte()}") { Length = 1 }; - private static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => + public static BinInterpNode MakeSByteNode(EndianReader bin, string name, out sbyte value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadSByte()}") { Length = 1 }; - private static BinInterpNode MakeInt16Node(EndianReader bin, string name) => + public static BinInterpNode MakeInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt16()}") { Length = 2 }; - private static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => + public static BinInterpNode MakeInt16Node(EndianReader bin, string name, out short value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt16()}") { Length = 2 }; - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => + public static BinInterpNode MakeUInt16Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt16()}") { Length = 2 }; - private static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => + public static BinInterpNode MakeUInt16Node(EndianReader bin, string name, out ushort value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt16()}") { Length = 2 }; - private static BinInterpNode MakeInt32Node(EndianReader bin, string name) => + public 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 value) => + public static BinInterpNode MakeInt32Node(EndianReader bin, string name, out int value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt32()}", NodeType.StructLeafInt) { Length = 4 }; - private static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => + public static BinInterpNode MakeUInt32Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32()}") { Length = 4 }; - private static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => + public static BinInterpNode MakeUInt32Node(EndianReader bin, string name, out uint value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt32()}") { Length = 4 }; - private static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => + public static BinInterpNode MakeUInt32HexNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt32():X8}") { Length = 4 }; - private static BinInterpNode MakeInt64Node(EndianReader bin, string name) => + public static BinInterpNode MakeInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadInt64()}") { Length = 8 }; - private static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => + public static BinInterpNode MakeInt64Node(EndianReader bin, string name, out long value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadInt64()}") { Length = 8 }; - private static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => + public static BinInterpNode MakeUInt64Node(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadUInt64()}") { Length = 8 }; - private static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => + public static BinInterpNode MakeUInt64Node(EndianReader bin, string name, out ulong value) => new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadUInt64()}") { Length = 8 }; - private static BinInterpNode MakeFloatNode(EndianReader bin, string name) => + public static BinInterpNode MakeFloatNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; - private static BinInterpNode MakeFloatNode(EndianReader bin, string name, out float value) + public static BinInterpNode MakeFloatNode(EndianReader bin, string name, out float value) { return new BinInterpNode(bin.Position, $"{name}: {value = bin.ReadFloat()}", NodeType.StructLeafFloat) { Length = 4 }; } - private static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string name, bool create) + public static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string name, bool create) { if (create) { @@ -100,73 +100,85 @@ private static BinInterpNode MakeFloatNodeConditional(EndianReader bin, string n return null; } - private BinInterpNode MakeNameNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, - $"{name}: {bin.ReadNameReference(Pcc).Instanced}", NodeType.StructLeafName) { Length = 8 }; + public static BinInterpNode MakeNameNode(EndianReader bin, string name, IMEPackage pcc) => 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}", + 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 }; - private BinInterpNode MakeEntryNode(EndianReader bin, string name) => - new BinInterpNode(bin.Position, $"{name}: {entryRefString(bin)}", NodeType.StructLeafObject) { Length = 4 }; + public static BinInterpNode MakeEntryNode(EndianReader bin, string name, IMEPackage pcc) + { + return new BinInterpNode(bin.Position, $"{name}: {MakeEntryNodeString(bin, pcc)}", NodeType.StructLeafObject) + { Length = 4 }; + } - private BinInterpNode MakeEntryNode(EndianReader bin, string name, out int uIndex) + public static BinInterpNode MakeEntryNode(EndianReader bin, string name, IMEPackage pcc, 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 }; + 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)}"; } - private static BinInterpNode MakeArrayNode(int count, EndianReader bin, string name, Func selector, bool IsExpanded = false) + 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, + IsExpanded = isExpanded, Items = ReadList(count, selector) }; } - private static BinInterpNode MakeArrayNode(EndianReader bin, string name, Func selector, bool IsExpanded = false, + 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, + IsExpanded = isExpanded, Items = ReadList(count, selector), ArrayAddAlgorithm = arrayAddAlgo, Length = 4 }; } - private static BinInterpNode MakeArrayNodeByteCount(EndianReader bin, string name, Func selector, bool IsExpanded = false, + 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, + IsExpanded = isExpanded, Items = ReadList(count, selector), ArrayAddAlgorithm = arrayAddAlgo, Length = 1 }; } - private static BinInterpNode MakeArrayNodeInt16Count(EndianReader bin, string name, Func selector, bool IsExpanded = false, + 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, + IsExpanded = isExpanded, Items = ReadList(count, selector), ArrayAddAlgorithm = arrayAddAlgo, Length = 2 }; } - private static BinInterpNode MakeByteArrayNode(EndianReader bin, string name) + public static BinInterpNode MakeByteArrayNode(EndianReader bin, string name) { int pos = (int)bin.Position; int count = bin.ReadInt32(); @@ -174,14 +186,14 @@ private static BinInterpNode MakeByteArrayNode(EndianReader bin, string name) return new BinInterpNode(pos, $"{name} ({count} bytes)"); } - private static BinInterpNode MakePackedNormalNode(EndianReader bin, string name) => + 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 }; - private static BinInterpNode MakeVectorNodeEditable(EndianReader bin, string name, bool expanded = false) + 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 }; @@ -193,7 +205,7 @@ private static BinInterpNode MakeVectorNodeEditable(EndianReader bin, string nam return node; } - private static BinInterpNode MakeVector2DNodeEditable(EndianReader bin, string name, bool expanded = false) + 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 }; @@ -204,7 +216,7 @@ private static BinInterpNode MakeVector2DNodeEditable(EndianReader bin, string n return node; } - private static BinInterpNode MakeVectorNode(EndianReader bin, string name) + 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 }; @@ -215,7 +227,7 @@ private static BinInterpNode MakeVectorNode(EndianReader bin, string name) return node; } - private static BinInterpNode MakeQuatNode(EndianReader bin, string name) + 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()})") @@ -228,7 +240,7 @@ private static BinInterpNode MakeQuatNode(EndianReader bin, string name) return node; } - private static BinInterpNode MakeRotatorNode(EndianReader bin, string name) + 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 }; @@ -239,7 +251,7 @@ private static BinInterpNode MakeRotatorNode(EndianReader bin, string name) return node; } - private static BinInterpNode MakeBoxNode(EndianReader bin, string name) => + public static BinInterpNode MakeBoxNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, name) { IsExpanded = true, @@ -252,13 +264,13 @@ private static BinInterpNode MakeBoxNode(EndianReader bin, string name) => Length = 25 }; - private static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => + public static BinInterpNode MakeVector2DNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat()}, Y: {bin.ReadFloat()})") { Length = 8 }; - private static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => + public static BinInterpNode MakeVector2DHalfNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: (X: {bin.ReadFloat16()}, Y: {bin.ReadFloat16()})") { Length = 4 }; - private static BinInterpNode MakeColorNode(EndianReader bin, string name) + public static BinInterpNode MakeColorNode(EndianReader bin, string name) { return new BinInterpNode(bin.Position, $"{name}") { @@ -273,7 +285,7 @@ private static BinInterpNode MakeColorNode(EndianReader bin, string name) }; } - private static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) + public static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string name) { return new BinInterpNode(bin.Position, $"{name}") { @@ -286,10 +298,10 @@ private static BinInterpNode MakeBoxSphereBoundsNode(EndianReader bin, string na }; } - private static BinInterpNode MakeGuidNode(EndianReader bin, string name) => + public static BinInterpNode MakeGuidNode(EndianReader bin, string name) => new BinInterpNode(bin.Position, $"{name}: {bin.ReadGuid()}", NodeType.Guid) { Length = 16 }; - private static BinInterpNode MakeMaterialGuidNode(EndianReader bin, string name, + public static BinInterpNode MakeMaterialGuidNode(EndianReader bin, string name, Dictionary materialGuidMap = null) { var guid = bin.ReadGuid(); @@ -306,12 +318,12 @@ private static BinInterpNode MakeMaterialGuidNode(EndianReader bin, string name, return node; } - private BinInterpNode MakeStringNode(EndianReader bin, string nodeName) + public static BinInterpNode MakeStringNode(EndianReader bin, string nodeName, MEGame game) { int pos = (int)bin.Position; int strLen = bin.ReadInt32(); string str; - if (Pcc.Game is MEGame.ME3 or MEGame.LE3) + if (game is MEGame.ME3 or MEGame.LE3) { strLen *= -2; str = bin.BaseStream.ReadStringUnicodeNull(strLen); @@ -324,7 +336,7 @@ private BinInterpNode MakeStringNode(EndianReader bin, string nodeName) return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; } - private static BinInterpNode MakeStringUTF8Node(EndianReader bin, string nodeName) + public static BinInterpNode MakeStringUTF8Node(EndianReader bin, string nodeName) { int pos = (int)bin.Position; int strLen = bin.ReadInt32(); @@ -332,7 +344,7 @@ private static BinInterpNode MakeStringUTF8Node(EndianReader bin, string nodeNam return new BinInterpNode(pos, $"{nodeName}: {str}", NodeType.StructLeafStr) { Length = strLen + 4 }; } - private static BinInterpNode MakeSHANode(EndianReader bin, string name, out string sha) + public static BinInterpNode MakeSHANode(EndianReader bin, string name, out string sha) { var shaBytes = bin.ReadBytes(20); StringBuilder sb = new StringBuilder(); @@ -345,7 +357,7 @@ private static BinInterpNode MakeSHANode(EndianReader bin, string name, out stri return new BinInterpNode(bin.Position, $"{name}: {sha}") { Length = 20 }; } - private static List ReadList(int count, Func selector) + public static List ReadList(int count, Func selector) { //sanity check. if this number is too small, feel free to increase if (count > 5097152) @@ -362,19 +374,13 @@ private static List ReadList(int count, Func selector } catch (Exception ex) { - new BinInterpNode { Header = $"Error reading binary data: {ex}" }; + list.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } return list; } - - private string entryRefString(EndianReader bin) - { - int n = bin.ReadInt32(); - return $"#{n} {CurrentLoadedExport.FileRef.GetEntryString(n)}"; - } - private BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T : Enum + public static BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T : Enum { var value = bin.ReadByte(); var parsedValue = Enum.GetName(typeof(T), value); @@ -382,7 +388,7 @@ private BinInterpNode MakeByteEnumNode(EndianReader bin, string name) where T return new BinInterpNode(bin.Position - 1, $"{name}: {parsedValue}") { Length = 1 }; } - private BinInterpNode MakeUInt32EnumNode(EndianReader bin, string name) where T : Enum + public static BinInterpNode MakeUInt32EnumNode(EndianReader bin, string name) where T : Enum { var value = bin.ReadUInt32(); var parsedValue = Enum.GetName(typeof(T), value); diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs index 46769e5a9c..bae06e0154 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/BioScans.cs @@ -9,6 +9,7 @@ using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.Classes; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -25,7 +26,7 @@ private List StartBioPawnScan(byte[] data, ref int 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 }) + Items = ReadList(count, i => new BinInterpNode(bin.Position, $"{bin.ReadNameReference(Pcc)}: {MakeEntryNodeString(bin, Pcc)}", NodeType.StructLeafObject) { Length = 4 }) }); binarystart = (int)bin.Position; @@ -51,7 +52,7 @@ private List StartSFXMorphFaceFrontEndDataSourceScan(byte[] data, ref IsExpanded = true, Items = { - MakeStringNode(bin, "Name"), + MakeStringNode(bin, "Name", Pcc.Game), MakeInt32Node(bin, "Index") } }, true)); @@ -1629,15 +1630,15 @@ private List StartBioGestureRuntimeDataScan(byte[] data, ref int bina 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") + 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) } }); } @@ -1653,9 +1654,9 @@ private List StartBioGestureRuntimeDataScan(byte[] data, ref int bina 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(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")); @@ -1672,21 +1673,21 @@ private List StartBioGestureRuntimeDataScan(byte[] data, ref int bina Length = 8 }; propActionsNode.Items.Add(node2); - node2.Items.Add(MakeNameNode(bin, "nmActionName")); + node2.Items.Add(MakeNameNode(bin, "nmActionName", Pcc)); if (CurrentLoadedExport.Game.IsGame2()) { - node2.Items.Add(MakeStringNode(bin, "sEffect")); + node2.Items.Add(MakeStringNode(bin, "sEffect", Pcc.Game)); } node2.Items.Add(MakeBoolIntNode(bin, "bActivate")); - node2.Items.Add(MakeNameNode(bin, "nmAttachTo")); + 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")); - node2.Items.Add(MakeStringNode(bin, "sClientEffect")); + 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") { @@ -1696,7 +1697,7 @@ private List StartBioGestureRuntimeDataScan(byte[] data, ref int bina { MakeVectorNode(bin, "vHitLocation"), MakeVectorNode(bin, "vHitNormal"), - MakeNameNode(bin, "nmHitBone"), + MakeNameNode(bin, "nmHitBone", Pcc), MakeVectorNode(bin, "vRayDir"), MakeVectorNode(bin, "vSpawnValue") } @@ -1742,7 +1743,7 @@ private List Scan_Bio2DA(byte[] data) type switch { Bio2DACell.Bio2DADataType.TYPE_INT => MakeInt32Node(bin, "Value"), - Bio2DACell.Bio2DADataType.TYPE_NAME => MakeNameNode(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() @@ -1968,7 +1969,7 @@ private List StartBioGestureRulesDataScan(byte[] data, ref int binary var node = new BinInterpNode(bin.Position, $"Rule {i}"); subnodes.Add(node); - node.Items.Add(MakeNameNode(bin, "Name")); + node.Items.Add(MakeNameNode(bin, "Name", Pcc)); var subcount = bin.ReadInt32(); var subnode = new BinInterpNode(bin.Position - 4, $"Num somethings: {subcount}"); @@ -1977,7 +1978,7 @@ private List StartBioGestureRulesDataScan(byte[] data, ref int binary for (int j = 0; j < subcount; j++) { // Read name, some integer - subnode.Items.Add(MakeNameNode(bin, "SomeName")); + subnode.Items.Add(MakeNameNode(bin, "SomeName", Pcc)); subnode.Items.Add(MakeInt32Node(bin, "SomeNum")); } } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs index d3e2c9230a..9b1dfed6d0 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/ComponentScans.cs @@ -6,6 +6,7 @@ using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -32,11 +33,11 @@ private List StartStaticMeshComponentScan(byte[] data, ref int binary }; node.Items.Add(new BinInterpNode(bin.Position, $"ShadowMaps ({bin.ReadInt32()})") { - Items = ReadList(bin.Skip(-4).ReadInt32(), j => MakeEntryNode(bin, $"{j}")) + 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}")) + 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 @@ -205,7 +206,7 @@ private List StartModelComponentScan(byte[] data, ref int binarystart { int count; bin.JumpTo(binarystart); - subnodes.Add(MakeEntryNode(bin, "Model")); + subnodes.Add(MakeEntryNode(bin, "Model", Pcc)); subnodes.Add(MakeInt32Node(bin, "ZoneIndex")); subnodes.Add(new BinInterpNode(bin.Position, $"Elements ({count = bin.ReadInt32()})") { @@ -214,15 +215,15 @@ private List StartModelComponentScan(byte[] data, ref int binarystart Items = { MakeLightMapNode(bin), - MakeEntryNode(bin, "Component"), - MakeEntryNode(bin, "Material"), + 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}")) + Items = ReadList(count, j => MakeEntryNode(bin, $"{j}", Pcc)) }, new BinInterpNode(bin.Position, $"IrrelevantLights ({count = bin.ReadInt32()})") { @@ -311,7 +312,7 @@ private List StartDecalComponentScan(byte[] data, ref int binarystart var node = new BinInterpNode(bin.Position, $"{i}"); try { - node.Items.Add(MakeEntryNode(bin, "Component")); + 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()})") { @@ -351,7 +352,7 @@ private List StartDecalComponentScan(byte[] data, ref int binarystart 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 }) + 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")); @@ -399,7 +400,7 @@ private BinInterpNode MakeLightMapNode(EndianReader bin, List<(int, int)> lightm }, ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_1D, () => new ITreeItem[] { - MakeEntryNode(bin, "Owner"), + MakeEntryNode(bin, "Owner", Pcc), MakeUInt32Node(bin, "BulkDataFlags:"), new BinInterpNode(bin.Position, $"ElementCount: {bulkSerializeElementCount = bin.ReadInt32()}"), new BinInterpNode(bin.Position, $"BulkDataSizeOnDisk: {bulkSerializeDataSize = bin.ReadInt32()}"), @@ -431,15 +432,15 @@ private BinInterpNode MakeLightMapNode(EndianReader bin, List<(int, int)> lightm }.NonNull()), ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_2D, () => new List { - MakeEntryNode(bin, "Texture 1"), + MakeEntryNode(bin, "Texture 1", Pcc), MakeVectorNodeEditable(bin, "ScaleVector 1", true), - MakeEntryNode(bin, "Texture 2"), + MakeEntryNode(bin, "Texture 2", Pcc), MakeVectorNodeEditable(bin, "ScaleVector 2", true), - MakeEntryNode(bin, "Texture 3"), + MakeEntryNode(bin, "Texture 3", Pcc), MakeVectorNodeEditable(bin, "ScaleVector 3", true), ListInitHelper.ConditionalAdd(Pcc.Game < MEGame.ME3, () => new ITreeItem[] { - MakeEntryNode(bin, "Texture 4"), + MakeEntryNode(bin, "Texture 4", Pcc), MakeVectorNodeEditable(bin, "ScaleVector 4", true), }), MakeVector2DNodeEditable(bin, "CoordinateScale", true), @@ -465,11 +466,11 @@ private BinInterpNode MakeLightMapNode(EndianReader bin, List<(int, int)> lightm }), ListInitHelper.ConditionalAdd(lightMapType == ELightMapType.LMT_4 || lightMapType == ELightMapType.LMT_6, () => new List { - MakeEntryNode(bin, "Texture 1"), + MakeEntryNode(bin, "Texture 1", Pcc), new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), - MakeEntryNode(bin, "Texture 2"), + MakeEntryNode(bin, "Texture 2", Pcc), new ListInitHelper.InitCollection(ReadList(8, j => MakeFloatNode(bin, "Unknown float"))), - MakeEntryNode(bin, "Texture 3"), + 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"))), }), diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs index d58fdc1036..c00cdb46c6 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/FaceFXScans.cs @@ -7,6 +7,7 @@ using LegendaryExplorerCore.Helpers; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Unreal.BinaryConverters; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MaterialScans.cs index c37ca71d22..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 { @@ -344,12 +342,12 @@ private BinInterpNode ReadMaterialUniformExpression(EndianReader bin, string pre } else { - node.Items.Add(MakeEntryNode(bin, "TextureIndex")); + node.Items.Add(MakeEntryNode(bin, "TextureIndex", Pcc)); } break; case "FMaterialUniformExpressionFlipbookParameter": node.Items.Add(MakeInt32Node(bin, "Index:")); - node.Items.Add(MakeEntryNode(bin, "TextureIndex")); + node.Items.Add(MakeEntryNode(bin, "TextureIndex", Pcc)); break; case "FMaterialUniformExpressionTextureParameter": node.Items.Add(new BinInterpNode(bin.Position, $"ParameterName: {bin.ReadNameReference(Pcc).Instanced}")); diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs index c25221ebe3..2b4d983780 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/MeshScans.cs @@ -6,8 +6,8 @@ using LegendaryExplorerCore.Gammtek.IO; using LegendaryExplorerCore.Packages; using LegendaryExplorerCore.Helpers; -using LegendaryExplorerCore.Unreal; using LegendaryExplorerCore.Unreal.Classes; +using static LegendaryExplorer.UserControls.ExportLoaderControls.BinaryNodeFactory; namespace LegendaryExplorer.UserControls.ExportLoaderControls; @@ -24,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")); @@ -111,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")); @@ -271,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")); } @@ -292,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) @@ -328,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 = @@ -477,7 +477,7 @@ private List StartSkeletalMeshScan(byte[] data, ref int binarystart) subnodes.Add(MakeBoxSphereBoundsNode(bin, "Bounds")); subnodes.Add(MakeArrayNode(bin, "Materials", i => { - var matNode = MakeEntryNode(bin, $"{i}"); + var matNode = MakeEntryNode(bin, $"{i}", Pcc); try { var value = bin.Skip(-4).ReadInt32(); @@ -700,7 +700,7 @@ private List StartSkeletalMeshScan(byte[] data, ref int binarystart) 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(MakeArrayNode(bin, "ClothingAssets", i => MakeEntryNode(bin, $"{i}", Pcc))); } } catch (Exception ex) 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 46c0523ddf..cbf659b92e 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); } @@ -1599,7 +1598,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 0504cefb26..2f063cddea 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -31,6 +31,7 @@ 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; @@ -258,7 +259,7 @@ 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) From f8790c40fe91524c0957c6e3dbaed42496b402d4 Mon Sep 17 00:00:00 2001 From: henbagle Date: Sun, 8 Jun 2025 17:30:32 -0500 Subject: [PATCH 07/21] Update Wwiser, BinInterp: Scan STMG chunk, Hirc State item on some versions --- .../BinaryInterpreter/WwiseBankScans.cs | 147 +++++++++++++++--- LegendaryExplorer/submodules/Wwiser.NET | 2 +- 2 files changed, 128 insertions(+), 21 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index 0504cefb26..96f417192b 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -12,6 +12,7 @@ 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; @@ -20,7 +21,6 @@ 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; @@ -170,6 +170,9 @@ private List Scan_WwiseBank(byte[] data) case "INIT": Scan_WwiseBank_INIT(chunkNode, bin); break; + case "STMG": + Scan_WwiseBank_STMG(chunkNode, bin, version); + break; } // Just in case we don't parse chunk in full - jump to next chunk @@ -281,6 +284,41 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo 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 if (version <= 126) + { + var propBPos = bin.Position; + var propCount = bin.ReadByte(); + 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) = ParameterId.DeserializeStatic(bin.BaseStream, version, false); // TODO: use modulator not handles on high versions! + return paramId.HasValue + ? new BinInterpNode(pidPos, $"ParameterId {i}: {Enum.GetName(paramId.Value)}") + { Length = (int)(bin.Position - pidPos) } + : new BinInterpNode(pidPos, $"ModulatorParameterId {i}: {Enum.GetName(modParamId.Value)}") + { Length = (int)(bin.Position - pidPos) }; + + }, true)); + root.Items.Add(MakeArrayNode(propCount, bin, "Values", i => MakeFloatNode(bin, $"{i}"))); + } + + break; case HircType.Sound: Scan_HIRC_BankSourceData(root, bin, version); Scan_HIRC_NodeBaseParams(root, bin, version, useFeedback); @@ -539,14 +577,7 @@ 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); @@ -588,6 +619,15 @@ 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 => @@ -1167,17 +1207,15 @@ private void Scan_HIRC_RTPCParameterNodeBase(BinInterpNode root, EndianReader bi 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")); - 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); - } + + var pidPos = bin.Position; + var (paramId, modParamId) = ParameterId.DeserializeStatic(bin.BaseStream, version, false); // TODO: use modulator not handles on high versions! + rtpc.Items.Add(paramId.HasValue + ? new BinInterpNode(pidPos, $"ParameterId: {Enum.GetName(paramId.Value)}") + { Length = (int)(bin.Position - pidPos) } + : new BinInterpNode(pidPos, $"ModulatorParameterId: {Enum.GetName(modParamId.Value)}") + { Length = (int)(bin.Position - pidPos) }); + rtpc.Items.Add(MakeWwiseIdRefNode(bin, "RtpcCurveId")); rtpc.Items.Add(MakeByteEnumNode(bin, "CurveScaling")); rtpc.Items.Add(MakeArrayNodeInt16Count(bin, "Graph", j => @@ -1243,6 +1281,75 @@ private void Scan_WwiseBank_INIT(BinInterpNode root, EndianReader bin) })); } + private void Scan_WwiseBank_STMG(BinInterpNode root, EndianReader bin, uint version) + { + 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 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 => MakeHIRCNode(j, bin, version, false))); + 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) + { + var rtpcType = bin.ReadByte(); + if (version <= 140 && rtpcType == 0x02) + { + rtpcType = 0x04; + } + sg.Items.Add(new BinInterpNode(bin.Position - 1, $"RtpcType: {Enum.GetName((RtpcTypeInner)rtpcType)}") { Length = 1 }); + } + 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) + { + 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; + })); + + 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"]) + { + at.Items.Add(MakeFloatNode(bin, p)); + } + return at; + })); + } + private List Scan_WwiseBankOld(byte[] data) { var subnodes = new List(); diff --git a/LegendaryExplorer/submodules/Wwiser.NET b/LegendaryExplorer/submodules/Wwiser.NET index eed6d1af12..2b3e882040 160000 --- a/LegendaryExplorer/submodules/Wwiser.NET +++ b/LegendaryExplorer/submodules/Wwiser.NET @@ -1 +1 @@ -Subproject commit eed6d1af125b224d0d05797c80c001a37a31ade1 +Subproject commit 2b3e882040bea715a24a6e5738f838d55d146eac From b22ecba800c278530e6f47e44bb8c69b3b9d3f20 Mon Sep 17 00:00:00 2001 From: henbagle Date: Mon, 9 Jun 2025 12:39:52 -0500 Subject: [PATCH 08/21] Correct parse of HircState ParameterId --- .../BinaryInterpreter/NodeHelpers.cs | 2 +- .../BinaryInterpreter/WwiseBankScans.cs | 9 +++++---- LegendaryExplorer/submodules/Wwiser.NET | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs index 3e7b12d3bb..a50ba33d33 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/NodeHelpers.cs @@ -362,7 +362,7 @@ private static List ReadList(int count, Func selector } catch (Exception ex) { - new BinInterpNode { Header = $"Error reading binary data: {ex}" }; + list.Add(new BinInterpNode { Header = $"Error reading binary data: {ex}" }); } return list; diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs index 96f417192b..42b9b31daf 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/BinaryInterpreter/WwiseBankScans.cs @@ -299,15 +299,16 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo root.Items.Add(MakeByteEnumNode(bin, "LPFValueMeaning")); } } - else if (version <= 126) + else { var propBPos = bin.Position; - var propCount = bin.ReadByte(); + 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) = ParameterId.DeserializeStatic(bin.BaseStream, version, false); // TODO: use modulator not handles on high versions! + // TODO: Does this ever use modulator? prolly not + var (paramId, modParamId) = ParameterId.DeserializeStatic(bin.BaseStream, version, false, isState: true); return paramId.HasValue ? new BinInterpNode(pidPos, $"ParameterId {i}: {Enum.GetName(paramId.Value)}") { Length = (int)(bin.Position - pidPos) } @@ -315,7 +316,7 @@ private BinInterpNode MakeHIRCNode(int index, EndianReader bin, uint version, bo { Length = (int)(bin.Position - pidPos) }; }, true)); - root.Items.Add(MakeArrayNode(propCount, bin, "Values", i => MakeFloatNode(bin, $"{i}"))); + root.Items.Add(MakeArrayNode(propCount, bin, "Values", i => MakeFloatNode(bin, $"{i}"), true)); } break; diff --git a/LegendaryExplorer/submodules/Wwiser.NET b/LegendaryExplorer/submodules/Wwiser.NET index 2b3e882040..a0c27df67e 160000 --- a/LegendaryExplorer/submodules/Wwiser.NET +++ b/LegendaryExplorer/submodules/Wwiser.NET @@ -1 +1 @@ -Subproject commit 2b3e882040bea715a24a6e5738f838d55d146eac +Subproject commit a0c27df67ea8a5787ab440323ce378eaa3f1cc01 From c8b41c7392059fec185d20d69efb62b530d8ac26 Mon Sep 17 00:00:00 2001 From: henbagle Date: Mon, 23 Jun 2025 15:38:55 -0500 Subject: [PATCH 09/21] Implement Wwiser.NET into LEC, Soundpanel Add Wwiser.NET ObjectBinary subclass for WwiseBanks\nMove Soundplorer into new namespace, break HIRC hex editor into it's own UserControl\nPort soundplorer mostly to Wwiser.NET backing types, breaking many things\nPort some other small functions to use Wwiser.NET types --- .../Converters/SoundplorerConverters.cs | 30 +- .../AssetDatabase/AssetDatabaseWindow.xaml | 3 +- .../Dialogue Editor/DialogueEditorWindow.xaml | 5 +- .../Experiments/PackageEditorExperimentsO.cs | 108 ++++--- .../PackageEditor/PackageEditorWindow.xaml | 3 +- .../PackageEditor/PackageEditorWindow.xaml.cs | 1 + .../Tools/Soundplorer/SoundplorerEntries.cs | 5 +- .../Tools/Soundplorer/SoundplorerWPF.xaml | 3 +- .../Tools/Soundplorer/SoundplorerWPF.xaml.cs | 62 ++-- .../Tools/WwiseEditor/WwiseEditorWindow.xaml | 3 +- .../FaceFXAnimSetEditorControl.xaml | 3 +- .../FaceFXAnimSetEditorControl.xaml.cs | 1 + .../Soundpanel/HIRCDisplayObject.cs | 61 +++- .../Soundpanel/HIRCItemEditor.xaml | 67 ++++ .../Soundpanel/HIRCItemEditor.xaml.cs | 276 ++++++++++++++++ .../Soundpanel/Soundpanel.xaml | 68 +--- .../Soundpanel/Soundpanel.xaml.cs | 295 ++++-------------- .../Soundpanel/SoundpanelAudioPlayer.cs | 7 +- .../LegendaryExplorerCore.csproj | 4 + .../Unreal/BinaryConverters/WwiseBank.cs | 50 ++- LegendaryExplorer/submodules/Wwiser.NET | 2 +- 21 files changed, 648 insertions(+), 409 deletions(-) create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml create mode 100644 LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml.cs 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 c79839ca89..ce0c3664ff 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" @@ -1068,7 +1069,7 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/Dialogue Editor/DialogueEditorWindow.xaml index bbddefffa8..75e25e6e7f 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" @@ -1383,11 +1384,11 @@ Female - + Male - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs index 5ea16a6d84..8a8e16a2b4 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsO.cs @@ -17,6 +17,8 @@ using System.Linq; using System.Numerics; using System.Windows; +using ME3Tweaks.Wwiser.Model.Action; +using WwiseParserLib.Parsers; using static LegendaryExplorer.Misc.ExperimentsTools.PackageAutomations; using static LegendaryExplorer.Misc.ExperimentsTools.SequenceAutomations; using static LegendaryExplorer.Misc.ExperimentsTools.SharedMethods; @@ -1931,30 +1933,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)); + } } } @@ -1998,6 +2002,8 @@ public static void UpdateAudioIDs_EXPERIMENTAL(ExportEntry wwiseBankEntry, strin // } // } //} + + wwiseBankEntry.WriteBinary(wwiserBinary); string bankBinaryAsString = Convert.ToHexString(wwiseBankEntry.GetBinaryData()); @@ -2028,21 +2034,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. @@ -2050,40 +2056,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 { ActionParams: Play playAction }) { - newBankIDArr.CopyTo(bankIDSpan); + if(playAction.BankId == oldBankID) + { + playAction.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 022329d45d..212bb40e8d 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" @@ -562,7 +563,7 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs index acdc2db521..cb01b02fa0 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs @@ -50,6 +50,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..be503a107a 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..c070aefcf6 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Soundplorer/SoundplorerWPF.xaml.cs @@ -32,6 +32,7 @@ using LegendaryExplorerCore.Gammtek.Extensions.Collections.Generic; using LegendaryExplorerCore.Packages.CloningImportingAndRelinking; using LegendaryExplorerCore.Sound.ISACT; +using ME3Tweaks.Wwiser; using AudioStreamHelper = LegendaryExplorer.UnrealExtensions.AudioStreamHelper; using WwiseStream = LegendaryExplorerCore.Unreal.BinaryConverters.WwiseStream; @@ -501,39 +502,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 +1036,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/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml index 7f887370b6..787fbb28bf 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" @@ -156,6 +157,6 @@ - + diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs index f63e8752b2..4ed1f21f09 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..890a0b5d7f 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCDisplayObject.cs @@ -1,14 +1,29 @@ 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 { + private HircItemContainer _item; + + public HircItemContainer Item + { + get => _item; + set => SetProperty(ref _item, value); + } + + private BankSerializationContext _context; + public int Index { get; set; } public byte ObjType { get; set; } @@ -59,7 +74,7 @@ public HIRCDisplayObject Clone() return clone; } - public HIRCDisplayObject(int i, WwiseBankParsed.HIRCObject src, MEGame game) + /*public HIRCDisplayObject(int i, WwiseBankParsed.HIRCObject src, MEGame game) { Data = src.ToBytes(game); Index = i; @@ -69,7 +84,6 @@ public HIRCDisplayObject(int i, WwiseBankParsed.HIRCObject src, MEGame game) { case WwiseBankParsed.SoundSFXVoice sfxVoice: unk1 = sfxVoice.Unk1; - State = (uint)sfxVoice.State; SourceID = sfxVoice.SourceID; AudioID = sfxVoice.AudioID; SoundType = (byte)sfxVoice.SoundType; @@ -78,6 +92,47 @@ public HIRCDisplayObject(int i, WwiseBankParsed.HIRCObject src, MEGame game) EventIDs = eventHIRC.EventActions.Clone(); break; } + }*/ + + public HIRCDisplayObject(int i, HircItemContainer item, byte[] data, BankSerializationContext context) + { + Index = i; + Item = item; + Data = data; + _context = context; + } + + public void SaveData(byte[] toArray) + { + Data = toArray; + DataChanged = true; + } + + public void Commit() + { + if (DataChanged) + { + var serializer = new BinarySerializer(); + using var stream = new MemoryStream(Data); + Item = serializer.Deserialize(stream, _context); + DataChanged = false; + } + } + + 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++) + { + 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..f6d6df8549 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/Soundpanel/HIRCItemEditor.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + - + Visibility="{Binding Converter={StaticResource HIRCObjectTypeVisibilityConverter},ConverterParameter={x:Static wwiserEnums:HircType.Sound}}"> + - + @@ -246,44 +239,7 @@ BorderThickness="0,5" BorderBrush="Transparent" Panel.ZIndex="60"/> - - - - - - - - - - - - -