Skip to content

Commit 69d83d5

Browse files
authored
Merge pull request #602 from GMMan/add-audio-export
Add save audio option to context menus
2 parents 6baf1fc + de34f9c commit 69d83d5

File tree

9 files changed

+97
-39
lines changed

9 files changed

+97
-39
lines changed

FModel/Enums.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,6 @@ public enum EBulkType
114114
Textures = 1 << 2,
115115
Meshes = 1 << 3,
116116
Skeletons = 1 << 4,
117-
Animations = 1 << 5
117+
Animations = 1 << 5,
118+
Audio = 1 << 6
118119
}

FModel/MainWindow.xaml

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@
103103
</Viewbox>
104104
</MenuItem.Icon>
105105
</MenuItem>
106-
<Separator />
107-
<MenuItem Header="Auto Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
108-
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" />
109106
</MenuItem>
110107
<MenuItem Header="Views">
111108
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
@@ -376,6 +373,15 @@
376373
</Viewbox>
377374
</MenuItem.Icon>
378375
</MenuItem>
376+
<MenuItem Header="Save Folder's Packages Audio" Click="OnFolderAudioClick">
377+
<MenuItem.Icon>
378+
<Viewbox Width="16" Height="16">
379+
<Canvas Width="24" Height="24">
380+
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
381+
</Canvas>
382+
</Viewbox>
383+
</MenuItem.Icon>
384+
</MenuItem>
379385
<Separator />
380386
<MenuItem Header="Favorite Directory" Click="OnFavoriteDirectoryClick">
381387
<MenuItem.Icon>
@@ -608,6 +614,21 @@
608614
</Viewbox>
609615
</MenuItem.Icon>
610616
</MenuItem>
617+
<MenuItem Header="Save Audio" Command="{Binding DataContext.RightClickMenuCommand}">
618+
<MenuItem.CommandParameter>
619+
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
620+
<Binding Source="Assets_Save_Audio" />
621+
<Binding Path="SelectedItems" />
622+
</MultiBinding>
623+
</MenuItem.CommandParameter>
624+
<MenuItem.Icon>
625+
<Viewbox Width="16" Height="16">
626+
<Canvas Width="24" Height="24">
627+
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
628+
</Canvas>
629+
</Viewbox>
630+
</MenuItem.Icon>
631+
</MenuItem>
611632
<Separator />
612633
<MenuItem Header="Copy">
613634
<MenuItem.Icon>
@@ -838,19 +859,6 @@
838859
</Viewbox>
839860
</StatusBarItem>
840861

841-
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Sounds Enabled">
842-
<StatusBarItem.Style>
843-
<Style TargetType="StatusBarItem">
844-
<Style.Triggers>
845-
<DataTrigger Binding="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" Value="False">
846-
<Setter Property="Visibility" Value="Collapsed" />
847-
</DataTrigger>
848-
</Style.Triggers>
849-
</Style>
850-
</StatusBarItem.Style>
851-
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="SND" />
852-
</StatusBarItem>
853-
854862
<StatusBarItem Margin="10 0 0 0">
855863
<TextBlock Text="{Binding LastUpdateCheck, Source={x:Static local:Settings.UserSettings.Default}, Converter={x:Static converters:RelativeDateTimeConverter.Instance}, StringFormat=Last Refresh: {0}}" />
856864
</StatusBarItem>

FModel/MainWindow.xaml.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,19 @@ private async void OnFolderAnimationClick(object sender, RoutedEventArgs e)
238238
}
239239
}
240240

241+
private async void OnFolderAudioClick(object sender, RoutedEventArgs e)
242+
{
243+
if (AssetsFolderName.SelectedItem is TreeItem folder)
244+
{
245+
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.AudioFolder(cancellationToken, folder); });
246+
FLogger.Append(ELog.Information, () =>
247+
{
248+
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
249+
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.AudioDirectory, true);
250+
});
251+
}
252+
}
253+
241254
private void OnFavoriteDirectoryClick(object sender, RoutedEventArgs e)
242255
{
243256
if (AssetsFolderName.SelectedItem is not TreeItem folder) return;

FModel/Settings/UserSettings.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,6 @@ public int LastOpenedSettingTab
140140
set => SetProperty(ref _lastOpenedSettingTab, value);
141141
}
142142

143-
private bool _isAutoOpenSounds = true;
144-
public bool IsAutoOpenSounds
145-
{
146-
get => _isAutoOpenSounds;
147-
set => SetProperty(ref _isAutoOpenSounds, value);
148-
}
149-
150143
private bool _isLoggerExpanded = true;
151144
public bool IsLoggerExpanded
152145
{

FModel/ViewModels/CUE4ParseViewModel.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ public void ModelFolder(CancellationToken cancellationToken, TreeItem folder)
583583
public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder)
584584
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto));
585585

586+
public void AudioFolder(CancellationToken cancellationToken, TreeItem folder)
587+
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto));
588+
586589
public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None)
587590
{
588591
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", entry.Path);
@@ -594,6 +597,7 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
594597
var updateUi = !HasFlag(bulk, EBulkType.Auto);
595598
var saveProperties = HasFlag(bulk, EBulkType.Properties);
596599
var saveTextures = HasFlag(bulk, EBulkType.Textures);
600+
var saveAudio = HasFlag(bulk, EBulkType.Audio);
597601
switch (entry.Extension)
598602
{
599603
case "uasset":
@@ -740,7 +744,7 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
740744
var medias = WwiseProvider.ExtractBankSounds(wwise);
741745
foreach (var media in medias)
742746
{
743-
SaveAndPlaySound(media.OutputPath, media.Extension, media.Data);
747+
SaveAndPlaySound(media.OutputPath, media.Extension, media.Data, saveAudio);
744748
}
745749

746750
break;
@@ -755,7 +759,7 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
755759
// todo: CSCore.MediaFoundation.MediaFoundationException The byte stream type of the given URL is unsupported. case "aif":
756760
{
757761
var data = Provider.SaveAsset(entry);
758-
SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data);
762+
SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data, saveAudio);
759763

760764
break;
761765
}
@@ -870,6 +874,7 @@ public void ExtractAndScroll(CancellationToken cancellationToken, string fullPat
870874
var isNone = bulk == EBulkType.None;
871875
var updateUi = !HasFlag(bulk, EBulkType.Auto);
872876
var saveTextures = HasFlag(bulk, EBulkType.Textures);
877+
var saveAudio = HasFlag(bulk, EBulkType.Audio);
873878

874879
var pointer = new FPackageIndex(pkg, index + 1).ResolvedObject;
875880
if (pointer?.Object is null) return false;
@@ -940,37 +945,37 @@ public void ExtractAndScroll(CancellationToken cancellationToken, string fullPat
940945
TabControl.SelectedTab.AddImage(sourceFile.SubstringAfterLast('/'), false, bitmap, false, updateUi);
941946
return false;
942947
}
943-
case UAkAudioEvent when isNone && pointer.Object.Value is UAkAudioEvent audioEvent:
948+
case UAkAudioEvent when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEvent audioEvent:
944949
{
945950
var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent);
946951
foreach (var sound in extractedSounds)
947952
{
948-
SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data);
953+
SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data, saveAudio);
949954
}
950955
return false;
951956
}
952-
case UFMODEvent when isNone && pointer.Object.Value is UFMODEvent fmodEvent:
957+
case UFMODEvent when (isNone || saveAudio) && pointer.Object.Value is UFMODEvent fmodEvent:
953958
{
954959
var extractedSounds = FmodProvider.ExtractEventSounds(fmodEvent);
955960
var directory = Path.GetDirectoryName(fmodEvent.Owner?.Name) ?? "/FMOD/Desktop/";
956961
foreach (var sound in extractedSounds)
957962
{
958-
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data);
963+
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
959964
}
960965
return false;
961966
}
962-
case UFMODBank when isNone && pointer.Object.Value is UFMODBank fmodBank:
967+
case UFMODBank when (isNone || saveAudio) && pointer.Object.Value is UFMODBank fmodBank:
963968
{
964969
var extractedSounds = FmodProvider.ExtractBankSounds(fmodBank);
965970
var directory = Path.GetDirectoryName(fmodBank.Owner?.Name) ?? "/FMOD/Desktop/";
966971
foreach (var sound in extractedSounds)
967972
{
968-
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data);
973+
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
969974
}
970975
return false;
971976
}
972-
case UAkMediaAssetData when isNone:
973-
case USoundWave when isNone:
977+
case UAkMediaAssetData when isNone || saveAudio:
978+
case USoundWave when isNone || saveAudio:
974979
{
975980
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
976981
pointer.Object.Value.Decode(shouldDecompress, out var audioFormat, out var data);
@@ -981,7 +986,7 @@ public void ExtractAndScroll(CancellationToken cancellationToken, string fullPat
981986
return false;
982987
}
983988

984-
SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data);
989+
SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data, saveAudio);
985990
return false;
986991
}
987992
case UWorld when isNone && UserSettings.Default.PreviewWorlds:
@@ -1102,13 +1107,13 @@ public void Decompile(GameFile entry)
11021107
TabControl.SelectedTab.SetDocumentText(cpp, false, false);
11031108
}
11041109

1105-
private void SaveAndPlaySound(string fullPath, string ext, byte[] data)
1110+
private void SaveAndPlaySound(string fullPath, string ext, byte[] data, bool isBulk)
11061111
{
11071112
if (fullPath.StartsWith('/')) fullPath = fullPath[1..];
11081113
var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory,
11091114
UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}";
11101115

1111-
if (!UserSettings.Default.IsAutoOpenSounds)
1116+
if (isBulk)
11121117
{
11131118
Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/'));
11141119
using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write);

FModel/ViewModels/Commands/RightClickMenuCommand.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ await _threadWorkerView.Begin(cancellationToken =>
9292
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Animations | updateUi);
9393
}
9494
break;
95+
case "Assets_Save_Audio":
96+
foreach (var entry in entries)
97+
{
98+
Thread.Yield();
99+
cancellationToken.ThrowIfCancellationRequested();
100+
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Audio | updateUi);
101+
}
102+
break;
95103
}
96104
});
97105
}

FModel/ViewModels/Commands/TabCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Windows;
1+
using System.Windows;
22
using AdonisUI.Controls;
33
using FModel.Framework;
44
using FModel.Services;
@@ -58,6 +58,12 @@ await _threadWorkerView.Begin(cancellationToken =>
5858
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Animations);
5959
});
6060
break;
61+
case "Asset_Save_Audio":
62+
await _threadWorkerView.Begin(cancellationToken =>
63+
{
64+
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Audio);
65+
});
66+
break;
6167
case "Open_Properties":
6268
if (tabViewModel.Header == "New Tab" || tabViewModel.Document == null) return;
6369
Helper.OpenWindow<AdonisWindow>(tabViewModel.Header + " (Properties)", () =>

FModel/Views/Resources/Resources.xaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,15 @@
909909
</Viewbox>
910910
</MenuItem.Icon>
911911
</MenuItem>
912+
<MenuItem Header="Save Audio" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Audio">
913+
<MenuItem.Icon>
914+
<Viewbox Width="16" Height="16">
915+
<Canvas Width="24" Height="24">
916+
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
917+
</Canvas>
918+
</Viewbox>
919+
</MenuItem.Icon>
920+
</MenuItem>
912921
<Separator />
913922
<MenuItem Header="Open Properties" Command="{Binding TabCommand}" CommandParameter="Open_Properties">
914923
<MenuItem.Icon>

FModel/Views/SearchView.xaml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
1+
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
@@ -254,6 +254,21 @@
254254
</Viewbox>
255255
</MenuItem.Icon>
256256
</MenuItem>
257+
<MenuItem Header="Save Audio" Command="{Binding DataContext.RightClickMenuCommand}">
258+
<MenuItem.CommandParameter>
259+
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
260+
<Binding Source="Assets_Save_Audio" />
261+
<Binding Path="SelectedItems" />
262+
</MultiBinding>
263+
</MenuItem.CommandParameter>
264+
<MenuItem.Icon>
265+
<Viewbox Width="16" Height="16">
266+
<Canvas Width="24" Height="24">
267+
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
268+
</Canvas>
269+
</Viewbox>
270+
</MenuItem.Icon>
271+
</MenuItem>
257272
<Separator />
258273
<MenuItem Header="Copy">
259274
<MenuItem.Icon>

0 commit comments

Comments
 (0)