diff --git a/Directory.Packages.props b/Directory.Packages.props index 9b380cd..c05694a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,6 +17,8 @@ + + diff --git a/src/G2DataGUI.Common/Data/Audio/AudioFiles.cs b/src/G2DataGUI.Common/Data/Audio/AudioFiles.cs new file mode 100644 index 0000000..e83cf28 --- /dev/null +++ b/src/G2DataGUI.Common/Data/Audio/AudioFiles.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Threading.Tasks; + +namespace G2DataGUI.Common.Data.Audio; + +public class AudioFiles +{ + public static AudioFiles Instance { get; private set; } = new(); + public ObservableCollection DirectoryAudioFiles { get; private set; } = []; + public event EventHandler CollectionRefreshed; + + private AudioFiles() + { + _ = ReadAudioFileSystemStructureAsync(); + } + + public void Reload() { } + + private async Task ReadAudioFileSystemStructureAsync() => + await Task.Run(ReadAudioFileSystemStructure).ConfigureAwait(false); + + private void ReadAudioFileSystemStructure() + { + DirectoryAudioFiles.Clear(); + foreach (var node in RecursiveDirectoryCrawl(Version.Instance.RootContentDirectory)) + { + DirectoryAudioFiles.Add(node); + } + + CollectionRefreshed?.Invoke(this, EventArgs.Empty); + } + + private ObservableCollection RecursiveDirectoryCrawl(string directory) + { + ObservableCollection node = []; + + foreach(string dir in Directory.GetDirectories(directory)) + { + var child = new AudioNode(new DirectoryInfo(dir).Name, dir, false, RecursiveDirectoryCrawl(dir)); + node.Add(child); + } + + foreach(string file in Directory.GetFiles(directory, "*.ogg")) + { + AudioNode child = new(new DirectoryInfo(file).Name, file, true); + node.Add(child); + } + + return node; + } +} diff --git a/src/G2DataGUI.Common/Data/Audio/AudioNode.cs b/src/G2DataGUI.Common/Data/Audio/AudioNode.cs new file mode 100644 index 0000000..ca9977a --- /dev/null +++ b/src/G2DataGUI.Common/Data/Audio/AudioNode.cs @@ -0,0 +1,13 @@ +using System.Collections.ObjectModel; + +namespace G2DataGUI.Common.Data.Audio; + +public class AudioNode(string title, string path, bool isFile, ObservableCollection children) +{ + public ObservableCollection Children { get; } = children; + public string Title { get; } = title; + public string Path { get; } = path; + public bool IsFile { get; } = isFile; + + public AudioNode(string title, string path, bool isFile) : this(title, path, isFile, []) { } +} diff --git a/src/G2DataGUI.Common/Data/DDS/DDSFiles.cs b/src/G2DataGUI.Common/Data/DDS/DDSFiles.cs index 4979e32..e59d47c 100644 --- a/src/G2DataGUI.Common/Data/DDS/DDSFiles.cs +++ b/src/G2DataGUI.Common/Data/DDS/DDSFiles.cs @@ -8,7 +8,7 @@ namespace G2DataGUI.Common.Data.DDS; public class DDSFiles { public static DDSFiles Instance { get; private set; } = new(); - public ObservableCollection DirectoryDDSFiles { get; private set; } = new(); + public ObservableCollection DirectoryDDSFiles { get; private set; } = []; public event EventHandler CollectionRefreshed; private DDSFiles() @@ -19,7 +19,7 @@ private DDSFiles() public void Reload() => _ = ReadDDSFileSystemStructureAsync(); private async Task ReadDDSFileSystemStructureAsync() => - await Task.Run(() => ReadDDSFileSystemStructure()).ConfigureAwait(false); + await Task.Run(ReadDDSFileSystemStructure).ConfigureAwait(false); private void ReadDDSFileSystemStructure() { diff --git a/src/G2DataGUI.Common/Data/DDS/DDSNode.cs b/src/G2DataGUI.Common/Data/DDS/DDSNode.cs index fb7933b..174ad75 100644 --- a/src/G2DataGUI.Common/Data/DDS/DDSNode.cs +++ b/src/G2DataGUI.Common/Data/DDS/DDSNode.cs @@ -2,24 +2,15 @@ namespace G2DataGUI.Common.Data.DDS; -public class DDSNode +public class DDSNode(string title, string path, uint guid, bool isFile, ObservableCollection children) { - public ObservableCollection Children { get; } - public string Title { get; } - public string Path { get; } - public bool IsFile { get; } - public uint Guid { get; } + public ObservableCollection Children { get; } = children; + public string Title { get; } = title; + public string Path { get; } = path; + public bool IsFile { get; } = isFile; + public uint Guid { get; } = guid; - public DDSNode(string title, string path, uint guid, bool isFile) : this(title, path, guid, isFile, new ObservableCollection()) { } - - public DDSNode(string title, string path, uint guid, bool isFile, ObservableCollection children) - { - Title = title; - Path = path; - Guid = guid; - IsFile = isFile; - Children = children; - } + public DDSNode(string title, string path, uint guid, bool isFile) : this(title, path, guid, isFile, []) { } public void AddChild(DDSNode child) => Children.Add(child); } diff --git a/src/G2DataGUI.Common/Version.cs b/src/G2DataGUI.Common/Version.cs index daa8c58..6b80223 100644 --- a/src/G2DataGUI.Common/Version.cs +++ b/src/G2DataGUI.Common/Version.cs @@ -15,9 +15,15 @@ private Version() } } + public string RootContentDirectory => + IsHDVersion ? ".\\content\\data" : ".\\data"; + public string RootDataDirectory => IsHDVersion ? ".\\content\\data\\afs\\" : ".\\data\\afs\\"; public string RootTextDirectory => IsHDVersion ? ".\\content\\data\\text" : ".\\data\\text"; + + public string RootAudioDirectory => + IsHDVersion ? ".\\content\\data\\sound" : ".\\data\\sound"; } diff --git a/src/G2DataGUI/Events/AudioEventArgs.cs b/src/G2DataGUI/Events/AudioEventArgs.cs new file mode 100644 index 0000000..9c8d3bf --- /dev/null +++ b/src/G2DataGUI/Events/AudioEventArgs.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace G2DataGUI.Events; + +public class AudioEventArgs() : EventArgs +{ +} diff --git a/src/G2DataGUI/Events/ImageEventArgs.cs b/src/G2DataGUI/Events/ImageEventArgs.cs index ab92e5c..4b68450 100644 --- a/src/G2DataGUI/Events/ImageEventArgs.cs +++ b/src/G2DataGUI/Events/ImageEventArgs.cs @@ -3,12 +3,7 @@ namespace G2DataGUI.Events; -public class ImageEventArgs : EventArgs +public class ImageEventArgs(IImage image) : EventArgs { - public ImageEventArgs(IImage image) - { - Image = image; - } - - public IImage Image { get; } + public IImage Image { get; } = image; } diff --git a/src/G2DataGUI/G2DataGUI.csproj b/src/G2DataGUI/G2DataGUI.csproj index 4681407..dd3ae2a 100644 --- a/src/G2DataGUI/G2DataGUI.csproj +++ b/src/G2DataGUI/G2DataGUI.csproj @@ -28,6 +28,9 @@ + + + diff --git a/src/G2DataGUI/UI/ViewModels/AudioViewerViewModel.cs b/src/G2DataGUI/UI/ViewModels/AudioViewerViewModel.cs new file mode 100644 index 0000000..b13863a --- /dev/null +++ b/src/G2DataGUI/UI/ViewModels/AudioViewerViewModel.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.ObjectModel; +using G2DataGUI.Common.Data.Audio; +using G2DataGUI.UI.Common.ViewModels; +using NAudio.Vorbis; +using NAudio.CoreAudioApi; +using NAudio.Wave; + +namespace G2DataGUI.UI.ViewModels; + +public class AudioViewerViewModel : BaseViewModel, IDisposable +{ + public ObservableCollection GameDirectoryAudioFiles { get; } = AudioFiles.Instance.DirectoryAudioFiles; + private AudioNode _selectedAudioFile = null; + private VorbisWaveReader _waveReader = null; + private WasapiOut _wasapiOut = null; + public int TreeViewWidth { get; } = 200; + + public static AudioViewerViewModel Instance { get; private set; } = new(); + + private AudioViewerViewModel() { } + + private MMDevice GetDefaultAudioDevice() + { + using var enumerator = new MMDeviceEnumerator(); + return enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console); + } + + private void AudioFileCollectionRefreshed(object sender, EventArgs args) { } + + private void LoadAudioFile() + { + StopPlayback(); + + if (SelectedAudioFile != null) + { + _waveReader = new VorbisWaveReader(SelectedAudioFile.Path); + _wasapiOut = new(GetDefaultAudioDevice(), AudioClientShareMode.Shared, false, 10); + _wasapiOut.Init(_waveReader); + _wasapiOut.Play(); + } + } + + private void StopPlayback() + { + if (_wasapiOut != null && _wasapiOut.PlaybackState == PlaybackState.Playing) + { + _wasapiOut.Stop(); + _wasapiOut?.Dispose(); + _wasapiOut = null; + } + + if (_waveReader != null) + { + _waveReader?.Dispose(); + _waveReader = null; + } + } + + public void Dispose() => StopPlayback(); + + public AudioNode SelectedAudioFile + { + get => _selectedAudioFile; + set + { + if (value.IsFile) + { + _selectedAudioFile = value; + LoadAudioFile(); + OnPropertyChanged(nameof(SelectedAudioFile)); + } + } + } +} diff --git a/src/G2DataGUI/UI/ViewModels/AudioViewerWindowViewModel.cs b/src/G2DataGUI/UI/ViewModels/AudioViewerWindowViewModel.cs new file mode 100644 index 0000000..d8ba542 --- /dev/null +++ b/src/G2DataGUI/UI/ViewModels/AudioViewerWindowViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using G2DataGUI.Events; +using G2DataGUI.UI.Common.ViewModels; + +namespace G2DataGUI.UI.ViewModels; + +public class AudioViewerWindowViewModel : BaseViewModel +{ + public static AudioViewerWindowViewModel Instance { get; private set; } = new(); + + private AudioViewerWindowViewModel() + { + + } + + public void OnAudioChange(object sender, AudioEventArgs e) { } +} diff --git a/src/G2DataGUI/UI/ViewModels/MenuBarViewModel.cs b/src/G2DataGUI/UI/ViewModels/MenuBarViewModel.cs index 45020d4..1f8cce7 100644 --- a/src/G2DataGUI/UI/ViewModels/MenuBarViewModel.cs +++ b/src/G2DataGUI/UI/ViewModels/MenuBarViewModel.cs @@ -105,4 +105,10 @@ public void OpenDDSViewer() DDSViewerWindow ddsViewerWindow = new(); ddsViewerWindow.Show(); } + + public void OpenAudioViewer() + { + AudioViewerWindow audioViewerWindow = new(); + audioViewerWindow.Show(); + } } diff --git a/src/G2DataGUI/UI/Views/AudioViewerView.axaml b/src/G2DataGUI/UI/Views/AudioViewerView.axaml new file mode 100644 index 0000000..cad9fa4 --- /dev/null +++ b/src/G2DataGUI/UI/Views/AudioViewerView.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/src/G2DataGUI/UI/Views/AudioViewerView.axaml.cs b/src/G2DataGUI/UI/Views/AudioViewerView.axaml.cs new file mode 100644 index 0000000..0f38cff --- /dev/null +++ b/src/G2DataGUI/UI/Views/AudioViewerView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using G2DataGUI.UI.ViewModels; + +namespace G2DataGUI.UI.Views; + +public partial class AudioViewerView : UserControl +{ + public AudioViewerView() + { + DataContext = AudioViewerViewModel.Instance; + InitializeComponent(); + } +} diff --git a/src/G2DataGUI/UI/Views/MenuBar.axaml b/src/G2DataGUI/UI/Views/MenuBar.axaml index 9a4db41..fd07a44 100644 --- a/src/G2DataGUI/UI/Views/MenuBar.axaml +++ b/src/G2DataGUI/UI/Views/MenuBar.axaml @@ -43,7 +43,10 @@ Header="Windows"> + Header="DDS Viewer" /> + + + diff --git a/src/G2DataGUI/UI/Windows/AudioViewerWindow.axaml.cs b/src/G2DataGUI/UI/Windows/AudioViewerWindow.axaml.cs new file mode 100644 index 0000000..1e77c41 --- /dev/null +++ b/src/G2DataGUI/UI/Windows/AudioViewerWindow.axaml.cs @@ -0,0 +1,17 @@ +using Avalonia; +using Avalonia.Controls; +using G2DataGUI.UI.ViewModels; + +namespace G2DataGUI.UI.Windows; + +public partial class AudioViewerWindow : Window +{ + public AudioViewerWindow() + { + DataContext = AudioViewerWindowViewModel.Instance; + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } +} diff --git a/src/G2DataGUI/UI/Windows/DDSViewerWindow.axaml b/src/G2DataGUI/UI/Windows/DDSViewerWindow.axaml index f397710..1e5b611 100644 --- a/src/G2DataGUI/UI/Windows/DDSViewerWindow.axaml +++ b/src/G2DataGUI/UI/Windows/DDSViewerWindow.axaml @@ -10,4 +10,4 @@ Title="DDS Viewer" mc:Ignorable="d"> - \ No newline at end of file +