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
+