diff --git a/CUE4Parse b/CUE4Parse index 4a3efdaf..ba77931b 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 4a3efdafa7321e98d0ca1c29d3c3d2140980ab9f +Subproject commit ba77931bb2ff50610a05fbabb30180c80f8ab06b diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index ff782d64..3abdaba7 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -14,10 +14,18 @@ Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}"> diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 12e09244..03a5fd18 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -63,7 +63,7 @@ public partial class MainWindow await _applicationView.CUE4Parse.Initialize(); await _applicationView.AesManager.InitAes(); - await _applicationView.AesManager.UpdateProvider(true); + await _applicationView.UpdateProvider(true); #if !DEBUG await _applicationView.CUE4Parse.InitInformation(); #endif @@ -73,15 +73,12 @@ public partial class MainWindow await _applicationView.InitOodle(); if (UserSettings.Default.DiscordRpc == EDiscordRpc.Always) - _discordHandler.Initialize(_applicationView.CUE4Parse.Provider.GameName); + _discordHandler.Initialize(_applicationView.GameDisplayName); #if DEBUG - await _threadWorkerView.Begin(cancellationToken => - _applicationView.CUE4Parse.Extract(cancellationToken, - "ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Models/AB_BountyHunter_S0_4_TrailCreature_Skelmesh.uasset")); - await _threadWorkerView.Begin(cancellationToken => - _applicationView.CUE4Parse.Extract(cancellationToken, - "ShooterGame/Content/Characters/BountyHunter/S0/Ability_4/1P/Anims/FP_BountyHunter_S0_4_Aim_S.uasset")); + // await _threadWorkerView.Begin(cancellationToken => + // _applicationView.CUE4Parse.Extract(cancellationToken, + // "Discovery/Content/Discovery/Items/Charms/Charm_Skateboard_01/SM_Charm_Skateboard_01_A.uasset")); #endif } diff --git a/FModel/Services/DiscordService.cs b/FModel/Services/DiscordService.cs index 2d8712bf..822afd53 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.GameName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages", + $"{viewModel.Provider.GameDisplayName} - {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/AesManagerViewModel.cs b/FModel/ViewModels/AesManagerViewModel.cs index 82b37e18..19dc8258 100644 --- a/FModel/ViewModels/AesManagerViewModel.cs +++ b/FModel/ViewModels/AesManagerViewModel.cs @@ -103,17 +103,11 @@ public class AesManagerViewModel : ViewModel } } - public async Task UpdateProvider(bool isLaunch) + public void SetAesKeys() { - if (!isLaunch && !HasChange) return; - - _cue4Parse.ClearProvider(); - await _cue4Parse.LoadVfs(AesKeys); - if (_cue4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.ContainsKey(UserSettings.Default.GameDirectory)) UserSettings.Default.ManualGames[UserSettings.Default.GameDirectory].AesKeys = _keysFromSettings; else UserSettings.Default.AesKeys[_cue4Parse.Game] = _keysFromSettings; - Log.Information("{@Json}", UserSettings.Default); } diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index f9de0272..b922da9d 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -25,7 +25,7 @@ public class ApplicationViewModel : ViewModel public EBuildKind Build { get => _build; - private set + private init { SetProperty(ref _build, value); RaisePropertyChanged(nameof(TitleExtra)); @@ -36,7 +36,7 @@ public class ApplicationViewModel : ViewModel public FStatus Status { get => _status; - set => SetProperty(ref _status, value); + private init => SetProperty(ref _status, value); } public RightClickMenuCommand RightClickMenuCommand => _rightClickMenuCommand ??= new RightClickMenuCommand(this); @@ -46,9 +46,10 @@ public class ApplicationViewModel : ViewModel public CopyCommand CopyCommand => _copyCommand ??= new CopyCommand(this); private CopyCommand _copyCommand; + public string InitialWindowTitle => $"FModel {UserSettings.Default.UpdateMode}"; + public string GameDisplayName => CUE4Parse.Provider.GameDisplayName ?? "Unknown"; public string TitleExtra => - $"{UserSettings.Default.UpdateMode} - {CUE4Parse.Game.GetDescription()} (" + // FModel {UpdateMode} - {FGame} ({UE}) ({Build}) - $"{(CUE4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.TryGetValue(UserSettings.Default.GameDirectory, out var settings) ? settings.OverridedGame : UserSettings.Default.OverridedGame[CUE4Parse.Game])})" + + $"({(CUE4Parse.Game == FGame.Unknown && UserSettings.Default.ManualGames.TryGetValue(UserSettings.Default.GameDirectory, out var settings) ? settings.OverridedGame : UserSettings.Default.OverridedGame[CUE4Parse.Game])})" + $"{(Build != EBuildKind.Release ? $" ({Build})" : "")}"; public LoadingModesViewModel LoadingModes { get; } @@ -79,6 +80,7 @@ public class ApplicationViewModel : ViewModel //A hard exit is preferable to an unhandled expection in this case Environment.Exit(0); } + CUE4Parse = new CUE4ParseViewModel(UserSettings.Default.GameDirectory); CustomDirectories = new CustomDirectoriesViewModel(CUE4Parse.Game, UserSettings.Default.GameDirectory); SettingsView = new SettingsViewModel(CUE4Parse.Game); @@ -103,6 +105,21 @@ public class ApplicationViewModel : ViewModel RestartWithWarning(); } + public async Task UpdateProvider(bool isLaunch) + { + if (!isLaunch && !AesManager.HasChange) return; + + CUE4Parse.ClearProvider(); + await ApplicationService.ThreadWorkerView.Begin(cancellationToken => + { + CUE4Parse.LoadVfs(cancellationToken, AesManager.AesKeys); + CUE4Parse.Provider.LoadIniConfigs(); + // ConsoleVariables - a.StripAdditiveRefPose=1 + AesManager.SetAesKeys(); + }); + RaisePropertyChanged(nameof(GameDisplayName)); + } + public void RestartWithWarning() { MessageBox.Show("It looks like you just changed something.\nFModel will restart to apply your changes.", "Uh oh, a restart is needed", MessageBoxButton.OK, MessageBoxImage.Warning); diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 3121471c..64e7edeb 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -298,41 +298,38 @@ public class CUE4ParseViewModel : ViewModel /// load virtual files system from GameDirectory /// /// - public async Task LoadVfs(IEnumerable aesKeys) + public void LoadVfs(CancellationToken token, IEnumerable aesKeys) { - await _threadWorkerView.Begin(cancellationToken => + GameDirectory.DeactivateAll(); + + // load files using UnloadedVfs to include non-encrypted vfs + foreach (var key in aesKeys) { - GameDirectory.DeactivateAll(); + token.ThrowIfCancellationRequested(); // cancel if needed - // load files using UnloadedVfs to include non-encrypted vfs - foreach (var key in aesKeys) + var k = key.Key.Trim(); + if (k.Length != 66) k = Constants.ZERO_64_CHAR; + Provider.SubmitKey(key.Guid, new FAesKey(k)); + } + + // files in MountedVfs will be enabled + foreach (var file in GameDirectory.DirectoryFiles) + { + token.ThrowIfCancellationRequested(); + if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs) { - cancellationToken.ThrowIfCancellationRequested(); // cancel if needed + if (Provider.UnloadedVfs.FirstOrDefault(x => x.Name == file.Name) is IoStoreReader store) + file.FileCount = (int) store.Info.TocEntryCount - 1; - var k = key.Key.Trim(); - if (k.Length != 66) k = Constants.ZERO_64_CHAR; - Provider.SubmitKey(key.Guid, new FAesKey(k)); + continue; } - // files in MountedVfs will be enabled - foreach (var file in GameDirectory.DirectoryFiles) - { - cancellationToken.ThrowIfCancellationRequested(); - if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs) - { - if (Provider.UnloadedVfs.FirstOrDefault(x => x.Name == file.Name) is IoStoreReader store) - file.FileCount = (int) store.Info.TocEntryCount - 1; + file.IsEnabled = true; + file.MountPoint = vfs.MountPoint; + file.FileCount = vfs.FileCount; + } - continue; - } - - file.IsEnabled = true; - file.MountPoint = vfs.MountPoint; - file.FileCount = vfs.FileCount; - } - - Game = Helper.IAmThePanda(Provider.GameName) ? FGame.PandaGame : Provider.GameName.ToEnum(Game); - }); + Game = Helper.IAmThePanda(Provider.GameName) ? FGame.PandaGame : Provider.GameName.ToEnum(Game); } public void ClearProvider() diff --git a/FModel/ViewModels/Commands/MenuCommand.cs b/FModel/ViewModels/Commands/MenuCommand.cs index ca4e84e7..b33e534e 100644 --- a/FModel/ViewModels/Commands/MenuCommand.cs +++ b/FModel/ViewModels/Commands/MenuCommand.cs @@ -96,28 +96,26 @@ public class MenuCommand : ViewModelCommand } } - private static void SetFoldersIsExpanded(AssetsFolderViewModel root, bool isExpanded, CancellationToken cancellationToken) + private void SetFoldersIsExpanded(AssetsFolderViewModel root, bool expand, CancellationToken cancellationToken) { - LinkedList nodes = new(); + var nodes = new LinkedList(); foreach (TreeItem folder in root.Folders) - { nodes.AddLast(folder); - } - LinkedListNode current = nodes.First; + var current = nodes.First; while (current != null) { - TreeItem folder = current.Value; + var folder = current.Value; // Collapse top-down (reduce layout updates) - if (!isExpanded) + if (!expand && folder.IsExpanded) { - folder.IsExpanded = isExpanded; + folder.IsExpanded = false; Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); } - foreach (TreeItem child in folder.Folders) + foreach (var child in folder.Folders) { nodes.AddLast(child); } @@ -125,15 +123,14 @@ public class MenuCommand : ViewModelCommand current = current.Next; } + if (!expand) return; + // Expand bottom-up (reduce layout updates) - if (isExpanded) + for (var node = nodes.Last; node != null; node = node.Previous) { - for (LinkedListNode node = nodes.Last; node != null; node = node.Previous) - { - node.Value.IsExpanded = isExpanded; - Thread.Yield(); - cancellationToken.ThrowIfCancellationRequested(); - } + node.Value.IsExpanded = true; + Thread.Yield(); + cancellationToken.ThrowIfCancellationRequested(); } } } diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index 1da8395e..ab009a4b 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -29,7 +29,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Extract_New_Tab": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true); } @@ -37,7 +37,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Export_Data": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.ExportData(asset.FullPath); } @@ -45,7 +45,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Properties": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Properties); } @@ -53,7 +53,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Textures": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Textures); } @@ -61,7 +61,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Models": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Meshes | EBulkType.Auto); } @@ -69,7 +69,7 @@ public class RightClickMenuCommand : ViewModelCommand case "Assets_Save_Animations": foreach (var asset in assetItems) { - Thread.Sleep(10); + Thread.Yield(); cancellationToken.ThrowIfCancellationRequested(); contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Animations | EBulkType.Auto); } diff --git a/FModel/Views/AesManager.xaml.cs b/FModel/Views/AesManager.xaml.cs index 7abe0a41..aa0c59ba 100644 --- a/FModel/Views/AesManager.xaml.cs +++ b/FModel/Views/AesManager.xaml.cs @@ -29,6 +29,6 @@ public partial class AesManager private async void OnClosing(object sender, CancelEventArgs e) { - await _applicationView.AesManager.UpdateProvider(false); + await _applicationView.UpdateProvider(false); } -} \ No newline at end of file +} diff --git a/README.md b/README.md index 1682005f..9e49065c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ FModel is actively maintained and developed by a dedicated community of contribu ### Installation: For installation, follow the instructions from [here](https://github.com/4sval/FModel/wiki/Installing-FModel) -### Support: +### Sponsorship: