diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index aeba0307..c189aeff 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -19,6 +19,7 @@ using CUE4Parse.FileProvider.Objects; using CUE4Parse.FileProvider.Vfs; using CUE4Parse.GameTypes.Aion2.Objects; using CUE4Parse.GameTypes.AshEchoes.FileProvider; +using CUE4Parse.GameTypes.DPA.UE4.Assets.Exports.Wwise; using CUE4Parse.GameTypes.KRD.Assets.Exports; using CUE4Parse.MappingsProvider; using CUE4Parse.UE4.AssetRegistry; @@ -1060,6 +1061,13 @@ public class CUE4ParseViewModel : ViewModel TabControl.SelectedTab.AddImage(sourceFile.SubstringAfterLast('/'), false, bitmap, false, updateUi); return false; + } + // The Dark Pictures Anthology: House of Ashes + case UExternalSource when (isNone || saveAudio) && pointer.Object.Value is UExternalSource externalSource: + { + var audioName = Path.GetFileNameWithoutExtension(externalSource.ExternalSourcePath); + SaveAndPlaySound(audioName, "wem", externalSource.Data?.WemFile ?? [], saveAudio); + return false; } case UAkAudioEvent when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEvent audioEvent: { diff --git a/FModel/ViewModels/GameFileViewModel.cs b/FModel/ViewModels/GameFileViewModel.cs index e0d84c7f..bc311e3e 100644 --- a/FModel/ViewModels/GameFileViewModel.cs +++ b/FModel/ViewModels/GameFileViewModel.cs @@ -6,6 +6,7 @@ using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using CUE4Parse.FileProvider.Objects; +using CUE4Parse.GameTypes.DPA.UE4.Assets.Exports.Wwise; using CUE4Parse.GameTypes.FN.Assets.Exports.DataAssets; using CUE4Parse.UE4.Assets; using CUE4Parse.UE4.Assets.Exports.Animation; @@ -200,7 +201,8 @@ public class GameFileViewModel(GameFile asset) : ViewModel UCurveBase => EAssetCategory.CurveBase, UWwiseAssetLibrary or USoundBase or UAkMediaAssetData or UAtomWaveBank or USoundAtomCue - or UAtomCueSheet or USoundAtomCueSheet or UFMODBank or UFMODEvent or UAkAudioType => EAssetCategory.Audio, + or UAtomCueSheet or USoundAtomCueSheet or UFMODBank or UFMODEvent or UAkAudioType + or UExternalSource or UExternalSourceBank => EAssetCategory.Audio, UFileMediaSource => EAssetCategory.Video, UFont or UFontFace => EAssetCategory.Font, diff --git a/FModel/Views/Resources/Controls/ContextMenus/FileContextMenu.xaml b/FModel/Views/Resources/Controls/ContextMenus/FileContextMenu.xaml index 59785879..d8a7ea31 100644 --- a/FModel/Views/Resources/Controls/ContextMenus/FileContextMenu.xaml +++ b/FModel/Views/Resources/Controls/ContextMenus/FileContextMenu.xaml @@ -157,9 +157,10 @@ - + + diff --git a/FModel/Views/Resources/Converters/AnyItemMeetsConditionConverter.cs b/FModel/Views/Resources/Converters/AnyItemMeetsConditionConverter.cs index eaf2de3f..c98977e9 100644 --- a/FModel/Views/Resources/Converters/AnyItemMeetsConditionConverter.cs +++ b/FModel/Views/Resources/Converters/AnyItemMeetsConditionConverter.cs @@ -14,18 +14,36 @@ public class AnyItemMeetsConditionConverter : IValueConverter { public Collection Conditions { get; } = []; + /// + /// Determines how multiple conditions are evaluated. Default is 'And'. + /// + public EConditionMode ConditionMode { get; set; } = EConditionMode.And; + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not IEnumerable items || Conditions.Count == 0) return false; - return items.OfType().Any(item => Conditions.All(c => c.Matches(item))); + Func predicate = ConditionMode switch + { + EConditionMode.And => item => Conditions.All(condition => condition.Matches(item)), + EConditionMode.Or => item => Conditions.Any(condition => condition.Matches(item)), + _ => throw new ArgumentOutOfRangeException() + }; + + return items.OfType().Any(predicate); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } + + public enum EConditionMode + { + And, + Or + } } public interface IItemCondition @@ -39,7 +57,16 @@ public class ItemCategoryCondition : IItemCondition public bool Matches(GameFileViewModel item) { - return item != null && item.AssetCategory.IsOfCategory(Category); + if (item == null) return false; + + // if the specified category is a base category, check if the item's category is derived from it + if (Category.IsBaseCategory()) + { + return item.AssetCategory.IsOfCategory(Category); + } + + // if the specified category is a targeted non-base category, check for exact match + return item.AssetCategory == Category; } }