diff --git a/CUE4Parse b/CUE4Parse index 94dbeb7c..ab6dff8e 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 94dbeb7cd3232b6719120219ae7d2a63968e9036 +Subproject commit ab6dff8e98e94335916a549810466eb4571a072b diff --git a/FModel/Constants.cs b/FModel/Constants.cs index 48507954..bccfa2aa 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -2,9 +2,8 @@ using System.Diagnostics; using System.IO; using System.Numerics; -using System.Reflection; using CUE4Parse.UE4.Objects.Core.Misc; -using FModel.Extensions; +using CUE4Parse.Utils; namespace FModel; @@ -12,7 +11,7 @@ public static class Constants { public static readonly string APP_PATH = Path.GetFullPath(Environment.GetCommandLineArgs()[0]); public static readonly string APP_VERSION = FileVersionInfo.GetVersionInfo(APP_PATH).FileVersion; - public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion.SubstringAfter('+'); + public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion?.SubstringAfter('+'); public static readonly string APP_SHORT_COMMIT_ID = APP_COMMIT_ID[..7]; public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000"; diff --git a/FModel/Creator/Bases/FN/BaseBundle.cs b/FModel/Creator/Bases/FN/BaseBundle.cs index 9f4df8ce..8437f3f7 100644 --- a/FModel/Creator/Bases/FN/BaseBundle.cs +++ b/FModel/Creator/Bases/FN/BaseBundle.cs @@ -3,7 +3,7 @@ using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.UObject; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.Framework; using SkiaSharp; using SkiaSharp.HarfBuzz; diff --git a/FModel/Creator/Bases/FN/BaseQuest.cs b/FModel/Creator/Bases/FN/BaseQuest.cs index fd0c862b..c6d85cb1 100644 --- a/FModel/Creator/Bases/FN/BaseQuest.cs +++ b/FModel/Creator/Bases/FN/BaseQuest.cs @@ -7,7 +7,7 @@ using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.UObject; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.Framework; using SkiaSharp; using SkiaSharp.HarfBuzz; diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index e79f35bd..32464916 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -67,7 +67,7 @@ public class Typefaces Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream); - switch (viewModel.Provider.InternalGameName.ToUpperInvariant()) + switch (viewModel.Provider.ProjectName.ToUpperInvariant()) { case "FORTNITEGAME": { diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index 9d79ae31..d12814fd 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -12,6 +12,7 @@ using CUE4Parse.UE4.Objects.UObject; using CUE4Parse.UE4.Versions; using CUE4Parse_Conversion.Textures; using CUE4Parse.UE4.Assets.Objects; +using CUE4Parse.Utils; using FModel.Framework; using FModel.Extensions; using FModel.Services; @@ -160,12 +161,7 @@ public static class Utils // fullpath must be either without any extension or with the export objectname public static bool TryLoadObject(string fullPath, out T export) where T : UObject { - return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export); - } - - public static IEnumerable LoadExports(string packagePath) - { - return _applicationView.CUE4Parse.Provider.LoadAllObjects(packagePath); + return _applicationView.CUE4Parse.Provider.TryLoadPackageObject(fullPath, out export); } public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f) @@ -417,4 +413,4 @@ public static class Utils return ret; } -} \ No newline at end of file +} diff --git a/FModel/Extensions/StringExtensions.cs b/FModel/Extensions/StringExtensions.cs index b632865d..07d6873c 100644 --- a/FModel/Extensions/StringExtensions.cs +++ b/FModel/Extensions/StringExtensions.cs @@ -24,76 +24,6 @@ public static class StringExtensions return $"{size:# ###.##} {sizes[order]}".TrimStart(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBefore(this string s, char delimiter) - { - var index = s.IndexOf(delimiter); - return index == -1 ? s : s[..index]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBefore(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal) - { - var index = s.IndexOf(delimiter, comparisonType); - return index == -1 ? s : s[..index]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringAfter(this string s, char delimiter) - { - var index = s.IndexOf(delimiter); - return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringAfter(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal) - { - var index = s.IndexOf(delimiter, comparisonType); - return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBeforeLast(this string s, char delimiter) - { - var index = s.LastIndexOf(delimiter); - return index == -1 ? s : s[..index]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBeforeLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal) - { - var index = s.LastIndexOf(delimiter, comparisonType); - return index == -1 ? s : s[..index]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBeforeWithLast(this string s, char delimiter) - { - var index = s.LastIndexOf(delimiter); - return index == -1 ? s : s[..(index + 1)]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringBeforeWithLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal) - { - var index = s.LastIndexOf(delimiter, comparisonType); - return index == -1 ? s : s[..(index + 1)]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringAfterLast(this string s, char delimiter) - { - var index = s.LastIndexOf(delimiter); - return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SubstringAfterLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal) - { - var index = s.LastIndexOf(delimiter, comparisonType); - return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetNameLineNumber(this string s, string lineToFind) { diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index c92895e8..edcdd5b7 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -6,7 +6,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using AdonisUI.Controls; -using FModel.Extensions; using FModel.Services; using FModel.Settings; using FModel.ViewModels; @@ -267,7 +266,7 @@ public partial class MainWindow return; var filters = textBox.Text.Trim().Split(' '); - folder.AssetsList.AssetsView.Filter = o => { return o is AssetItem assetItem && filters.All(x => assetItem.FullPath.SubstringAfterLast('/').Contains(x, StringComparison.OrdinalIgnoreCase)); }; + folder.AssetsList.AssetsView.Filter = o => { return o is AssetItem assetItem && filters.All(x => assetItem.FileName.Contains(x, StringComparison.OrdinalIgnoreCase)); }; } private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e) diff --git a/FModel/Services/DiscordService.cs b/FModel/Services/DiscordService.cs index 91ae7627..22eabbbf 100644 --- a/FModel/Services/DiscordService.cs +++ b/FModel/Services/DiscordService.cs @@ -48,7 +48,7 @@ namespace FModel.Services public void UpdatePresence(CUE4ParseViewModel viewModel) => UpdatePresence( - $"{viewModel.Provider.GameDisplayName ?? viewModel.Provider.InternalGameName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages", + $"{viewModel.Provider.GameDisplayName ?? viewModel.Provider.ProjectName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages", $"Mode: {UserSettings.Default.LoadingMode.GetDescription()} - {viewModel.SearchVm.ResultsCount:### ### ###} Loaded Assets".Trim()); public void UpdatePresence(string details, string state) diff --git a/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs index 4e06260f..4b151c15 100644 --- a/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.Framework; using FModel.ViewModels.ApiEndpoints.Models; using Newtonsoft.Json.Linq; diff --git a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs index 34fcca98..aeaf42b9 100644 --- a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using AutoUpdaterDotNET; +using CUE4Parse.Utils; using FModel.Extensions; using FModel.Framework; using FModel.Services; diff --git a/FModel/ViewModels/AssetsListViewModel.cs b/FModel/ViewModels/AssetsListViewModel.cs index 5c52448e..cab5ba0f 100644 --- a/FModel/ViewModels/AssetsListViewModel.cs +++ b/FModel/ViewModels/AssetsListViewModel.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.Windows.Data; using CUE4Parse.Compression; +using CUE4Parse.Utils; using FModel.Framework; namespace FModel.ViewModels; @@ -49,7 +50,33 @@ public class AssetItem : ViewModel private set => SetProperty(ref _compression, value); } - public AssetItem(string fullPath, bool isEncrypted, long offset, long size, string archive, CompressionMethod compression) + private string _directory; + public string Directory + { + get => _directory; + private set => SetProperty(ref _directory, value); + } + + private string _fileName; + public string FileName + { + get => _fileName; + private set => SetProperty(ref _fileName, value); + } + + private string _extension; + public string Extension + { + get => _extension; + private set => SetProperty(ref _extension, value); + } + + public AssetItem(string titleExtra, AssetItem asset) : this(asset.FullPath, asset.IsEncrypted, asset.Offset, asset.Size, asset.Archive, asset.Compression) + { + FullPath += titleExtra; + } + + public AssetItem(string fullPath, bool isEncrypted = false, long offset = 0, long size = 0, string archive = "", CompressionMethod compression = CompressionMethod.None) { FullPath = fullPath; IsEncrypted = isEncrypted; @@ -57,6 +84,10 @@ public class AssetItem : ViewModel Size = size; Archive = archive; Compression = compression; + + Directory = FullPath.SubstringBeforeLast('/'); + FileName = FullPath.SubstringAfterLast('/'); + Extension = FullPath.SubstringAfterLast('.').ToLowerInvariant(); } public override string ToString() => FullPath; diff --git a/FModel/ViewModels/AudioPlayerViewModel.cs b/FModel/ViewModels/AudioPlayerViewModel.cs index e6c099db..cf577f52 100644 --- a/FModel/ViewModels/AudioPlayerViewModel.cs +++ b/FModel/ViewModels/AudioPlayerViewModel.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Windows; using System.Windows.Data; using CSCore.CoreAudioAPI; +using CUE4Parse.Utils; using FModel.Extensions; using FModel.Framework; using FModel.Services; diff --git a/FModel/ViewModels/BackupManagerViewModel.cs b/FModel/ViewModels/BackupManagerViewModel.cs index 814258c1..92a15561 100644 --- a/FModel/ViewModels/BackupManagerViewModel.cs +++ b/FModel/ViewModels/BackupManagerViewModel.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using CUE4Parse.FileProvider.Objects; -using CUE4Parse.UE4.VirtualFileSystem; using FModel.Framework; using FModel.Services; using FModel.Settings; diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 0ee16cb5..5eed6bad 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -9,9 +9,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; - using AdonisUI.Controls; - using CUE4Parse.Compression; using CUE4Parse.Encryption.Aes; using CUE4Parse.FileProvider; @@ -37,15 +35,14 @@ using CUE4Parse.UE4.Readers; using CUE4Parse.UE4.Shaders; using CUE4Parse.UE4.Versions; using CUE4Parse.UE4.Wwise; - using CUE4Parse_Conversion; using CUE4Parse_Conversion.Sounds; using CUE4Parse.UE4.Assets; using CUE4Parse.UE4.Objects.UObject; +using CUE4Parse.Utils; using EpicManifestParser; using EpicManifestParser.UE; using EpicManifestParser.ZlibngDotNetDecompressor; - using FModel.Creator; using FModel.Extensions; using FModel.Framework; @@ -54,18 +51,12 @@ using FModel.Settings; using FModel.Views; using FModel.Views.Resources.Controls; using FModel.Views.Snooper; - using Newtonsoft.Json; - using OpenTK.Windowing.Common; using OpenTK.Windowing.Desktop; - using Serilog; - using SkiaSharp; - using UE4Config.Parsing; - using Application = System.Windows.Application; using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid; @@ -78,13 +69,6 @@ public class CUE4ParseViewModel : ViewModel private readonly Regex _fnLiveRegex = new(@"^FortniteGame[/\\]Content[/\\]Paks[/\\]", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - private string _internalGameName; - public string InternalGameName - { - get => _internalGameName; - set => SetProperty(ref _internalGameName, value); - } - private bool _modelIsOverwritingMaterial; public bool ModelIsOverwritingMaterial { @@ -153,20 +137,18 @@ public class CUE4ParseViewModel : ViewModel { case Constants._FN_LIVE_TRIGGER: { - InternalGameName = "FortniteGame"; Provider = new StreamedFileProvider("FortniteLive", true, versionContainer); break; } case Constants._VAL_LIVE_TRIGGER: { - InternalGameName = "ShooterGame"; Provider = new StreamedFileProvider("ValorantLive", true, versionContainer); break; } default: { - InternalGameName = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\"); - Provider = InternalGameName switch + var project = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\"); + Provider = project switch { "StateOfDecay2" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), [ @@ -299,11 +281,10 @@ public class CUE4ParseViewModel : ViewModel { Provider.SubmitKeys(aesKeys); Provider.PostMount(); - InternalGameName = Provider.InternalGameName; var aesMax = Provider.RequiredKeys.Count + Provider.Keys.Count; var archiveMax = Provider.UnloadedVfs.Count + Provider.MountedVfs.Count; - Log.Information($"Project: {InternalGameName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax}"); + Log.Information($"Project: {Provider.ProjectName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax}"); } public void ClearProvider() @@ -337,7 +318,7 @@ public class CUE4ParseViewModel : ViewModel { await _threadWorkerView.Begin(cancellationToken => { - var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.InternalGameName); + var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.ProjectName); if (info == null) return; FLogger.Append(ELog.None, () => @@ -420,7 +401,7 @@ public class CUE4ParseViewModel : ViewModel public Task VerifyOnDemandArchives() { // only local fortnite - if (Provider is not DefaultFileProvider || !Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) + if (Provider is not DefaultFileProvider || !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) return Task.CompletedTask; // scuffed but working @@ -477,7 +458,7 @@ public class CUE4ParseViewModel : ViewModel } private Task LoadHotfixedLocalizedResources() { - if (!Provider.InternalGameName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask; + if (!Provider.ProjectName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask; return Task.Run(() => { var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(default, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); @@ -524,7 +505,7 @@ public class CUE4ParseViewModel : ViewModel { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs); + Extract(cancellationToken, asset, TabControl.HasNoTabs); } } @@ -552,48 +533,44 @@ public class CUE4ParseViewModel : ViewModel Parallel.ForEach(folder.AssetsList.Assets, asset => { cancellationToken.ThrowIfCancellationRequested(); - ExportData(asset.FullPath, false); + ExportData(asset, false); }); foreach (var f in folder.Folders) ExportFolder(cancellationToken, f); } public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs)); public void SaveFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); public void TextureFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto)); public void ModelFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto)); public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto)); + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto)); - public void Extract(CancellationToken cancellationToken, string fullPath, bool addNewTab = false, EBulkType bulk = EBulkType.None) + public void Extract(CancellationToken cancellationToken, AssetItem asset, bool addNewTab = false, EBulkType bulk = EBulkType.None) { - Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", fullPath); + Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", asset.FullPath); - var directory = fullPath.SubstringBeforeLast('/'); - var fileName = fullPath.SubstringAfterLast('/'); - var ext = fullPath.SubstringAfterLast('.').ToLower(); - - if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(fileName, directory); - else TabControl.SelectedTab.SoftReset(fileName, directory); - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext); + if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(asset); + else TabControl.SelectedTab.SoftReset(asset); + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(asset.Extension); var updateUi = !HasFlag(bulk, EBulkType.Auto); var saveProperties = HasFlag(bulk, EBulkType.Properties); var saveTextures = HasFlag(bulk, EBulkType.Textures); - switch (ext) + switch (asset.Extension) { case "uasset": case "umap": { - var pkg = Provider.LoadPackage(fullPath); + var pkg = Provider.LoadPackage(asset.FullPath, asset.Archive); if (saveProperties || updateUi) { TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), saveProperties, updateUi); @@ -637,85 +614,71 @@ public class CUE4ParseViewModel : ViewModel case "po": case "h": { - if (Provider.TrySaveAsset(fullPath, out var data)) - { - using var stream = new MemoryStream(data) { Position = 0 }; - using var reader = new StreamReader(stream); + var data = Provider.SaveAsset(asset.FullPath, asset.Archive); + using var stream = new MemoryStream(data) { Position = 0 }; + using var reader = new StreamReader(stream); - TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); - } + TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); break; } case "locmeta": { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var metadata = new FTextLocalizationMetaDataResource(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var metadata = new FTextLocalizationMetaDataResource(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi); break; } case "locres": { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var locres = new FTextLocalizationResource(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var locres = new FTextLocalizationResource(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi); break; } - case "bin" when fileName.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase): + case "bin" when asset.FileName.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase): { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var registry = new FAssetRegistryState(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var registry = new FAssetRegistryState(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); break; } - case "bin" when fileName.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase): + case "bin" when asset.FileName.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase): { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var registry = new FGlobalShaderCache(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var registry = new FGlobalShaderCache(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); break; } case "bnk": case "pck": { - if (Provider.TryCreateReader(fullPath, out var archive)) + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var wwise = new WwiseReader(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi); + foreach (var (name, data) in wwise.WwiseEncodedMedias) { - var wwise = new WwiseReader(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi); - foreach (var (name, data) in wwise.WwiseEncodedMedias) - { - SaveAndPlaySound(fullPath.SubstringBeforeWithLast("/") + name, "WEM", data); - } + SaveAndPlaySound(asset.FullPath.SubstringBeforeWithLast('/') + name, "WEM", data); } break; } case "wem": { - if (Provider.TrySaveAsset(fullPath, out var input)) - SaveAndPlaySound(fullPath, "WEM", input); + var data = Provider.SaveAsset(asset.FullPath, asset.Archive); + SaveAndPlaySound(asset.FullPath, "WEM", data); break; } case "udic": { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var header = new FOodleDictionaryArchive(archive).Header; - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var header = new FOodleDictionaryArchive(archive).Header; + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi); break; } @@ -723,55 +686,49 @@ public class CUE4ParseViewModel : ViewModel case "jpg": case "bmp": { - if (Provider.TrySaveAsset(fullPath, out var data)) - { - using var stream = new MemoryStream(data) { Position = 0 }; - TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream), saveTextures, updateUi); - } + var data = Provider.SaveAsset(asset.FullPath, asset.Archive); + using var stream = new MemoryStream(data) { Position = 0 }; + TabControl.SelectedTab.AddImage(asset.FileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream), saveTextures, updateUi); break; } case "svg": { - if (Provider.TrySaveAsset(fullPath, out var data)) + var data = Provider.SaveAsset(asset.FullPath, asset.Archive); + using var stream = new MemoryStream(data) { Position = 0 }; + var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512)); + svg.Load(stream); + + var bitmap = new SKBitmap(512, 512); + using (var canvas = new SKCanvas(bitmap)) + using (var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium }) { - using var stream = new MemoryStream(data) { Position = 0 }; - var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512)); - svg.Load(stream); - - var bitmap = new SKBitmap(512, 512); - using (var canvas = new SKCanvas(bitmap)) - using (var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium }) - { - canvas.DrawPicture(svg.Picture, paint); - } - - TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, bitmap, saveTextures, updateUi); + canvas.DrawPicture(svg.Picture, paint); } + TabControl.SelectedTab.AddImage(asset.FileName.SubstringBeforeLast("."), false, bitmap, saveTextures, updateUi); + break; } case "ufont": case "otf": case "ttf": FLogger.Append(ELog.Warning, () => - FLogger.Text($"Export '{fileName}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true)); + FLogger.Text($"Export '{asset.FileName}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true)); break; case "ushaderbytecode": case "ushadercode": { - if (Provider.TryCreateReader(fullPath, out var archive)) - { - var ar = new FShaderCodeArchive(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); - } + var archive = Provider.CreateReader(asset.FullPath, asset.Archive); + var ar = new FShaderCodeArchive(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); break; } default: { FLogger.Append(ELog.Warning, () => - FLogger.Text($"The package '{fileName}' is of an unknown type.", Constants.WHITE, true)); + FLogger.Text($"The package '{asset.FileName}' is of an unknown type.", Constants.WHITE, true)); break; } } @@ -780,7 +737,7 @@ public class CUE4ParseViewModel : ViewModel public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType) { Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath); - TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/'), parentExportType); + TabControl.AddTab(new AssetItem(fullPath), parentExportType); TabControl.SelectedTab.ScrollTrigger = objectName; var pkg = Provider.LoadPackage(fullPath); @@ -840,7 +797,7 @@ public class CUE4ParseViewModel : ViewModel { var fileName = sourceFile.SubstringAfterLast('/'); var path = Path.Combine(UserSettings.Default.TextureDirectory, - UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Directory : "", fileName!).Replace('\\', '/'); + UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Asset.Directory : "", fileName!).Replace('\\', '/'); Directory.CreateDirectory(path.SubstringBeforeLast('/')); @@ -881,7 +838,7 @@ public class CUE4ParseViewModel : ViewModel return false; } - SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data); + SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Asset.FullPath.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data); return false; } case UWorld when isNone && UserSettings.Default.PreviewWorlds: @@ -897,7 +854,7 @@ public class CUE4ParseViewModel : ViewModel case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh: case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial && - !(Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && + !(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && (pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))): @@ -943,15 +900,13 @@ public class CUE4ParseViewModel : ViewModel } } - public void ShowMetadata(string fullPath) + public void ShowMetadata(AssetItem asset) { - var package = Provider.LoadPackage(fullPath); + var package = Provider.LoadPackage(asset.FullPath, asset.Archive); - var directory = fullPath.SubstringBeforeLast('/'); - var fileName = $"{fullPath.SubstringAfterLast('/')} (Metadata)"; - - if (TabControl.CanAddTabs) TabControl.AddTab(fileName, directory); - else TabControl.SelectedTab.SoftReset(fileName, directory); + var a = new AssetItem(" (Metadata)", asset); + if (TabControl.CanAddTabs) TabControl.AddTab(a); + else TabControl.SelectedTab.SoftReset(a); TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(package, Formatting.Indented), false, false); @@ -1008,10 +963,11 @@ public class CUE4ParseViewModel : ViewModel } private readonly object _rawData = new (); - public void ExportData(string fullPath, bool updateUi = true) + public void ExportData(AssetItem asset, bool updateUi = true) { - var fileName = fullPath.SubstringAfterLast('/'); - if (Provider.TrySavePackage(fullPath, out var assets)) + // TODO: export by archive + // is that even useful? if user doesn't rename manually it's gonna overwrite the file anyway + if (Provider.TrySavePackage(asset.FullPath, out var assets)) { string path = UserSettings.Default.RawDataDirectory; Parallel.ForEach(assets, kvp => @@ -1024,21 +980,21 @@ public class CUE4ParseViewModel : ViewModel } }); - Log.Information("{FileName} successfully exported", fileName); + Log.Information("{FileName} successfully exported", asset.FileName); if (updateUi) { FLogger.Append(ELog.Information, () => { FLogger.Text("Successfully exported ", Constants.WHITE); - FLogger.Link(fileName, path, true); + FLogger.Link(asset.FileName, path, true); }); } } else { - Log.Error("{FileName} could not be exported", fileName); + Log.Error("{FileName} could not be exported", asset.FileName); if (updateUi) - FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{fileName}'", Constants.WHITE, true)); + FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{asset.FileName}'", Constants.WHITE, true)); } } diff --git a/FModel/ViewModels/Commands/CopyCommand.cs b/FModel/ViewModels/Commands/CopyCommand.cs index e9131335..a46ce85c 100644 --- a/FModel/ViewModels/Commands/CopyCommand.cs +++ b/FModel/ViewModels/Commands/CopyCommand.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text; using System.Windows; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.Framework; namespace FModel.ViewModels.Commands; @@ -43,4 +43,4 @@ public class CopyCommand : ViewModelCommand Clipboard.SetText(sb.ToString().TrimEnd()); } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/Commands/LoadCommand.cs b/FModel/ViewModels/Commands/LoadCommand.cs index 2bb6a989..304286ff 100644 --- a/FModel/ViewModels/Commands/LoadCommand.cs +++ b/FModel/ViewModels/Commands/LoadCommand.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; using AdonisUI.Controls; using CUE4Parse.UE4.Readers; using CUE4Parse.UE4.VirtualFileSystem; -using FModel.Creator; +using CUE4Parse.Utils; using FModel.Extensions; using FModel.Framework; using FModel.Services; diff --git a/FModel/ViewModels/Commands/MenuCommand.cs b/FModel/ViewModels/Commands/MenuCommand.cs index 14b54904..00e363ba 100644 --- a/FModel/ViewModels/Commands/MenuCommand.cs +++ b/FModel/ViewModels/Commands/MenuCommand.cs @@ -29,7 +29,7 @@ public class MenuCommand : ViewModelCommand Helper.OpenWindow("AES Manager", () => new AesManager().Show()); break; case "Directory_Backup": - Helper.OpenWindow("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.InternalGameName).Show()); + Helper.OpenWindow("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.ProjectName).Show()); break; case "Directory_ArchivesInfo": contextViewModel.CUE4Parse.TabControl.AddTab("Archives Info"); diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index c74b27c9..065790d6 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -32,7 +32,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset, true); } break; case "Assets_Show_Metadata": @@ -40,7 +40,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.ShowMetadata(asset.FullPath); + contextViewModel.CUE4Parse.ShowMetadata(asset); } break; case "Assets_Export_Data": @@ -48,7 +48,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.ExportData(asset.FullPath); + contextViewModel.CUE4Parse.ExportData(asset); } break; case "Assets_Save_Properties": @@ -56,7 +56,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Properties | updateUi); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Properties | updateUi); } break; case "Assets_Save_Textures": @@ -64,7 +64,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Textures | updateUi); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Textures | updateUi); } break; case "Assets_Save_Models": @@ -72,7 +72,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Meshes | updateUi); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Meshes | updateUi); } break; case "Assets_Save_Animations": @@ -80,7 +80,7 @@ public class RightClickMenuCommand : ViewModelCommand { Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Animations | updateUi); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Animations | updateUi); } break; } diff --git a/FModel/ViewModels/Commands/TabCommand.cs b/FModel/ViewModels/Commands/TabCommand.cs index ba9c2fe0..7cc50ad1 100644 --- a/FModel/ViewModels/Commands/TabCommand.cs +++ b/FModel/ViewModels/Commands/TabCommand.cs @@ -32,44 +32,44 @@ public class TabCommand : ViewModelCommand _applicationView.CUE4Parse.TabControl.RemoveOtherTabs(contextViewModel); break; case "Asset_Export_Data": - await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(contextViewModel.FullPath)); + await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(contextViewModel.Asset)); break; case "Asset_Save_Properties": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Properties); + _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Properties); }); break; case "Asset_Save_Textures": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Textures); + _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Textures); }); break; case "Asset_Save_Models": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Meshes | EBulkType.Auto); + _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Meshes); }); break; case "Asset_Save_Animations": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Animations | EBulkType.Auto); + _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Animations); }); break; case "Open_Properties": - if (contextViewModel.Header == "New Tab" || contextViewModel.Document == null) return; - Helper.OpenWindow(contextViewModel.Header + " (Properties)", () => + if (contextViewModel.Asset.FileName == "New Tab" || contextViewModel.Document == null) return; + Helper.OpenWindow(contextViewModel.Asset.FileName + " (Properties)", () => { new PropertiesPopout(contextViewModel) { - Title = contextViewModel.Header + " (Properties)" + Title = contextViewModel.Asset.FileName + " (Properties)" }.Show(); }); break; case "Copy_Asset_Path": - Clipboard.SetText(contextViewModel.FullPath); + Clipboard.SetText(contextViewModel.Asset.FullPath); break; } } diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index b535855d..03804c54 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -1,4 +1,3 @@ -using FModel.Extensions; using FModel.Framework; using Newtonsoft.Json; using Serilog; @@ -10,6 +9,7 @@ using System.Linq; using System.Text.RegularExpressions; using CUE4Parse.UE4.Objects.Core.Serialization; using CUE4Parse.UE4.Versions; +using CUE4Parse.Utils; using FModel.Settings; using FModel.ViewModels.ApiEndpoints.Models; using Microsoft.Win32; diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index a09c1596..e48da67b 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -16,6 +16,7 @@ using System.Windows; using System.Windows.Media.Imaging; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse_Conversion.Textures; +using CUE4Parse.Utils; namespace FModel.ViewModels; @@ -92,22 +93,13 @@ public class TabItem : ViewModel { public string ParentExportType { get; private set; } - private string _header; - public string Header + private AssetItem _asset; + public AssetItem Asset { - get => _header; - set => SetProperty(ref _header, value); + get => _asset; + set => SetProperty(ref _asset, value); } - private string _directory; - public string Directory - { - get => _directory; - set => SetProperty(ref _directory, value); - } - - public string FullPath => this.Directory + "/" + this.Header.SubstringBeforeLast(" ("); - private bool _hasSearchOpen; public bool HasSearchOpen { @@ -217,18 +209,16 @@ public class TabItem : ViewModel private GoToCommand _goToCommand; public GoToCommand GoToCommand => _goToCommand ??= new GoToCommand(null); - public TabItem(string header, string directory, string parentExportType) + public TabItem(AssetItem asset, string parentExportType) { - Header = header; - Directory = directory; + Asset = asset; ParentExportType = parentExportType; _images = new ObservableCollection(); } - public void SoftReset(string header, string directory) + public void SoftReset(AssetItem asset) { - Header = header; - Directory = directory; + Asset = asset; ParentExportType = string.Empty; ScrollTrigger = null; Application.Current.Dispatcher.Invoke(() => @@ -316,9 +306,9 @@ public class TabItem : ViewModel var fileName = image.ExportName + ext; var path = Path.Combine(UserSettings.Default.TextureDirectory, - UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/'); + UserSettings.Default.KeepDirectoryStructure ? Asset.Directory : "", fileName!).Replace('\\', '/'); - System.IO.Directory.CreateDirectory(path.SubstringBeforeLast('/')); + Directory.CreateDirectory(path.SubstringBeforeLast('/')); SaveImage(image, path, fileName, updateUi); } @@ -337,11 +327,11 @@ public class TabItem : ViewModel public void SaveProperty(bool updateUi) { - var fileName = Path.ChangeExtension(Header, ".json"); + var fileName = Path.ChangeExtension(Asset.FileName, ".json"); var directory = Path.Combine(UserSettings.Default.PropertiesDirectory, - UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName).Replace('\\', '/'); + UserSettings.Default.KeepDirectoryStructure ? Asset.Directory : "", fileName).Replace('\\', '/'); - System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/')); + Directory.CreateDirectory(directory.SubstringBeforeLast('/')); Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text)); SaveCheck(directory, fileName, updateUi); @@ -390,28 +380,27 @@ public class TabControlViewModel : ViewModel public TabControlViewModel() { - _tabItems = new ObservableCollection(EnumerateTabs()); + _tabItems = []; TabsItems = new ReadOnlyObservableCollection(_tabItems); - SelectedTab = TabsItems.FirstOrDefault(); + AddTab(); } - public void AddTab(string header = null, string directory = null, string parentExportType = null) + public void AddTab() => AddTab("New Tab"); + public void AddTab(string title) => AddTab(new AssetItem(title)); + public void AddTab(AssetItem asset, string parentExportType = null) { if (!CanAddTabs) return; - var h = header ?? "New Tab"; - var d = directory ?? string.Empty; var p = parentExportType ?? string.Empty; - if (SelectedTab is { Header : "New Tab" }) + if (SelectedTab?.Asset.FileName == "New Tab") { - SelectedTab.Header = h; - SelectedTab.Directory = d; + SelectedTab.Asset = asset; return; } Application.Current.Dispatcher.Invoke(() => { - _tabItems.Add(new TabItem(h, d, p)); + _tabItems.Add(new TabItem(asset, p)); SelectedTab = _tabItems.Last(); }); } @@ -470,9 +459,4 @@ public class TabControlViewModel : ViewModel _tabItems.Clear(); }); } - - private static IEnumerable EnumerateTabs() - { - yield return new TabItem("New Tab", string.Empty, string.Empty); - } } diff --git a/FModel/ViewModels/UpdateViewModel.cs b/FModel/ViewModels/UpdateViewModel.cs index b0727a3b..36849816 100644 --- a/FModel/ViewModels/UpdateViewModel.cs +++ b/FModel/ViewModels/UpdateViewModel.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using System.Windows.Data; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.Framework; using FModel.Services; using FModel.Settings; diff --git a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs index 1e2a344f..86fdb4d4 100644 --- a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs +++ b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.TextFormatting; +using CUE4Parse.Utils; using FModel.Extensions; using FModel.Services; using FModel.ViewModels; diff --git a/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs b/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs index f09fa3f8..d46721fc 100644 --- a/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs +++ b/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs @@ -119,7 +119,7 @@ public partial class AvalonEditor if (sender is not TextEditor avalonEditor || DataContext is not TabItem tabItem || avalonEditor.Document == null || string.IsNullOrEmpty(avalonEditor.Document.Text)) return; - avalonEditor.Document.FileName = tabItem.Directory + '/' + StringExtensions.SubstringBeforeLast(tabItem.Header, '.'); + avalonEditor.Document.FileName = tabItem.Asset.FullPath.SubstringBeforeLast('.'); if (!_savedCarets.ContainsKey(avalonEditor.Document.FileName)) _ignoreCaret = true; diff --git a/FModel/Views/Resources/Controls/PropertiesPopout.xaml.cs b/FModel/Views/Resources/Controls/PropertiesPopout.xaml.cs index 28629812..c2331100 100644 --- a/FModel/Views/Resources/Controls/PropertiesPopout.xaml.cs +++ b/FModel/Views/Resources/Controls/PropertiesPopout.xaml.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using System.Windows.Input; using System.Windows.Media; -using FModel.Extensions; +using CUE4Parse.Utils; using FModel.ViewModels; using ICSharpCode.AvalonEdit; using ICSharpCode.AvalonEdit.Document; @@ -24,7 +24,7 @@ public partial class PropertiesPopout MyAvalonEditor.Document = new TextDocument { Text = contextViewModel.Document.Text, - FileName = contextViewModel.Directory + '/' + contextViewModel.Header.SubstringBeforeLast('.') + FileName = contextViewModel.Asset.FullPath.SubstringBeforeLast('.') }; MyAvalonEditor.FontSize = contextViewModel.FontSize; MyAvalonEditor.SyntaxHighlighting = contextViewModel.Highlighter; diff --git a/FModel/Views/Resources/Converters/FullPathToFileConverter.cs b/FModel/Views/Resources/Converters/FullPathToFileConverter.cs index fe87d462..347ec234 100644 --- a/FModel/Views/Resources/Converters/FullPathToFileConverter.cs +++ b/FModel/Views/Resources/Converters/FullPathToFileConverter.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; using System.Windows.Data; -using FModel.Extensions; +using CUE4Parse.Utils; namespace FModel.Views.Resources.Converters; @@ -18,4 +18,4 @@ public class FullPathToFileConverter : IValueConverter { throw new NotImplementedException(); } -} \ No newline at end of file +} diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 03e061d9..a1a2f8de 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -651,7 +651,7 @@ - + @@ -913,7 +913,7 @@ - + diff --git a/FModel/Views/SearchView.xaml.cs b/FModel/Views/SearchView.xaml.cs index 8fbd6ad7..2be09d65 100644 --- a/FModel/Views/SearchView.xaml.cs +++ b/FModel/Views/SearchView.xaml.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; -using FModel.Extensions; using FModel.Services; using FModel.ViewModels; @@ -36,7 +35,7 @@ public partial class SearchView WindowState = WindowState.Minimized; MainWindow.YesWeCats.AssetsListName.ItemsSource = null; - var folder = _applicationView.CustomDirectories.GoToCommand.JumpTo(assetItem.FullPath.SubstringBeforeLast('/')); + var folder = _applicationView.CustomDirectories.GoToCommand.JumpTo(assetItem.Directory); if (folder == null) return; MainWindow.YesWeCats.Activate(); @@ -58,7 +57,7 @@ public partial class SearchView return; WindowState = WindowState.Minimized; - await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, assetItem.FullPath, true)); + await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, assetItem, true)); MainWindow.YesWeCats.Activate(); } diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index ec457307..9c023e14 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -534,7 +534,7 @@