From 2fe2ee57e319ef84461bcd603b97db9e8ede0cba Mon Sep 17 00:00:00 2001
From: Pear-231 <61670316+Pear-231@users.noreply.github.com>
Date: Sun, 18 Jan 2026 20:47:42 +0000
Subject: [PATCH] Matched Audio Explorer UI to the Audio Editor, made State
Paths hierarchical and improved names and auto expansion
---
.../AudioExplorer/AudioExplorerView.xaml | 103 +++++---
.../AudioExplorer/AudioExplorerView.xaml.cs | 2 +-
.../AudioExplorer/AudioExplorerViewModel.cs | 185 ++++++++++-----
Editors/Audio/AudioExplorer/HircTreeNode.cs | 4 +-
.../HircExploration/HircTreeBaseParser.cs | 4 +-
.../HircExploration/HircTreeChildrenParser.cs | 223 +++++++++++-------
.../HircExploration/HircTreeParentParser.cs | 26 +-
.../SoundParentStructureParser.cs | 26 +-
8 files changed, 359 insertions(+), 214 deletions(-)
diff --git a/Editors/Audio/AudioExplorer/AudioExplorerView.xaml b/Editors/Audio/AudioExplorer/AudioExplorerView.xaml
index 7eaeff587..1c8fefcae 100644
--- a/Editors/Audio/AudioExplorer/AudioExplorerView.xaml
+++ b/Editors/Audio/AudioExplorer/AudioExplorerView.xaml
@@ -6,9 +6,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:s="http://schemas.singulink.com/xaml"
- xmlns:behaviors="clr-namespace:Shared.Ui.Common.Behaviors;assembly=Shared.Ui"
+ xmlns:Behaviors="clr-namespace:Shared.Ui.Common.Behaviors;assembly=Shared.Ui"
xmlns:ValueConverters="clr-namespace:Editors.Audio.Shared.UI.ValueConverters"
- xmlns:audioexplorer="clr-namespace:Editors.Audio.AudioExplorer"
+ xmlns:AudioExplorer="clr-namespace:Editors.Audio.AudioExplorer"
d:DataContext="{d:DesignInstance Type=audioexplorer:AudioExplorerViewModel}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
@@ -26,7 +26,7 @@
+ Width="1*"/>
-
-
@@ -63,6 +53,10 @@
Height="Auto"/>
+
+
@@ -110,6 +104,7 @@
+ Header="Languages">
-
-
+
+
+
@@ -184,11 +180,24 @@
Margin="5, 5, 5, 5"
Content="Play Audio"
Height="25"
- Command="{Binding PlaySelectedSoundActionCommand}"
- IsEnabled="{Binding IsPlaySoundButtonEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
+ Command="{Binding PlayAudioCommand}"
+ IsEnabled="{Binding IsPlayAudioButtonEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
-
+
+
+
+
-
@@ -331,20 +340,44 @@
+
+
+ Grid.Row="1"
+ Grid.Column="2"
+ Background="{DynamicResource TreeView.Static.Background}">
+
+
+
+ Grid.Row="1"
+ Padding="10, 10, 10, 10">
diff --git a/Editors/Audio/AudioExplorer/AudioExplorerView.xaml.cs b/Editors/Audio/AudioExplorer/AudioExplorerView.xaml.cs
index 6dc4a62ec..2a5f32396 100644
--- a/Editors/Audio/AudioExplorer/AudioExplorerView.xaml.cs
+++ b/Editors/Audio/AudioExplorer/AudioExplorerView.xaml.cs
@@ -12,6 +12,6 @@ public AudioExplorerView()
InitializeComponent();
}
- private void OnNodeDoubleClick(object sender, MouseButtonEventArgs e) => ViewModel.PlaySelectedSoundAction();
+ private void OnNodeDoubleClick(object sender, MouseButtonEventArgs e) => ViewModel.PlayAudio();
}
}
diff --git a/Editors/Audio/AudioExplorer/AudioExplorerViewModel.cs b/Editors/Audio/AudioExplorer/AudioExplorerViewModel.cs
index 15266719f..c574c83cc 100644
--- a/Editors/Audio/AudioExplorer/AudioExplorerViewModel.cs
+++ b/Editors/Audio/AudioExplorer/AudioExplorerViewModel.cs
@@ -7,6 +7,7 @@
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
+using Editors.Audio.AudioEditor.Presentation.Shared.Table;
using Editors.Audio.Shared.GameInformation.Warhammer3;
using Editors.Audio.Shared.Storage;
using Editors.Audio.Shared.Utilities;
@@ -15,7 +16,6 @@
using Shared.GameFormats.Wwise.Enums;
using Shared.GameFormats.Wwise.Hirc;
using Shared.GameFormats.Wwise.Hirc.V112;
-using Shared.GameFormats.Wwise.Hirc.V136;
namespace Editors.Audio.AudioExplorer
{
@@ -33,14 +33,15 @@ public partial class AudioExplorerViewModel : ObservableObject, IEditorInterface
[ObservableProperty] private ExplorerListSelectionFilter _explorerFilter;
[ObservableProperty] private ObservableCollection _treeList = [];
[ObservableProperty] private HircTreeNode _selectedNode;
- [ObservableProperty] private string _selectedNodeText = string.Empty;
+ [ObservableProperty] private string _selectedNodeText = string.Empty;
+ [ObservableProperty] private string _wwiseObjectLabel;
[ObservableProperty] private ObservableCollection _languages = [];
[ObservableProperty] private ObservableCollection _selectedLanguages = [];
[ObservableProperty] private bool _searchByActionEvent = false;
[ObservableProperty] private bool _searchByDialogueEvent = true;
[ObservableProperty] private bool _searchByHircId = false;
[ObservableProperty] private bool _searchByVOActor = false;
- [ObservableProperty] private bool _isPlaySoundButtonEnabled = false;
+ [ObservableProperty] private bool _isPlayAudioButtonEnabled = false;
public string DisplayName { get; set; } = "Audio Explorer";
@@ -66,6 +67,8 @@ public AudioExplorerViewModel(IAudioRepository audioRepository, SoundPlayer soun
ExplorerFilter = new ExplorerListSelectionFilter(_audioRepository, SearchByActionEvent, SearchByDialogueEvent, SearchByHircId, SearchByVOActor);
ExplorerFilter.ExplorerList.SelectedItemChanged += OnEventSelected;
+
+ WwiseObjectLabel = "Wwise Object Data";
}
partial void OnSearchByActionEventChanged(bool value)
@@ -126,45 +129,27 @@ partial void OnSearchByVOActorChanged(bool value)
partial void OnSelectedNodeChanged(HircTreeNode value) => OnNodeSelected(value);
- private void OnLanguagesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.NewItems != null)
- {
- foreach (AudioLanguage item in e.NewItems)
- item.PropertyChanged += OnAudioLanguageChanged;
- }
-
- if (e.OldItems != null)
- {
- foreach (AudioLanguage item in e.OldItems)
- item.PropertyChanged -= OnAudioLanguageChanged;
- }
-
- SetSelectedLanguages();
- }
-
- private void OnAudioLanguageChanged(object sender, PropertyChangedEventArgs e)
- {
- if (e.PropertyName == nameof(AudioLanguage.IsChecked))
- SetSelectedLanguages();
- }
-
private void OnNodeSelected(HircTreeNode selectedNode)
{
- IsPlaySoundButtonEnabled = selectedNode?.Item is ICAkSound or ICAkMusicTrack;
+ IsPlayAudioButtonEnabled = selectedNode?.Hirc is ICAkSound or ICAkMusicTrack;
SelectedNodeText = string.Empty;
- if (selectedNode == null || selectedNode.Item == null)
+ if (selectedNode == null || selectedNode.Hirc == null)
return;
+ var nodeName = selectedNode.DisplayName;
+ if (nodeName.Contains("_"))
+ nodeName = TableHelpers.DuplicateUnderscores(nodeName);
+ WwiseObjectLabel = $"Wwise Object Data - {nodeName}";
+
var options = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() }, WriteIndented = true };
- var hircAsString = JsonSerializer.Serialize((object)selectedNode.Item, options);
+ var hircAsString = JsonSerializer.Serialize((object)selectedNode.Hirc, options);
SelectedNodeText = hircAsString;
- if (selectedNode.Item.HircType == AkBkHircType.Sound)
+ if (selectedNode.Hirc.HircType == AkBkHircType.Sound)
{
- var parentStructures = SoundParentStructureParser.Compute(selectedNode.Item, _audioRepository);
+ var parentStructures = SoundParentStructureParser.Compute(selectedNode.Hirc, _audioRepository);
SelectedNodeText += "\n\nParent structure:\n";
foreach (var parentStruct in parentStructures)
@@ -176,20 +161,77 @@ private void OnNodeSelected(HircTreeNode selectedNode)
SelectedNodeText += "\n";
}
}
+
+ ExpandNodes(selectedNode);
+ }
+
+ private static void ExpandNodes(HircTreeNode selectedNode)
+ {
+ // Expand ancestors and collapse siblings at branching levels
+ var currentNode = selectedNode;
+ while (currentNode.Parent != null)
+ {
+ var parentNode = currentNode.Parent;
+
+ parentNode.IsExpanded = true;
+
+ if (parentNode.Children != null && parentNode.Children.Count > 1)
+ {
+ foreach (var siblingNode in parentNode.Children)
+ siblingNode.IsExpanded = false;
+ }
+
+ currentNode.IsExpanded = true;
+ currentNode = parentNode;
+ }
+
+ // Expand where there's only one child
+ currentNode = selectedNode;
+ while (currentNode.Children != null && currentNode.Children.Count == 1)
+ {
+ currentNode.IsExpanded = true;
+ currentNode = currentNode.Children[0];
+ }
+
+ if (currentNode.Children != null && currentNode.Children.Count > 0)
+ currentNode.IsExpanded = true;
+ }
+
+ private void OnLanguagesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.NewItems != null)
+ {
+ foreach (AudioLanguage item in e.NewItems)
+ item.PropertyChanged += OnAudioLanguageChanged;
+ }
+
+ if (e.OldItems != null)
+ {
+ foreach (AudioLanguage item in e.OldItems)
+ item.PropertyChanged -= OnAudioLanguageChanged;
+ }
+
+ SetSelectedLanguages();
+ }
+
+ private void OnAudioLanguageChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(AudioLanguage.IsChecked))
+ SetSelectedLanguages();
}
private void RefreshList() => ExplorerFilter.Refresh(SearchByActionEvent, SearchByDialogueEvent, SearchByHircId, SearchByVOActor);
private void SetSelectedLanguages()
{
- SelectedLanguages = new ObservableCollection(Languages.Where(audioLanguage => audioLanguage.IsChecked).Select(audioLanguage => audioLanguage.Language));
+ SelectedLanguages = new ObservableCollection(Languages
+ .Where(audioLanguage => audioLanguage.IsChecked)
+ .Select(audioLanguage => audioLanguage.Language));
}
[RelayCommand] public void LoadAudioRepositoryForSelectedLanguages()
{
- var languages = SelectedLanguages
- .Select(Wh3LanguageInformation.GetLanguageAsString)
- .ToList();
+ var languages = SelectedLanguages.Select(Wh3LanguageInformation.GetLanguageAsString).ToList();
_audioRepository.Load(languages);
Reset();
}
@@ -199,13 +241,12 @@ private void OnEventSelected(ExplorerListItem newValue)
if (newValue == null)
return;
- if (newValue?.Id == SelectedNode?.Item?.Id)
+ if (newValue?.Id == SelectedNode?.Hirc?.Id)
return;
if (SearchByVOActor)
{
var wwiseTreeParserChildren = new HircTreeChildrenParser(_audioRepository);
- var statePathParser = new StatePathParser(_audioRepository);
SelectedNode = null;
TreeList.Clear();
@@ -214,18 +255,13 @@ private void OnEventSelected(ExplorerListItem newValue)
foreach (var dialogueEvent in dialogueEvents)
{
var dialogueEventRootNode = wwiseTreeParserChildren.BuildHierarchy(dialogueEvent);
- var matchingChildren = dialogueEventRootNode.Children
- .Where(child => child.DisplayName.Contains(newValue.DisplayName))
- .ToList();
-
- if (matchingChildren.Count > 0)
- {
- dialogueEventRootNode.Children = matchingChildren;
+ if (FilterTreeByVOActor(dialogueEventRootNode, newValue.DisplayName))
TreeList.Add(dialogueEventRootNode);
- }
}
+
+ return;
}
- else
+ else
{
var wwiseTreeParserChildren = new HircTreeChildrenParser(_audioRepository);
@@ -233,33 +269,60 @@ private void OnEventSelected(ExplorerListItem newValue)
TreeList.Clear();
var rootNode = wwiseTreeParserChildren.BuildHierarchy(newValue.HircItem);
+ rootNode.IsExpanded = true;
+
TreeList.Add(rootNode);
}
}
- [RelayCommand] public void PlaySelectedSoundAction()
+ private static bool FilterTreeByVOActor(HircTreeNode currentNode, string voActor)
+ {
+ var currentNodeMatches = currentNode.DisplayName.Contains(voActor, StringComparison.OrdinalIgnoreCase);
+ if (currentNodeMatches)
+ return true;
+
+ if (currentNode.Children == null || currentNode.Children.Count == 0)
+ return false;
+
+ var anyMatches = false;
+ for (var i = currentNode.Children.Count - 1; i >= 0; i--)
+ {
+ var childNode = currentNode.Children[i];
+
+ var isMatch = FilterTreeByVOActor(childNode, voActor);
+ if (!isMatch)
+ currentNode.Children.RemoveAt(i);
+ else
+ anyMatches = true;
+ }
+
+ return anyMatches;
+ }
+
+ [RelayCommand] public void PlayAudio()
{
- if (SelectedNode?.Item is ICAkSound sound)
+ // From at least V136 and newer, AkMediaInformation no longer stores and FileOffset. To get the wem data you would search the DidxChunk
+ // for the SourceId. While some Warhammer 3 AkBankSourceData are AKBKSourceType.Data_BNK and therefore should appear in the DidxChunk,
+ // no Warhammer 3 wems are in there and instead all wems are stored in Packs so they're actually AKBKSourceType.Streaming.
+ // This could be explained by Wwiser's Enum for AKBKSourceType in V136 mapping incorrectly, or V136 not supporting data bnks but who knows?
+ // So, as there are no data wems in Warhammer 3, functionality to find wem data in V136 is not implemented as they can only be streamed.
+ if (SelectedNode?.Hirc is ICAkSound sound)
{
- if (sound.GetStreamType() == AKBKSourceType.Data_BNK)
+ if (sound.GetStreamType() == AKBKSourceType.Data_BNK && sound is CAkSound_V112 sound_V112)
{
- if (sound is CAkSound_V136)
- _soundPlayer.PlayStreamedWem(sound.GetSourceId().ToString());
- else if (sound is CAkSound_V112 sound_V112)
- {
- _soundPlayer.PlayDataWem(
- sound_V112.AkBankSourceData.AkMediaInformation.SourceId,
- sound_V112.AkBankSourceData.AkMediaInformation.FileId,
- (int)sound_V112.AkBankSourceData.AkMediaInformation.FileOffset,
- (int)sound_V112.AkBankSourceData.AkMediaInformation.InMemoryMediaSize
- );
- }
+ _soundPlayer.PlayDataWem(
+ sound_V112.AkBankSourceData.AkMediaInformation.SourceId,
+ sound_V112.AkBankSourceData.AkMediaInformation.FileId,
+ (int)sound_V112.AkBankSourceData.AkMediaInformation.FileOffset,
+ (int)sound_V112.AkBankSourceData.AkMediaInformation.InMemoryMediaSize
+ );
}
else
_soundPlayer.PlayStreamedWem(sound.GetSourceId().ToString());
}
- else if (SelectedNode?.Item is ICAkMusicTrack musicTrack)
+ else if (SelectedNode?.Hirc is ICAkMusicTrack musicTrack)
{
+ // There is normally only one MusicTrack?
var musicTrackId = musicTrack.GetChildren().FirstOrDefault();
_soundPlayer.PlayStreamedWem(musicTrackId.ToString());
}
diff --git a/Editors/Audio/AudioExplorer/HircTreeNode.cs b/Editors/Audio/AudioExplorer/HircTreeNode.cs
index ea0074c96..010c078e2 100644
--- a/Editors/Audio/AudioExplorer/HircTreeNode.cs
+++ b/Editors/Audio/AudioExplorer/HircTreeNode.cs
@@ -5,9 +5,9 @@ namespace Editors.Audio.AudioExplorer
{
public class HircTreeNode
{
- public bool IsExpanded { get; set; } = true;
+ public bool IsExpanded { get; set; } = false;
public string DisplayName { get; set; } = string.Empty;
- public HircItem Item { get; set; }
+ public HircItem Hirc { get; set; }
public bool IsMetaNode { get; set; } // things like switch nodes
public List Children { get; set; } = [];
public HircTreeNode Parent { get; set; } = null;
diff --git a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeBaseParser.cs b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeBaseParser.cs
index 259db97bd..d955f55ef 100644
--- a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeBaseParser.cs
+++ b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeBaseParser.cs
@@ -49,7 +49,7 @@ private void ProcessHircObject(HircItem item, HircTreeNode parent)
func(item, parent);
else
{
- var unknownNode = new HircTreeNode() { DisplayName = $"Unknown node type {item.HircType} for ID {item.Id} in {item.BnkFilePath}", Item = item };
+ var unknownNode = new HircTreeNode() { DisplayName = $"Unknown node type {item.HircType} for ID {item.Id} in {item.BnkFilePath}", Hirc = item };
parent.Children.Add(unknownNode);
}
}
@@ -84,7 +84,7 @@ protected virtual string GetDisplayId(uint id, string fileName, bool hidenNameIf
protected static Wanted GetAsType(HircItem instance) where Wanted : class
{
if (instance is not Wanted wanted)
- throw new Exception($"HircItem with Id {instance.Id} is of type {instance.GetType().Name} and cannot be converted to {typeof(Wanted).Name}.");
+ throw new Exception($"Hirc with Id {instance.Id} is of type {instance.GetType().Name} and cannot be converted to {typeof(Wanted).Name}.");
return wanted;
}
}
diff --git a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeChildrenParser.cs b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeChildrenParser.cs
index 9b33e46b8..108bf2e28 100644
--- a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeChildrenParser.cs
+++ b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeChildrenParser.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
using Editors.Audio.AudioExplorer;
using Editors.Audio.Shared.Storage;
using Shared.GameFormats.Wwise.Enums;
@@ -9,61 +10,83 @@ namespace Editors.Audio.Shared.Wwise.HircExploration
{
public class HircTreeChildrenParser : HircTreeBaseParser
{
+ private record ArgumentPathLookupKey(HircTreeNode ParentNode, int Depth, uint State);
+
public HircTreeChildrenParser(IAudioRepository audioRepository) : base(audioRepository)
{
HircProcessChildMap.Add(AkBkHircType.Event, ProcessEvent);
HircProcessChildMap.Add(AkBkHircType.Action, ProcessAction);
- HircProcessChildMap.Add(AkBkHircType.SwitchContainer, ProcessSwitchControl);
- HircProcessChildMap.Add(AkBkHircType.LayerContainer, ProcessLayerContainer);
- HircProcessChildMap.Add(AkBkHircType.RandomSequenceContainer, ProcessSequenceContainer);
+ HircProcessChildMap.Add(AkBkHircType.SwitchContainer, ProcessSwitchContainer);
+ HircProcessChildMap.Add(AkBkHircType.LayerContainer, ProcessBlendContainer);
+ HircProcessChildMap.Add(AkBkHircType.RandomSequenceContainer, ProcessRandomSequenceContainer);
HircProcessChildMap.Add(AkBkHircType.Sound, ProcessSound);
HircProcessChildMap.Add(AkBkHircType.ActorMixer, ProcessActorMixer);
HircProcessChildMap.Add(AkBkHircType.Dialogue_Event, ProcessDialogueEvent);
HircProcessChildMap.Add(AkBkHircType.Music_Track, ProcessMusicTrack);
HircProcessChildMap.Add(AkBkHircType.Music_Segment, ProcessMusicSegment);
- HircProcessChildMap.Add(AkBkHircType.Music_Switch, ProcessMusicSwitch);
- HircProcessChildMap.Add(AkBkHircType.Music_Random_Sequence, ProcessRandMusicContainer);
+ HircProcessChildMap.Add(AkBkHircType.Music_Switch, ProcessMusicSwitchContainer);
+ HircProcessChildMap.Add(AkBkHircType.Music_Random_Sequence, ProcessMusicRandomSequenceContainer);
}
private void ProcessDialogueEvent(HircItem item, HircTreeNode parent)
{
- var hirc = GetAsType(item);
-
+ var dialogueEvent = GetAsType(item);
var statePathParser = new StatePathParser(AudioRepository);
- var result = statePathParser.GetStatePaths(hirc);
-
- var dialogueEventNode = new HircTreeNode() { DisplayName = $"Dialogue Event {AudioRepository.GetNameFromId(item.Id)} - [{result.Header.GetAsString()}]", Item = item };
+ var result = statePathParser.GetStatePaths(dialogueEvent);
+
+ var dialogueEventNode = new HircTreeNode() { DisplayName = $"Dialogue Event - {AudioRepository.GetNameFromId(item.Id)}", Hirc = item };
parent.Children.Add(dialogueEventNode);
- foreach (var path in result.StatePaths)
+ var argumentPathLookup = new Dictionary();
+ foreach (var statePath in result.StatePaths)
{
- var pathNode = new HircTreeNode() { DisplayName = path.GetAsString(), Item = item, IsExpanded = false };
- dialogueEventNode.Children.Add(pathNode);
- ProcessNext(path.ChildNodeId, pathNode);
+ var currentNode = dialogueEventNode;
+
+ for (var depth = 0; depth < statePath.Items.Count; depth++)
+ {
+ var statePathItem = statePath.Items[depth];
+ var lookupKey = new ArgumentPathLookupKey(currentNode, depth, statePathItem.Value);
+ if (!argumentPathLookup.TryGetValue(lookupKey, out var existingNode))
+ {
+ existingNode = new HircTreeNode()
+ {
+ DisplayName = $"State [{result.Header.Items[depth].DisplayName}] - {statePathItem.DisplayName}",
+ Hirc = dialogueEventNode.Hirc,
+ IsMetaNode = true
+ };
+
+ currentNode.Children.Add(existingNode);
+ argumentPathLookup.Add(lookupKey, existingNode);
+ }
+
+ currentNode = existingNode;
+ }
+
+ ProcessNext(statePath.ChildNodeId, currentNode);
}
}
private void ProcessEvent(HircItem item, HircTreeNode parent)
{
- var actionHirc = GetAsType(item);
- var actionTreeNode = new HircTreeNode() { DisplayName = $"Action Event {AudioRepository.GetNameFromId(item.Id)}", Item = item };
- parent.Children.Add(actionTreeNode);
+ var actionEvent = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Action Event - {AudioRepository.GetNameFromId(item.Id)}", Hirc = item };
+ parent.Children.Add(node);
- var actions = actionHirc.GetActionIds();
- ProcessNext(actions, actionTreeNode);
+ var actions = actionEvent.GetActionIds();
+ ProcessNext(actions, node);
}
private void ProcessAction(HircItem item, HircTreeNode parent)
{
- var actionHirc = GetAsType(item);
- var actionTreeNode = new HircTreeNode() { DisplayName = $"Action {actionHirc.GetActionType()}", Item = item };
- parent.Children.Add(actionTreeNode);
- var childId = actionHirc.GetChildId();
+ var action = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"{action.GetActionType()} Action", Hirc = item, IsExpanded = true };
+ parent.Children.Add(node);
+ var childId = action.GetChildId();
// Override child id if type is setState based on parameters
- if (actionHirc.GetActionType() == AkActionType.SetState)
+ if (action.GetActionType() == AkActionType.SetState)
{
- var stateGroupId = actionHirc.GetStateGroupId();
+ var stateGroupId = action.GetStateGroupId();
var musicSwitches = AudioRepository.HircsById
.SelectMany(kvp => kvp.Value)
.Where(hirc => hirc.HircType == AkBkHircType.Music_Switch)
@@ -75,7 +98,7 @@ private void ProcessAction(HircItem item, HircTreeNode parent)
{
var allArgs = musicSwitch.Arguments.Select(x => x.GroupId).ToList();
if (allArgs.Contains(stateGroupId))
- ProcessNext(musicSwitch.Id, actionTreeNode);
+ ProcessNext(musicSwitch.Id, node);
}
var normalSwitches = AudioRepository.HircsById
@@ -86,118 +109,144 @@ private void ProcessAction(HircItem item, HircTreeNode parent)
.ToList();
foreach (var normalSwitch in normalSwitches)
+ {
if (normalSwitch.GroupId == stateGroupId)
- ProcessNext(normalSwitch.Id, actionTreeNode);
+ ProcessNext(normalSwitch.Id, node);
+ }
}
- else ProcessNext(childId, actionTreeNode);
+ else
+ ProcessNext(childId, node);
}
private void ProcessSound(HircItem item, HircTreeNode parent)
{
- var soundHirc = GetAsType(item);
+ var sound = GetAsType(item);
- var displayName = soundHirc.GetStreamType() == AKBKSourceType.Data_BNK
- ? $"Sound {soundHirc.GetSourceId()}.wem (stream type: {soundHirc.GetStreamType()})"
- : $"Sound {soundHirc.GetSourceId()}.wem";
+ var displayName = $"Sound - {sound.GetSourceId()}.wem";
+ if (sound.GetStreamType() == AKBKSourceType.Data_BNK)
+ displayName = $"Sound ({sound.GetStreamType()}) - {sound.GetSourceId()}.wem";
- var soundTreeNode = new HircTreeNode() { DisplayName = displayName, Item = item };
- parent.Children.Add(soundTreeNode);
+ var node = new HircTreeNode() { DisplayName = displayName, Hirc = item };
+ parent.Children.Add(node);
}
- public void ProcessActorMixer(HircItem item, HircTreeNode parent)
+ private void ProcessSwitchContainer(HircItem item, HircTreeNode parent)
{
- var actorMixer = GetAsType(item);
- var actorMixerNode = new HircTreeNode() { DisplayName = $"Actor Mixer {AudioRepository.GetNameFromId(item.Id)}", Item = item };
- parent.Children.Add(actorMixerNode);
+ var switchContainer = GetAsType(item);
+ var switchGroup = AudioRepository.GetNameFromId(switchContainer.GroupId);
- ProcessNext(actorMixer.GetChildren(), actorMixerNode);
- }
+ var defaultSwitchValue = AudioRepository.GetNameFromId(switchContainer.DefaultSwitch);
+ if (defaultSwitchValue == "0")
+ defaultSwitchValue = "Any";
- private void ProcessSwitchControl(HircItem item, HircTreeNode parent)
- {
- var switchControl = GetAsType(item);
- var switchType = AudioRepository.GetNameFromId(switchControl.GroupId);
- var defaultValue = AudioRepository.GetNameFromId(switchControl.DefaultSwitch);
- var switchControlNode = new HircTreeNode() { DisplayName = $"Switch {switchType} Default Value: {defaultValue}", Item = item };
- parent.Children.Add(switchControlNode);
+ var node = new HircTreeNode() { DisplayName = $"Switch Container (Default Value: {defaultSwitchValue})", Hirc = item };
+ parent.Children.Add(node);
- foreach (var switchCase in switchControl.SwitchList)
+ foreach (var switchCase in switchContainer.SwitchList)
{
var switchValue = AudioRepository.GetNameFromId(switchCase.SwitchId);
- var switchValueNode = new HircTreeNode() { DisplayName = $"Switch Value: {switchValue}", Item = item, IsMetaNode = true };
- switchControlNode.Children.Add(switchValueNode);
-
+ var switchValueNode = new HircTreeNode() { DisplayName = $"Switch [{switchGroup}] - {switchValue}", Hirc = item, IsMetaNode = true };
+ node.Children.Add(switchValueNode);
ProcessNext(switchCase.NodeIdList, switchValueNode);
}
}
- private void ProcessLayerContainer(HircItem item, HircTreeNode parent)
+ private void ProcessBlendContainer(HircItem item, HircTreeNode parent)
{
- var layerContainer = GetAsType(item);
- var layerNode = new HircTreeNode() { DisplayName = $"Layer Container", Item = item };
- parent.Children.Add(layerNode);
+ var blendContainer = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Blend Container", Hirc = item };
+ parent.Children.Add(node);
- foreach (var layer in layerContainer.GetChildren())
- ProcessNext(layer, layerNode);
+ foreach (var layer in blendContainer.GetChildren())
+ ProcessNext(layer, node);
}
- private void ProcessSequenceContainer(HircItem item, HircTreeNode parent)
+ private void ProcessRandomSequenceContainer(HircItem item, HircTreeNode parent)
{
- var layerContainer = GetAsType(item);
- var layerNode = new HircTreeNode() { DisplayName = $"Random Sequence Container", Item = item };
- parent.Children.Add(layerNode);
-
- ProcessNext(layerContainer.GetChildren(), layerNode);
+ var randomSequenceContainer = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Random / Sequence Container", Hirc = item, IsExpanded = true };
+ parent.Children.Add(node);
+ ProcessNext(randomSequenceContainer.GetChildren(), node);
}
private void ProcessMusicTrack(HircItem item, HircTreeNode parent)
{
- var musicTrackHirc = GetAsType(item);
-
- foreach (var sourceItem in musicTrackHirc.GetChildren())
+ var musicTrack = GetAsType(item);
+ foreach (var sourceItem in musicTrack.GetChildren())
{
- var musicTrackTreeNode = new HircTreeNode() { DisplayName = $"Music Track {sourceItem}.wem", Item = item };
- parent.Children.Add(musicTrackTreeNode);
+ var node = new HircTreeNode() { DisplayName = $"Music Track - {sourceItem}.wem", Hirc = item };
+ parent.Children.Add(node);
}
}
private void ProcessMusicSegment(HircItem item, HircTreeNode parent)
{
- var hirc = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Music Segment", Item = item };
+ var musicSegment = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Music Segment", Hirc = item };
parent.Children.Add(node);
- foreach (var childId in hirc.MusicNodeParams.Children.ChildIds)
+ foreach (var childId in musicSegment.MusicNodeParams.Children.ChildIds)
ProcessNext(childId, node);
}
- private void ProcessMusicSwitch(HircItem item, HircTreeNode parent)
+ private void ProcessMusicSwitchContainer(HircItem item, HircTreeNode parent)
{
- var hirc = GetAsType(item);
-
+ var musicSwitchContainer = GetAsType(item);
var statePathParser = new StatePathParser(AudioRepository);
- var result = statePathParser.GetStatePaths(hirc);
+ var result = statePathParser.GetStatePaths(musicSwitchContainer);
- var dialogueEventNode = new HircTreeNode() { DisplayName = $"Music Switch {AudioRepository.GetNameFromId(item.Id)} - [{result.Header.GetAsString()}]", Item = item };
- parent.Children.Add(dialogueEventNode);
+ var musicSwitchContainerNode = new HircTreeNode() { DisplayName = $"Music Switch Container", Hirc = item };
+ parent.Children.Add(musicSwitchContainerNode);
- foreach (var path in result.StatePaths)
+ var argumentPathLookup = new Dictionary();
+ foreach (var statePath in result.StatePaths)
{
- var pathNode = new HircTreeNode() { DisplayName = path.GetAsString(), Item = hirc, IsExpanded = false };
- dialogueEventNode.Children.Add(pathNode);
- ProcessNext(path.ChildNodeId, pathNode);
+ var currentNode = musicSwitchContainerNode;
+
+ for (var depth = 0; depth < statePath.Items.Count; depth++)
+ {
+ var statePathItem = statePath.Items[depth];
+ var lookupKey = new ArgumentPathLookupKey(currentNode, depth, statePathItem.Value);
+
+ if (!argumentPathLookup.TryGetValue(lookupKey, out var existingNode))
+ {
+ existingNode = new HircTreeNode()
+ {
+ DisplayName = $"Music Switch [{result.Header.Items[depth].DisplayName}] - {statePathItem.DisplayName}",
+ Hirc = musicSwitchContainerNode.Hirc,
+ IsMetaNode = true
+ };
+
+ currentNode.Children.Add(existingNode);
+ argumentPathLookup.Add(lookupKey, existingNode);
+ }
+
+ currentNode = existingNode;
+ }
+
+ ProcessNext(statePath.ChildNodeId, currentNode);
}
}
- private void ProcessRandMusicContainer(HircItem item, HircTreeNode parent)
+ private void ProcessMusicRandomSequenceContainer(HircItem item, HircTreeNode parent)
{
- var hirc = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Music Random Container", Item = item };
+ var musicRandomSequenceContainer = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Music Random / Sequence Container", Hirc = item, IsExpanded = true };
parent.Children.Add(node);
- if (hirc.PlayList.Count != 0)
- foreach (var playList in hirc.PlayList.First().PlayList)
+ if (musicRandomSequenceContainer.PlayList.Count != 0)
+ {
+ foreach (var playList in musicRandomSequenceContainer.PlayList.First().PlayList)
ProcessNext(playList.SegmentId, node);
+ }
+ }
+
+ public void ProcessActorMixer(HircItem item, HircTreeNode parent)
+ {
+ var actorMixer = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Actor Mixer", Hirc = item };
+ parent.Children.Add(node);
+ ProcessNext(actorMixer.GetChildren(), node);
}
}
}
diff --git a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeParentParser.cs b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeParentParser.cs
index ed205b8e0..714314ceb 100644
--- a/Editors/Audio/Shared/Wwise/HircExploration/HircTreeParentParser.cs
+++ b/Editors/Audio/Shared/Wwise/HircExploration/HircTreeParentParser.cs
@@ -9,8 +9,8 @@ public class HircTreeParentParser : HircTreeBaseParser
{
public HircTreeParentParser(IAudioRepository audioRepository) : base(audioRepository)
{
- HircProcessChildMap.Add(AkBkHircType.SwitchContainer, FindParentSwitchControl);
- HircProcessChildMap.Add(AkBkHircType.LayerContainer, FindParentLayerContainer);
+ HircProcessChildMap.Add(AkBkHircType.SwitchContainer, FindParentSwitchContainer);
+ HircProcessChildMap.Add(AkBkHircType.LayerContainer, FindParentBlendContainer);
HircProcessChildMap.Add(AkBkHircType.RandomSequenceContainer, FindParentRandomSequenceContainer);
HircProcessChildMap.Add(AkBkHircType.Sound, FindParentSound);
HircProcessChildMap.Add(AkBkHircType.ActorMixer, FindParentActorMixer);
@@ -18,18 +18,18 @@ public HircTreeParentParser(IAudioRepository audioRepository) : base(audioReposi
HircProcessChildMap.Add(AkBkHircType.FxShareSet, FindParentFxShareSet);
}
- private void FindParentLayerContainer(HircItem item, HircTreeNode parent)
+ private void FindParentBlendContainer(HircItem item, HircTreeNode parent)
{
- var layerContainer = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Layer Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(layerContainer.GetDirectParentId())}", Item = item };
+ var blendContainer = GetAsType(item);
+ var node = new HircTreeNode() { DisplayName = $"Layer Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(blendContainer.GetDirectParentId())}", Hirc = item };
parent.Children.Add(node);
- ProcessNext(layerContainer.GetDirectParentId(), node);
+ ProcessNext(blendContainer.GetDirectParentId(), node);
}
- private void FindParentSwitchControl(HircItem item, HircTreeNode parent)
+ private void FindParentSwitchContainer(HircItem item, HircTreeNode parent)
{
var switchContainer = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Switch Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(switchContainer.GetDirectParentId())}", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Switch Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(switchContainer.GetDirectParentId())}", Hirc = item };
parent.Children.Add(node);
ProcessNext(switchContainer.GetDirectParentId(), node);
}
@@ -37,7 +37,7 @@ private void FindParentSwitchControl(HircItem item, HircTreeNode parent)
private void FindParentRandomSequenceContainer(HircItem item, HircTreeNode parent)
{
var randomSequenceContainer = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Random Sequence Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(randomSequenceContainer.GetDirectParentId())}", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Random Sequence Container {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(randomSequenceContainer.GetDirectParentId())}", Hirc = item };
parent.Children.Add(node);
ProcessNext(randomSequenceContainer.GetDirectParentId(), node);
}
@@ -45,27 +45,27 @@ private void FindParentRandomSequenceContainer(HircItem item, HircTreeNode paren
private void FindParentActorMixer(HircItem item, HircTreeNode parent)
{
var actorMixer = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Actor Mixer {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(actorMixer.GetDirectParentId())}", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Actor Mixer {GetDisplayId(item.Id, item.BnkFilePath, false)} {GetParentInfo(actorMixer.GetDirectParentId())}", Hirc = item };
parent.Children.Add(node);
ProcessNext(actorMixer.GetDirectParentId(), node);
}
private void FindParentFxShareSet(HircItem item, HircTreeNode parent)
{
- var node = new HircTreeNode() { DisplayName = $"Fx Share Set {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Fx Share Set {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Hirc = item };
parent.Children.Add(node);
}
private void FindParentFxCustom(HircItem item, HircTreeNode parent)
{
- var node = new HircTreeNode() { DisplayName = $"Fx Custom {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Fx Custom {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Hirc = item };
parent.Children.Add(node);
}
private void FindParentSound(HircItem item, HircTreeNode parent)
{
var sound = GetAsType(item);
- var node = new HircTreeNode() { DisplayName = $"Sound {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Item = item };
+ var node = new HircTreeNode() { DisplayName = $"Sound {GetDisplayId(item.Id, item.BnkFilePath, false)} can't have parents", Hirc = item };
parent.Children.Add(node);
ProcessNext(sound.GetDirectParentId(), node);
}
diff --git a/Editors/Audio/Shared/Wwise/HircExploration/SoundParentStructureParser.cs b/Editors/Audio/Shared/Wwise/HircExploration/SoundParentStructureParser.cs
index 66787dd79..93f8c3894 100644
--- a/Editors/Audio/Shared/Wwise/HircExploration/SoundParentStructureParser.cs
+++ b/Editors/Audio/Shared/Wwise/HircExploration/SoundParentStructureParser.cs
@@ -57,37 +57,37 @@ private static ParentStructure GetAudioParentStructure(HircItem sound, IAudioRep
{
var busInfo = "";
- if (node.Item is CAkActorMixer_V136 mixerInstance && mixerInstance.NodeBaseParams.OverrideBusId != 0)
+ if (node.Hirc is CAkActorMixer_V136 mixerInstance && mixerInstance.NodeBaseParams.OverrideBusId != 0)
{
busInfo = $" - With Audio Bus [{mixerInstance.NodeBaseParams.OverrideBusId}]";
- busses.Add(new BusItem() { SourceDescription = $"{node.Item.HircType}[{node.Item.Id}]", BusId = mixerInstance.NodeBaseParams.OverrideBusId });
+ busses.Add(new BusItem() { SourceDescription = $"{node.Hirc.HircType}[{node.Hirc.Id}]", BusId = mixerInstance.NodeBaseParams.OverrideBusId });
}
- else if (node.Item is CAkSound_V136 soundInstance && soundInstance.NodeBaseParams.OverrideBusId != 0)
+ else if (node.Hirc is CAkSound_V136 soundInstance && soundInstance.NodeBaseParams.OverrideBusId != 0)
{
busInfo = $" - With Audio Bus [{soundInstance.NodeBaseParams.OverrideBusId}]";
- busses.Add(new BusItem() { SourceDescription = $"{node.Item.HircType}[{node.Item.Id}]", BusId = soundInstance.NodeBaseParams.OverrideBusId });
+ busses.Add(new BusItem() { SourceDescription = $"{node.Hirc.HircType}[{node.Hirc.Id}]", BusId = soundInstance.NodeBaseParams.OverrideBusId });
}
- else if (node.Item is CAkRanSeqCntr_V136 randInstance && randInstance.NodeBaseParams.OverrideBusId != 0)
+ else if (node.Hirc is CAkRanSeqCntr_V136 randInstance && randInstance.NodeBaseParams.OverrideBusId != 0)
{
busInfo = $" - With Audio Bus [{randInstance.NodeBaseParams.OverrideBusId}]";
- busses.Add(new BusItem() { SourceDescription = $"{node.Item.HircType}[{node.Item.Id}]", BusId = randInstance.NodeBaseParams.OverrideBusId });
+ busses.Add(new BusItem() { SourceDescription = $"{node.Hirc.HircType}[{node.Hirc.Id}]", BusId = randInstance.NodeBaseParams.OverrideBusId });
}
- else if (node.Item is CAkLayerCntr_V136 layerInstance && layerInstance.NodeBaseParams.OverrideBusId != 0)
+ else if (node.Hirc is CAkLayerCntr_V136 layerInstance && layerInstance.NodeBaseParams.OverrideBusId != 0)
{
busInfo = $" - With Audio Bus [{layerInstance.NodeBaseParams.OverrideBusId}]";
- busses.Add(new BusItem() { SourceDescription = $"{node.Item.HircType}[{node.Item.Id}]", BusId = layerInstance.NodeBaseParams.OverrideBusId });
+ busses.Add(new BusItem() { SourceDescription = $"{node.Hirc.HircType}[{node.Hirc.Id}]", BusId = layerInstance.NodeBaseParams.OverrideBusId });
}
- else if (node.Item is CAkSwitchCntr_V136 switchInstance && switchInstance.NodeBaseParams.OverrideBusId != 0)
+ else if (node.Hirc is CAkSwitchCntr_V136 switchInstance && switchInstance.NodeBaseParams.OverrideBusId != 0)
{
busInfo = $" - With Audio Bus [{switchInstance.NodeBaseParams.OverrideBusId}]";
- busses.Add(new BusItem() { SourceDescription = $"{node.Item.HircType}[{node.Item.Id}]", BusId = switchInstance.NodeBaseParams.OverrideBusId });
+ busses.Add(new BusItem() { SourceDescription = $"{node.Hirc.HircType}[{node.Hirc.Id}]", BusId = switchInstance.NodeBaseParams.OverrideBusId });
}
var graphItem = new ParentStructure.GraphItem()
{
- Description = $"{node.Item.HircType}[{node.Item.Id}]{busInfo}",
- Type = node.Item.HircType,
- Id = node.Item.Id,
+ Description = $"{node.Hirc.HircType}[{node.Hirc.Id}]{busInfo}",
+ Type = node.Hirc.HircType,
+ Id = node.Hirc.Id,
};
output.GraphItems.Add(graphItem);
}