mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
Merge pull request #602 from GMMan/add-audio-export
Some checks are pending
FModel QA Builder / build (push) Waiting to run
Some checks are pending
FModel QA Builder / build (push) Waiting to run
Add save audio option to context menus
This commit is contained in:
commit
69d83d5257
|
|
@ -114,5 +114,6 @@ public enum EBulkType
|
|||
Textures = 1 << 2,
|
||||
Meshes = 1 << 3,
|
||||
Skeletons = 1 << 4,
|
||||
Animations = 1 << 5
|
||||
Animations = 1 << 5,
|
||||
Audio = 1 << 6
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,9 +103,6 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Auto Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
|
||||
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="Views">
|
||||
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
|
||||
|
|
@ -376,6 +373,15 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Packages Audio" Click="OnFolderAudioClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Favorite Directory" Click="OnFavoriteDirectoryClick">
|
||||
<MenuItem.Icon>
|
||||
|
|
@ -608,6 +614,21 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Audio" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Audio" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Copy">
|
||||
<MenuItem.Icon>
|
||||
|
|
@ -838,19 +859,6 @@
|
|||
</Viewbox>
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Sounds Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="SND" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Margin="10 0 0 0">
|
||||
<TextBlock Text="{Binding LastUpdateCheck, Source={x:Static local:Settings.UserSettings.Default}, Converter={x:Static converters:RelativeDateTimeConverter.Instance}, StringFormat=Last Refresh: {0}}" />
|
||||
</StatusBarItem>
|
||||
|
|
|
|||
|
|
@ -238,6 +238,19 @@ public partial class MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
private async void OnFolderAudioClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is TreeItem folder)
|
||||
{
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.AudioFolder(cancellationToken, folder); });
|
||||
FLogger.Append(ELog.Information, () =>
|
||||
{
|
||||
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
|
||||
FLogger.Link(folder.PathAtThisPoint, UserSettings.Default.AudioDirectory, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFavoriteDirectoryClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AssetsFolderName.SelectedItem is not TreeItem folder) return;
|
||||
|
|
|
|||
|
|
@ -140,13 +140,6 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _lastOpenedSettingTab, value);
|
||||
}
|
||||
|
||||
private bool _isAutoOpenSounds = true;
|
||||
public bool IsAutoOpenSounds
|
||||
{
|
||||
get => _isAutoOpenSounds;
|
||||
set => SetProperty(ref _isAutoOpenSounds, value);
|
||||
}
|
||||
|
||||
private bool _isLoggerExpanded = true;
|
||||
public bool IsLoggerExpanded
|
||||
{
|
||||
|
|
|
|||
|
|
@ -583,6 +583,9 @@ public class CUE4ParseViewModel : ViewModel
|
|||
public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder)
|
||||
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto));
|
||||
|
||||
public void AudioFolder(CancellationToken cancellationToken, TreeItem folder)
|
||||
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto));
|
||||
|
||||
public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None)
|
||||
{
|
||||
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", entry.Path);
|
||||
|
|
@ -594,6 +597,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
var updateUi = !HasFlag(bulk, EBulkType.Auto);
|
||||
var saveProperties = HasFlag(bulk, EBulkType.Properties);
|
||||
var saveTextures = HasFlag(bulk, EBulkType.Textures);
|
||||
var saveAudio = HasFlag(bulk, EBulkType.Audio);
|
||||
switch (entry.Extension)
|
||||
{
|
||||
case "uasset":
|
||||
|
|
@ -740,7 +744,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
var medias = WwiseProvider.ExtractBankSounds(wwise);
|
||||
foreach (var media in medias)
|
||||
{
|
||||
SaveAndPlaySound(media.OutputPath, media.Extension, media.Data);
|
||||
SaveAndPlaySound(media.OutputPath, media.Extension, media.Data, saveAudio);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -755,7 +759,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
// todo: CSCore.MediaFoundation.MediaFoundationException The byte stream type of the given URL is unsupported. case "aif":
|
||||
{
|
||||
var data = Provider.SaveAsset(entry);
|
||||
SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data);
|
||||
SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data, saveAudio);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -870,6 +874,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
var isNone = bulk == EBulkType.None;
|
||||
var updateUi = !HasFlag(bulk, EBulkType.Auto);
|
||||
var saveTextures = HasFlag(bulk, EBulkType.Textures);
|
||||
var saveAudio = HasFlag(bulk, EBulkType.Audio);
|
||||
|
||||
var pointer = new FPackageIndex(pkg, index + 1).ResolvedObject;
|
||||
if (pointer?.Object is null) return false;
|
||||
|
|
@ -940,37 +945,37 @@ public class CUE4ParseViewModel : ViewModel
|
|||
TabControl.SelectedTab.AddImage(sourceFile.SubstringAfterLast('/'), false, bitmap, false, updateUi);
|
||||
return false;
|
||||
}
|
||||
case UAkAudioEvent when isNone && pointer.Object.Value is UAkAudioEvent audioEvent:
|
||||
case UAkAudioEvent when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEvent audioEvent:
|
||||
{
|
||||
var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent);
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data);
|
||||
SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UFMODEvent when isNone && pointer.Object.Value is UFMODEvent fmodEvent:
|
||||
case UFMODEvent when (isNone || saveAudio) && pointer.Object.Value is UFMODEvent fmodEvent:
|
||||
{
|
||||
var extractedSounds = FmodProvider.ExtractEventSounds(fmodEvent);
|
||||
var directory = Path.GetDirectoryName(fmodEvent.Owner?.Name) ?? "/FMOD/Desktop/";
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data);
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UFMODBank when isNone && pointer.Object.Value is UFMODBank fmodBank:
|
||||
case UFMODBank when (isNone || saveAudio) && pointer.Object.Value is UFMODBank fmodBank:
|
||||
{
|
||||
var extractedSounds = FmodProvider.ExtractBankSounds(fmodBank);
|
||||
var directory = Path.GetDirectoryName(fmodBank.Owner?.Name) ?? "/FMOD/Desktop/";
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data);
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UAkMediaAssetData when isNone:
|
||||
case USoundWave when isNone:
|
||||
case UAkMediaAssetData when isNone || saveAudio:
|
||||
case USoundWave when isNone || saveAudio:
|
||||
{
|
||||
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
|
||||
pointer.Object.Value.Decode(shouldDecompress, out var audioFormat, out var data);
|
||||
|
|
@ -981,7 +986,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
return false;
|
||||
}
|
||||
|
||||
SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data);
|
||||
SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data, saveAudio);
|
||||
return false;
|
||||
}
|
||||
case UWorld when isNone && UserSettings.Default.PreviewWorlds:
|
||||
|
|
@ -1102,13 +1107,13 @@ public class CUE4ParseViewModel : ViewModel
|
|||
TabControl.SelectedTab.SetDocumentText(cpp, false, false);
|
||||
}
|
||||
|
||||
private void SaveAndPlaySound(string fullPath, string ext, byte[] data)
|
||||
private void SaveAndPlaySound(string fullPath, string ext, byte[] data, bool isBulk)
|
||||
{
|
||||
if (fullPath.StartsWith('/')) fullPath = fullPath[1..];
|
||||
var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory,
|
||||
UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}";
|
||||
|
||||
if (!UserSettings.Default.IsAutoOpenSounds)
|
||||
if (isBulk)
|
||||
{
|
||||
Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/'));
|
||||
using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,14 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Animations | updateUi);
|
||||
}
|
||||
break;
|
||||
case "Assets_Save_Audio":
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
Thread.Yield();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
contextViewModel.CUE4Parse.Extract(cancellationToken, entry, false, EBulkType.Audio | updateUi);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows;
|
||||
using System.Windows;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
|
|
@ -58,6 +58,12 @@ public class TabCommand : ViewModelCommand<TabItem>
|
|||
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Animations);
|
||||
});
|
||||
break;
|
||||
case "Asset_Save_Audio":
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
_applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Audio);
|
||||
});
|
||||
break;
|
||||
case "Open_Properties":
|
||||
if (tabViewModel.Header == "New Tab" || tabViewModel.Document == null) return;
|
||||
Helper.OpenWindow<AdonisWindow>(tabViewModel.Header + " (Properties)", () =>
|
||||
|
|
|
|||
|
|
@ -909,6 +909,15 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Audio" Command="{Binding TabCommand}" CommandParameter="Asset_Save_Audio">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Open Properties" Command="{Binding TabCommand}" CommandParameter="Open_Properties">
|
||||
<MenuItem.Icon>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
|
||||
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
|
|
@ -254,6 +254,21 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Audio" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Assets_Save_Audio" />
|
||||
<Binding Path="SelectedItems" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource AudioIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Copy">
|
||||
<MenuItem.Icon>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user