From 8e66371fca5f86dee425f7acd54c5f0580162d19 Mon Sep 17 00:00:00 2001 From: LongerWarrior Date: Sun, 22 Mar 2026 18:43:58 +0200 Subject: [PATCH] Update WwiseProvider for FDeferredByteData, counting failed exports Co-authored-by: Masusder <59669685+Masusder@users.noreply.github.com> --- CUE4Parse | 2 +- FModel/Settings/UserSettings.cs | 7 -- FModel/ViewModels/CUE4ParseViewModel.cs | 69 +++++++++++-------- .../Commands/RightClickMenuCommand.cs | 14 +++- FModel/ViewModels/TabControlViewModel.cs | 5 +- FModel/Views/SettingsView.xaml | 10 +-- 6 files changed, 59 insertions(+), 48 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index dc0f8183..df263631 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit dc0f8183f6551f9b5bccc8aecee65807362ef297 +Subproject commit df263631dbb96469f8f0316d74f4904c51f439ed diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index f6a77b19..44104264 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -454,13 +454,6 @@ namespace FModel.Settings set => SetProperty(ref _cameraMode, value); } - private int _wwiseMaxBnkPrefetch; - public int WwiseMaxBnkPrefetch - { - get => _wwiseMaxBnkPrefetch; - set => SetProperty(ref _wwiseMaxBnkPrefetch, value); - } - private int _previewMaxTextureSize = 1024; public int PreviewMaxTextureSize { diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 9e75dc1a..3fa86b0f 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -163,6 +163,7 @@ public class CUE4ParseViewModel : ViewModel public ConcurrentBag UnknownExtensions = []; public int ExportedCount; + public int FailedExportCount; public CUE4ParseViewModel() { @@ -324,7 +325,7 @@ public class CUE4ParseViewModel : ViewModel } Provider.Initialize(); - _wwiseProviderLazy = new Lazy(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory, UserSettings.Default.WwiseMaxBnkPrefetch)); + _wwiseProviderLazy = new Lazy(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory)); _fmodProviderLazy = new Lazy(() => new FModProvider(Provider, UserSettings.Default.GameDirectory)); _criWareProviderLazy = new Lazy(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory)); Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}"); @@ -601,7 +602,7 @@ public class CUE4ParseViewModel : ViewModel } public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder, EBulkType bulk) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, bulk)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, bulk)); public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs)); @@ -807,13 +808,13 @@ public class CUE4ParseViewModel : ViewModel case "pck": { var archive = entry.CreateReader(); - var wwise = new WwiseReader(archive); + var wwise = new WwiseReader(archive, new WwiseGameFileSource(entry)); TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi); var medias = WwiseProvider.ExtractBankSounds(wwise); foreach (var media in medias) { - SaveAndPlaySound(cancellationToken, media.OutputPath, media.Extension, media.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, media.OutputPath, media.Extension, media.Data?.GetData() ?? [], saveAudio, updateUi); } break; @@ -1120,7 +1121,8 @@ public class CUE4ParseViewModel : ViewModel case UExternalSource when (isNone || saveAudio) && pointer.Object.Value is UExternalSource externalSource: { var audioName = Path.GetFileNameWithoutExtension(externalSource.ExternalSourcePath); - SaveAndPlaySound(cancellationToken, audioName, "wem", externalSource.Data?.WemFile ?? [], saveAudio, updateUi); + var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName); + SaveAndPlaySound(cancellationToken, outputPath, "wem", externalSource.Data?.WemFile?.GetData() ?? [], saveAudio, updateUi); return false; } case UAkAudioBank when (isNone || saveAudio) && pointer.Object.Value is UAkAudioBank soundBank: @@ -1128,7 +1130,7 @@ public class CUE4ParseViewModel : ViewModel var extractedSounds = WwiseProvider.ExtractBankSounds(soundBank); foreach (var sound in extractedSounds) { - SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi); } return false; } @@ -1137,7 +1139,7 @@ public class CUE4ParseViewModel : ViewModel var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent); foreach (var sound in extractedSounds) { - SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi); } return false; } @@ -1202,12 +1204,13 @@ public class CUE4ParseViewModel : ViewModel case UAkMediaAsset when (isNone || saveAudio) && pointer.Object.Value is UAkMediaAsset akMediaAsset: { var audioName = akMediaAsset.MediaName ?? akMediaAsset.Name; - if (akMediaAsset.CurrentMediaAssetData?.TryLoad(out var akMediaAssetData) is true) + var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName); + if (akMediaAsset.CurrentMediaAssetData?.ResolvedObject?.Object?.Value is UAkMediaAssetData akMediaAssetData) { var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed; akMediaAssetData.Decode(shouldDecompress, out var audioFormat, out var data); - SaveAndPlaySound(cancellationToken, audioName, audioFormat, data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi); } return false; } @@ -1216,14 +1219,15 @@ public class CUE4ParseViewModel : ViewModel var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed; foreach (var mediaIndex in akAudioEventData.MediaList) { - if (mediaIndex.TryLoad(out var akMediaAsset)) + if (mediaIndex.ResolvedObject?.Object?.Value is UAkMediaAsset akMediaAsset) { - if (akMediaAsset.CurrentMediaAssetData?.TryLoad(out var akMediaAssetData) is true) + if (akMediaAsset.CurrentMediaAssetData?.ResolvedObject?.Object?.Value is UAkMediaAssetData akMediaAssetData) { var audioName = akMediaAsset.MediaName ?? $"{akAudioEventData.Outer.Name} ({akMediaAsset.ID})"; + var outputPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), audioName); akMediaAssetData.Decode(shouldDecompress, out var audioFormat, out var data); - SaveAndPlaySound(cancellationToken, audioName, audioFormat, data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, outputPath, audioFormat, data, saveAudio, updateUi); } } } @@ -1235,7 +1239,7 @@ public class CUE4ParseViewModel : ViewModel var extractedSounds = WwiseProvider.ExtractDialogBorderlands3(dialogPerformanceData); foreach (var sound in extractedSounds) { - SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi); } return false; } @@ -1245,12 +1249,13 @@ public class CUE4ParseViewModel : ViewModel if (Provider.Versions.Game is not EGame.GAME_Borderlands4) return false; + var ownerDirectory = WwiseProvider.GetOwnerDirectory(faceFXAnimSet); foreach (var faceFXAnimData in faceFXAnimSet.FaceFXAnimDataList) { - var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(faceFXAnimData.ID.Name, false); + var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(ownerDirectory, faceFXAnimData.ID.Name, false); foreach (var sound in extractedSounds) { - SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi); } } @@ -1259,12 +1264,13 @@ public class CUE4ParseViewModel : ViewModel // Borderlands 4 case UGbxGraphAsset when (isNone || saveAudio) && pointer.Object.Value is UGbxGraphAsset gbxGraphAsset: { + var ownerDirectory = WwiseProvider.GetOwnerDirectory(gbxGraphAsset); foreach (var (eventName, useSoundTag) in GbxAudioUtil.GetAndClearEvents()) { - var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(eventName, useSoundTag); + var extractedSounds = WwiseProvider.ExtractAudioEventBorderlands4(ownerDirectory, eventName, useSoundTag); foreach (var sound in extractedSounds) { - SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data, saveAudio, updateUi); + SaveAndPlaySound(cancellationToken, sound.OutputPath, sound.Extension, sound.Data?.GetData() ?? [], saveAudio, updateUi); } } @@ -1404,28 +1410,33 @@ public class CUE4ParseViewModel : ViewModel TabControl.SelectedTab.SetDocumentText(cpp, false, false); } - private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool isBulk, bool updateUi) + private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool saveAudio, bool updateUi) { if (fullPath.StartsWith('/')) fullPath = fullPath[1..]; var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory, UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}"; - if (isBulk) + if (saveAudio) { cancellationToken.ThrowIfCancellationRequested(); - Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/')); - using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write); - using (var writer = new BinaryWriter(stream)) - { - writer.Write(data); - writer.Flush(); - } + var directory = Path.GetDirectoryName(savedAudioPath); + Directory.CreateDirectory(directory); bool conversionSuccess = true; if (UserSettings.Default.ConvertAudioOnBulkExport) { - conversionSuccess = AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath); - if (conversionSuccess) savedAudioPath = wavFilePath; + if (AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath)) + savedAudioPath = wavFilePath; + else + { + Interlocked.Increment(ref FailedExportCount); + return; + } + } + else + { + using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write); + stream.Write(data); } Interlocked.Increment(ref ExportedCount); @@ -1475,6 +1486,7 @@ public class CUE4ParseViewModel : ViewModel } else { + Interlocked.Increment(ref FailedExportCount); Log.Error("{FileName} could not be saved", export.Name); FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{export.Name}'", Constants.WHITE, true)); } @@ -1509,6 +1521,7 @@ public class CUE4ParseViewModel : ViewModel } else { + Interlocked.Increment(ref FailedExportCount); Log.Error("{FileName} could not be exported", entry.Name); if (updateUi) FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{entry.Name}'", Constants.WHITE, true)); diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index 9b88f7a8..6731918d 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -74,6 +74,7 @@ public class RightClickMenuCommand : ViewModelCommand }; Interlocked.Exchange(ref contextViewModel.CUE4Parse.ExportedCount, 0); + Interlocked.Exchange(ref contextViewModel.CUE4Parse.FailedExportCount, 0); await _threadWorkerView.Begin(cancellationToken => { if (action is EAction.Show) @@ -167,15 +168,24 @@ public class RightClickMenuCommand : ViewModelCommand FLogger.Link(directory, Path.Exists(path) ? path : basePath, true); }); } - else + else if (contextViewModel.CUE4Parse.FailedExportCount == 0) { // Not an error because folder simply might not contain type of asset user is trying to save FLogger.Append(ELog.Warning, () => { - FLogger.Text($"Failed to export any {fileType} from {directory}", Constants.WHITE, true); + FLogger.Text($"Failed to find any {fileType} in {directory}", Constants.WHITE, true); + }); + } + + if (contextViewModel.CUE4Parse.FailedExportCount > 0) + { + FLogger.Append(ELog.Error, () => + { + FLogger.Text($"Failed to export {contextViewModel.CUE4Parse.FailedExportCount} {fileType} from {directory}", Constants.WHITE, true); }); } Interlocked.Exchange(ref contextViewModel.CUE4Parse.ExportedCount, 0); + Interlocked.Exchange(ref contextViewModel.CUE4Parse.FailedExportCount, 0); } } diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index f03fcae4..748dd1ae 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -376,8 +376,7 @@ public class TabItem : ViewModel public void SaveImage() => SaveImage(SelectedImage, true); private void SaveImage(TabImage image, bool updateUi) { - if (image == null) - return; + if (image is null) return; var path = Path.Combine(UserSettings.Default.TextureDirectory, UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", image.ExportName).Replace('\\', '/'); @@ -394,6 +393,7 @@ public class TabItem : ViewModel private void SaveImage(TabImage image, string path) { + if (image.ImageBuffer is null) return; using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); fs.Write(image.ImageBuffer, 0, image.ImageBuffer.Length); } @@ -427,6 +427,7 @@ public class TabItem : ViewModel } else { + Interlocked.Increment(ref ApplicationService.ApplicationView.CUE4Parse.FailedExportCount); Log.Error("{FileName} could not be saved", fileName); if (updateUi) FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{fileName}'", Constants.WHITE, true)); diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index 4f60506d..658a38bb 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -43,7 +43,6 @@ - @@ -249,19 +248,14 @@ Margin="0 5 0 10" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" /> - - - -