diff --git a/AssetEditor.sln b/AssetEditor.sln index 1312836d2..2fb8122d7 100644 --- a/AssetEditor.sln +++ b/AssetEditor.sln @@ -118,12 +118,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmd", "Bmd", "{1D70E263-412 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Editors.BmdEditor", "Editors\BmdEditor\Editors.BmdEditor.csproj", "{9CFEEAF2-8260-F2F5-50FB-8FDA9BD12CF9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CampaignAnimationCreator", "CampaignAnimationCreator", "{97B5B2CE-D2E8-68AD-DABB-9986A92AD539}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CampaignAnimationCreator", "CampaignAnimationCreator", "{ED432A81-F5C7-0469-2ED7-3AF47D036060}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Editor.CampaignAnimationCreator", "Editors\CampaignAnimationCreator\Editor.CampaignAnimationCreator\Editor.CampaignAnimationCreator.csproj", "{438E6F1B-F5A9-49F3-8516-CB5E9D316C81}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.CampaignAnimationCreator", "Editors\CampaignAnimationCreator\Test.CampaignAnimationCreator\Test.CampaignAnimationCreator.csproj", "{91617891-AD16-423F-B56F-AC0D2CE82B95}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.CoreLog", "Shared\Logging\Shared.Core.Logging\Shared.CoreLog.csproj", "{C79EDB14-ED00-4474-B59F-9EED0AF39459}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -590,6 +592,18 @@ Global {91617891-AD16-423F-B56F-AC0D2CE82B95}.Release|x64.Build.0 = Release|Any CPU {91617891-AD16-423F-B56F-AC0D2CE82B95}.Release|x86.ActiveCfg = Release|Any CPU {91617891-AD16-423F-B56F-AC0D2CE82B95}.Release|x86.Build.0 = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|x64.ActiveCfg = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|x64.Build.0 = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|x86.ActiveCfg = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Debug|x86.Build.0 = Debug|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|Any CPU.Build.0 = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|x64.ActiveCfg = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|x64.Build.0 = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|x86.ActiveCfg = Release|Any CPU + {C79EDB14-ED00-4474-B59F-9EED0AF39459}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -644,9 +658,10 @@ Global {BB9F2AC1-680E-4289-B212-88744460383E} = {D84249ED-B24C-F001-6275-6713CC04DE13} {1D70E263-4123-4F00-A6AB-A42611732697} = {07AC615B-A8FC-4E1A-BDD5-BC11452429A0} {9CFEEAF2-8260-F2F5-50FB-8FDA9BD12CF9} = {1D70E263-4123-4F00-A6AB-A42611732697} - {97B5B2CE-D2E8-68AD-DABB-9986A92AD539} = {07AC615B-A8FC-4E1A-BDD5-BC11452429A0} - {438E6F1B-F5A9-49F3-8516-CB5E9D316C81} = {97B5B2CE-D2E8-68AD-DABB-9986A92AD539} - {91617891-AD16-423F-B56F-AC0D2CE82B95} = {97B5B2CE-D2E8-68AD-DABB-9986A92AD539} + {ED432A81-F5C7-0469-2ED7-3AF47D036060} = {07AC615B-A8FC-4E1A-BDD5-BC11452429A0} + {438E6F1B-F5A9-49F3-8516-CB5E9D316C81} = {ED432A81-F5C7-0469-2ED7-3AF47D036060} + {91617891-AD16-423F-B56F-AC0D2CE82B95} = {ED432A81-F5C7-0469-2ED7-3AF47D036060} + {C79EDB14-ED00-4474-B59F-9EED0AF39459} = {58F19D12-FE6E-A99A-941B-96C21567964E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB5968F3-98ED-4AFF-98EA-0DBEDCACADF2} diff --git a/AssetEditor/App.xaml.cs b/AssetEditor/App.xaml.cs index c9ad852a4..2beebd5b4 100644 --- a/AssetEditor/App.xaml.cs +++ b/AssetEditor/App.xaml.cs @@ -1,6 +1,4 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; +using System.Diagnostics; using System.Windows; using System.Windows.Threading; using AssetEditor.Services; @@ -10,14 +8,16 @@ using CommunityToolkit.Diagnostics; using Editors.Ipc; using Microsoft.Extensions.DependencyInjection; -using Shared.Core.DependencyInjection; using Shared.Core.DevConfig; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.ErrorHandling; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.Core.Settings; +using Shared.Core.ToolCreation; using Shared.Ui.Common; namespace AssetEditor @@ -100,7 +100,7 @@ private void LoadCAPackFiles(ApplicationSettingsService settingsService) { var packfileService = _serviceProvider.GetRequiredService(); var containerLoader = _serviceProvider.GetRequiredService(); - var loadRes = containerLoader.CreateFromGameEnum(PackFileContainerType.Cached, settingsService.CurrentSettings.CurrentGame); + var loadRes = containerLoader.CreateFromGameEnum(PackFileContainerType.Database, settingsService.CurrentSettings.CurrentGame); if (loadRes == null) MessageBox.Show($"Unable to load all CA packfiles in {gamePath}"); diff --git a/AssetEditor/DependencyInjectionContainer.cs b/AssetEditor/DependencyInjectionContainer.cs index fe7785e28..2d62a3f9c 100644 --- a/AssetEditor/DependencyInjectionContainer.cs +++ b/AssetEditor/DependencyInjectionContainer.cs @@ -5,10 +5,10 @@ using AssetEditor.Views.Settings; using AssetEditor.Views.Updater; using Microsoft.Extensions.DependencyInjection; +using Shared.Core.Commands; using Shared.Core.DependencyInjection; using Shared.Core.DevConfig; using Shared.Core.ErrorHandling.Exceptions; -using Shared.Core.Events.Global; using Shared.Core.ToolCreation; namespace AssetEditor @@ -21,8 +21,11 @@ public override void Register(IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddSingleton(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); - serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -40,6 +43,7 @@ public override void Register(IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddScoped(); diff --git a/AssetEditor/GlobalUsings.cs b/AssetEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/AssetEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/AssetEditor/Language_En.json b/AssetEditor/Language_En.json index e87eaedc2..8097c7561 100644 --- a/AssetEditor/Language_En.json +++ b/AssetEditor/Language_En.json @@ -102,11 +102,13 @@ "SettingsWindow.GameDataPathHint": "Browse for game data location e.g. \"C:\\SteamLibrary\\steamapps\\common\\Total War WARHAMMER III\\data\"", "MenuBar.File": "_File", - "MenuBar.File.NewPack": "New Pack", + "MenuBar.File.NewPack": "New Project/Pack", "MenuBar.File.NewAnimPackWh3": "New AnimPack (Warhammer III)", "MenuBar.File.NewAnimPack3K": "New AnimPack (Three Kingdoms)", "MenuBar.File.SaveActivePack": "Save active pack", - "MenuBar.File.OpenPack": "Open Pack", + "MenuBar.File.OpenProjectFolder": "Open Project", + "MenuBar.File.ImportPackAsProject": "Import Pack as project", + "MenuBar.File.ImportReferencePack": "Import reference project", "MenuBar.File.OpenRecentPacks": "Open Recent Packs", "MenuBar.File.OpenGamePacks": "Open Game Packs", "MenuBar.File.GameAttila": "Attila", diff --git a/AssetEditor/Services/DependencyInjectionConfig.cs b/AssetEditor/Services/DependencyInjectionConfig.cs index e56960051..fe6ab5542 100644 --- a/AssetEditor/Services/DependencyInjectionConfig.cs +++ b/AssetEditor/Services/DependencyInjectionConfig.cs @@ -1,7 +1,6 @@ -using System; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Shared.Core.DependencyInjection; -using Shared.Core.ErrorHandling; +using Shared.Core.Misc; using Shared.Core.ToolCreation; namespace AssetEditor.Services @@ -69,7 +68,7 @@ public IServiceProvider Build(bool forceValidateServiceScopes, Action replaceServices) { - Logging.Configure(Serilog.Events.LogEventLevel.Information); + Logging.Configure(Serilog.Events.LogEventLevel.Information, DirectoryHelper.LogDirectory); foreach (var container in _dependencyContainers) container.Register(services); diff --git a/AssetEditor/Services/EditorManager.cs b/AssetEditor/Services/EditorManager.cs index 1bb9a4149..1e54d336c 100644 --- a/AssetEditor/Services/EditorManager.cs +++ b/AssetEditor/Services/EditorManager.cs @@ -1,15 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; +using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.ToolCreation; diff --git a/AssetEditor/Services/GraphicsResourceExceptionInfoProvider.cs b/AssetEditor/Services/GraphicsResourceExceptionInfoProvider.cs index 3cdeac5d0..82ed231e7 100644 --- a/AssetEditor/Services/GraphicsResourceExceptionInfoProvider.cs +++ b/AssetEditor/Services/GraphicsResourceExceptionInfoProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using GameWorld.Core.Services; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling.Exceptions; using Shared.Core.ToolCreation; diff --git a/AssetEditor/Services/RecentFilesTracker.cs b/AssetEditor/Services/RecentFilesTracker.cs index 9695227d2..681022103 100644 --- a/AssetEditor/Services/RecentFilesTracker.cs +++ b/AssetEditor/Services/RecentFilesTracker.cs @@ -1,6 +1,6 @@ using System; using Shared.Core.Events; -using Shared.Core.Events.Global; +using Shared.Core.PackFiles.Events; using Shared.Core.Settings; namespace AssetEditor.Services @@ -25,7 +25,7 @@ private void Handler(PackFileContainerAddedEvent e) if (string.IsNullOrEmpty(e.Container.SystemFilePath)) return; - _applicationSettingsService.AddRecentlyOpenedPackFile(e.Container.SystemFilePath); + _applicationSettingsService.AddRecentlyOpenedPackFile(e.Container.SystemFilePath, e.Container.ContainerType, e.Container.IsReadOnly); _applicationSettingsService.Save(); } diff --git a/Shared/SharedCore/Shared.Core/Services/TouchedFilesRecorder.cs b/AssetEditor/Services/TouchedFilesRecorder.cs similarity index 94% rename from Shared/SharedCore/Shared.Core/Services/TouchedFilesRecorder.cs rename to AssetEditor/Services/TouchedFilesRecorder.cs index 221d836b4..aa05b65e9 100644 --- a/Shared/SharedCore/Shared.Core/Services/TouchedFilesRecorder.cs +++ b/AssetEditor/Services/TouchedFilesRecorder.cs @@ -1,11 +1,10 @@ -using Shared.Core.ErrorHandling; -using Shared.Core.Events; -using Shared.Core.Events.Global; +using Shared.Core.Events; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.Settings; -namespace Shared.Core.Services +namespace AssetEditor.Services { public class TouchedFilesRecorder { diff --git a/AssetEditor/Themes/Controls.xaml.cs b/AssetEditor/Themes/Controls.xaml.cs index b01cc4d5f..b1441a0fc 100644 --- a/AssetEditor/Themes/Controls.xaml.cs +++ b/AssetEditor/Themes/Controls.xaml.cs @@ -1,9 +1,6 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.IO; using System.Windows; -using Serilog; -using Shared.Core.ErrorHandling; using WindowHandling; namespace AssetEditor.Themes diff --git a/AssetEditor/UiCommands/CreateNewProjectCommand.cs b/AssetEditor/UiCommands/CreateNewProjectCommand.cs new file mode 100644 index 000000000..762aec479 --- /dev/null +++ b/AssetEditor/UiCommands/CreateNewProjectCommand.cs @@ -0,0 +1,42 @@ +using CommonControls.BaseDialogs; +using Shared.Core.Events; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace AssetEditor.UiCommands +{ + public class CreateNewProjectCommand : IAeCommand + { + private readonly IPackFileService _packFileService; + private readonly IStandardDialogs _standardDialogs; + private readonly ISystemFolderContainerFactory _systemFolderContainerFactory; + + public CreateNewProjectCommand( + IPackFileService packFileService, + IStandardDialogs standardDialogs, + ISystemFolderContainerFactory systemFolderContainerFactory) + { + _packFileService = packFileService; + _standardDialogs = standardDialogs; + _systemFolderContainerFactory = systemFolderContainerFactory; + } + + public void Execute() + { + var window = new NewPackFileWindow(); + if (window.ShowDialog() != true) + return; + + if (string.IsNullOrWhiteSpace(window.SelectedFolderPath)) + { + _standardDialogs.ShowDialogBox("No folder was selected", "Error"); + return; + } + + var folderPack = _systemFolderContainerFactory.Create(window.SelectedFolderPath); + _packFileService.AddContainer(folderPack); + _packFileService.SetEditablePack(folderPack); + } + } +} diff --git a/AssetEditor/UiCommands/ImportPackAsAsProjectCommand.cs b/AssetEditor/UiCommands/ImportPackAsAsProjectCommand.cs new file mode 100644 index 000000000..863f105dc --- /dev/null +++ b/AssetEditor/UiCommands/ImportPackAsAsProjectCommand.cs @@ -0,0 +1,76 @@ +using System.IO; +using System.Windows.Forms; +using Shared.Core.Events; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace AssetEditor.UiCommands +{ + public class ImportPackAsAsProjectCommand : IAeCommand + { + private readonly IPackFileService _packFileService; + private readonly IPackFileContainerLoader _packFileContainerLoader; + private readonly ISystemFolderContainerFactory _systemFolderContainerFactory; + private readonly IStandardDialogs _standardDialogs; + + public ImportPackAsAsProjectCommand( + IPackFileService packFileService, + IPackFileContainerLoader packFileContainerLoader, + ISystemFolderContainerFactory systemFolderContainerFactory, + IStandardDialogs standardDialogs) + { + _packFileService = packFileService; + _packFileContainerLoader = packFileContainerLoader; + _systemFolderContainerFactory = systemFolderContainerFactory; + _standardDialogs = standardDialogs; + } + + public void Execute() + { + // Step 1: Select the .pack file + using var packDialog = new OpenFileDialog + { + Filter = "Pack files (*.pack)|*.pack|All files (*.*)|*.*", + Title = "Select pack file to convert" + }; + + if (packDialog.ShowDialog() != DialogResult.OK) + return; + + // Step 2: Select destination folder + using var folderDialog = new FolderBrowserDialog + { + Description = "Select destination folder for extracted files", + UseDescriptionForTitle = true + }; + + if (folderDialog.ShowDialog() != DialogResult.OK) + return; + + var destinationFolder = folderDialog.SelectedPath; + + // Step 3: Load the pack and extract all files to the destination + var packContainer = _packFileContainerLoader.CreateFromPackFile(PackFileContainerType.Normal, packDialog.FileName, false); + var allFiles = packContainer.GetAllFiles(); + + foreach (var (relativePath, packFile) in allFiles) + { + var absolutePath = Path.Combine(destinationFolder, relativePath); + var directory = Path.GetDirectoryName(absolutePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + var data = packFile.DataSource.ReadData(); + File.WriteAllBytes(absolutePath, data); + } + + // Step 4: Open the extracted folder as a SystemFolderContainer + var systemContainer = _systemFolderContainerFactory.Create(destinationFolder); + _packFileService.AddContainer(systemContainer); + _packFileService.SetEditablePack(systemContainer); + + } + } +} diff --git a/AssetEditor/UiCommands/OpenPackFileCommand.cs b/AssetEditor/UiCommands/ImportReferencePackCommand.cs similarity index 50% rename from AssetEditor/UiCommands/OpenPackFileCommand.cs rename to AssetEditor/UiCommands/ImportReferencePackCommand.cs index 9862b81b8..86de74935 100644 --- a/AssetEditor/UiCommands/OpenPackFileCommand.cs +++ b/AssetEditor/UiCommands/ImportReferencePackCommand.cs @@ -1,31 +1,42 @@ using System.Windows.Forms; using Shared.Core.Events; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; namespace AssetEditor.UiCommands { - public class OpenPackFileCommand : IAeCommand + public class ImportReferencePackCommand : IAeCommand { private readonly IPackFileService _packFileService; private readonly IPackFileContainerLoader _packFileContainerLoader; + private readonly ISystemFolderContainerFactory _systemFolderContainerFactory; + private readonly IStandardDialogs _standardDialogs; - public OpenPackFileCommand(IPackFileService packFileService, IPackFileContainerLoader packFileContainerLoader) + public ImportReferencePackCommand( + IPackFileService packFileService, + IPackFileContainerLoader packFileContainerLoader, + ISystemFolderContainerFactory systemFolderContainerFactory, + IStandardDialogs standardDialogs) { _packFileService = packFileService; _packFileContainerLoader = packFileContainerLoader; + _systemFolderContainerFactory = systemFolderContainerFactory; + _standardDialogs = standardDialogs; } public void Execute() { - var dialog = new OpenFileDialog() + using var dialog = new OpenFileDialog { Filter = "Pack files (*.pack)|*.pack|All files (*.*)|*.*" }; if (dialog.ShowDialog() != DialogResult.OK) return; - var container = _packFileContainerLoader.CreateFromPackFile(PackFileContainerType.Normal, dialog.FileName, false); + + var container = _packFileContainerLoader.CreateFromPackFile(PackFileContainerType.Normal, dialog.FileName, true); _packFileService.AddContainer(container, true); } } diff --git a/AssetEditor/UiCommands/OpenGamePackCommand.cs b/AssetEditor/UiCommands/OpenGamePackCommand.cs index e1c9129a4..2252354ff 100644 --- a/AssetEditor/UiCommands/OpenGamePackCommand.cs +++ b/AssetEditor/UiCommands/OpenGamePackCommand.cs @@ -2,6 +2,7 @@ using System.Windows.Forms; using Shared.Core.Events; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Ui.Common; @@ -50,7 +51,7 @@ public void Execute() using (new WaitCursor()) { - var res = _packFileContainerLoader.CreateFromGameEnum(PackFileContainerType.Cached, _game); + var res = _packFileContainerLoader.CreateFromGameEnum(PackFileContainerType.Database, _game); _packFileService.AddContainer(res); } } diff --git a/AssetEditor/UiCommands/OpenProjectCommand.cs b/AssetEditor/UiCommands/OpenProjectCommand.cs new file mode 100644 index 000000000..47c8c754c --- /dev/null +++ b/AssetEditor/UiCommands/OpenProjectCommand.cs @@ -0,0 +1,41 @@ +using System.Windows.Forms; +using Shared.Core.Events; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace AssetEditor.UiCommands +{ + public class OpenProjectCommand : IAeCommand + { + private readonly IPackFileService _packFileService; + private readonly ISystemFolderContainerFactory _systemFolderContainerFactory; + private readonly IStandardDialogs _standardDialogs; + + public OpenProjectCommand( + IPackFileService packFileService, + ISystemFolderContainerFactory systemFolderContainerFactory, + IStandardDialogs standardDialogs) + { + _packFileService = packFileService; + _systemFolderContainerFactory = systemFolderContainerFactory; + _standardDialogs = standardDialogs; + } + + public void Execute() + { + using var dialog = new FolderBrowserDialog + { + Description = "Select folder to open as a pack", + UseDescriptionForTitle = true + }; + + if (dialog.ShowDialog() != DialogResult.OK) + return; + + var container = _systemFolderContainerFactory.Create(dialog.SelectedPath); + _packFileService.AddContainer(container); + _packFileService.SetEditablePack(container); + } + } +} diff --git a/AssetEditor/UiCommands/PrintScopesCommand.cs b/AssetEditor/UiCommands/PrintScopesCommand.cs index e06d7c54c..655cbe1b3 100644 --- a/AssetEditor/UiCommands/PrintScopesCommand.cs +++ b/AssetEditor/UiCommands/PrintScopesCommand.cs @@ -1,5 +1,5 @@ -using Shared.Core.DependencyInjection; -using Shared.Core.Events; +using Shared.Core.Events; +using Shared.Core.ToolCreation; namespace AssetEditor.UiCommands { diff --git a/AssetEditor/UiCommands/PrintTrackedGraphicsResourcesCommand.cs b/AssetEditor/UiCommands/PrintTrackedGraphicsResourcesCommand.cs index 606c6337d..a02b00d53 100644 --- a/AssetEditor/UiCommands/PrintTrackedGraphicsResourcesCommand.cs +++ b/AssetEditor/UiCommands/PrintTrackedGraphicsResourcesCommand.cs @@ -1,8 +1,5 @@ using System.Text; using GameWorld.Core.Services; -using Serilog; -using Shared.Core.DependencyInjection; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.ToolCreation; diff --git a/AssetEditor/ViewModels/EditorShortcutViewModel.cs b/AssetEditor/ViewModels/EditorShortcutViewModel.cs index 134c43947..9e6cc7eb3 100644 --- a/AssetEditor/ViewModels/EditorShortcutViewModel.cs +++ b/AssetEditor/ViewModels/EditorShortcutViewModel.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.Input; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.ToolCreation; namespace AssetEditor.ViewModels diff --git a/AssetEditor/ViewModels/MainViewModel.cs b/AssetEditor/ViewModels/MainViewModel.cs index 2e55bcb1d..5946702d7 100644 --- a/AssetEditor/ViewModels/MainViewModel.cs +++ b/AssetEditor/ViewModels/MainViewModel.cs @@ -3,9 +3,10 @@ using AssetEditor.Services; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.Services; using Shared.Core.Settings; diff --git a/AssetEditor/ViewModels/MenuBarViewModel.cs b/AssetEditor/ViewModels/MenuBarViewModel.cs index 2ef5629e2..a3383f7b4 100644 --- a/AssetEditor/ViewModels/MenuBarViewModel.cs +++ b/AssetEditor/ViewModels/MenuBarViewModel.cs @@ -3,8 +3,8 @@ using System.Diagnostics; using System.IO; using System.Linq; +using AssetEditor.Services; using AssetEditor.UiCommands; -using CommonControls.BaseDialogs; using CommunityToolkit.Mvvm.Input; using Editors.AnimationFragmentEditor.AnimationPack.Commands; using Editors.Reports.Animation; @@ -58,7 +58,7 @@ public MenuBarViewModel(IPackFileService packfileService, _packFileContainerLoader = packFileContainerLoader; _standardDialogs = standardDialogs; var settings = settingsService.CurrentSettings; - settings.RecentPackFilePaths.CollectionChanged += OnRecentPackFilePathsChanged; + settings.RecentPackFiles.CollectionChanged += OnRecentPackFilePathsChanged; CreateRecentPackFilesItems(); CreateTools(); } @@ -70,29 +70,14 @@ private void OnRecentPackFilePathsChanged(object? sender, System.Collections.Spe public void Dispose() { - _settingsService.CurrentSettings.RecentPackFilePaths.CollectionChanged -= OnRecentPackFilePathsChanged; + _settingsService.CurrentSettings.RecentPackFiles.CollectionChanged -= OnRecentPackFilePathsChanged; } [RelayCommand] private void OpenSettingsWindow() => _uiCommandFactory.Create().Execute(); - [RelayCommand] private void OpenPackFile() => _uiCommandFactory.Create().Execute(); - [RelayCommand] private void CreateNewPackFile() - { - var window = new TextInputWindow("New Pack Name", ""); - if (window.ShowDialog() == true) - { - if (string.IsNullOrWhiteSpace(window.TextValue)) - { - _standardDialogs.ShowDialogBox($"'{window.TextValue}' is not a valid packfile name", "Error"); - return; - } - - var currentGame = _settingsService.CurrentSettings.CurrentGame; - var pfsVersion = GameInformationDatabase.Games[currentGame].PackFileVersion; - - var newPackFile = _packfileService.CreateNewPackFileContainer(window.TextValue.Trim(), pfsVersion, PackFileCAType.MOD); - _packfileService.SetEditablePack(newPackFile); - } - } + [RelayCommand] private void OpenProjectFolder() => _uiCommandFactory.Create().Execute(); + [RelayCommand] private void ImportPackAsProject() => _uiCommandFactory.Create().Execute(); + [RelayCommand] private void ImportReferencePack() => _uiCommandFactory.Create().Execute(); + [RelayCommand] private void CreateNewProject() => _uiCommandFactory.Create().Execute(); [RelayCommand] private void CreateAnimPackWarhammer3() => _uiCommandFactory.Create().CreateAnimationDbWarhammer3(); [RelayCommand] private void CreateAnimPack3k() => _uiCommandFactory.Create().CreateAnimationDb3k(); @@ -164,14 +149,19 @@ void CreateRecentPackFilesItems() var settings = _settingsService.CurrentSettings; RecentPackFiles.Clear(); - var menuItemViewModels = settings.RecentPackFilePaths.Select(path => new RecentPackFileItem( - path, + var menuItemViewModels = settings.RecentPackFiles.Select(info => new RecentPackFileItem( + info.Path, + info.ContainerType, + info.IsReadOnly, () => { - var container = _packFileContainerLoader.CreateFromPackFile(PackFileContainerType.Normal, path, false); + var container = info.ContainerType == PackFileContainerType.SystemFolder + ? _packFileContainerLoader.CreateFromSystemFolder(info.Path) + : _packFileContainerLoader.CreateFromPackFile(info.ContainerType, info.Path, info.IsReadOnly); + if (container == null) { - System.Windows.MessageBox.Show($"Unable to load packfiles {path}"); + System.Windows.MessageBox.Show($"Unable to load packfiles {info.Path}"); return; } diff --git a/AssetEditor/ViewModels/RecentPackFileItem.cs b/AssetEditor/ViewModels/RecentPackFileItem.cs index 21ab7b32c..89fdec0d0 100644 --- a/AssetEditor/ViewModels/RecentPackFileItem.cs +++ b/AssetEditor/ViewModels/RecentPackFileItem.cs @@ -1,15 +1,27 @@ using System; using System.Windows.Input; using CommunityToolkit.Mvvm.Input; +using Shared.Core.PackFiles.Models; namespace AssetEditor.ViewModels { public class RecentPackFileItem { - public RecentPackFileItem(string path, Action execute) + public RecentPackFileItem(string path, PackFileContainerType containerType, bool isReadOnly, Action execute) { Command = new RelayCommand(execute); - Header = System.IO.Path.GetFileName(path); + Path = path; + Header = $"{System.IO.Path.GetFileName(path)} {BuildKindLabel(containerType, isReadOnly)}"; + } + + private static string BuildKindLabel(PackFileContainerType containerType, bool isReadOnly) + { + var typeText = containerType switch + { + PackFileContainerType.SystemFolder => "System Folder", + _ => containerType.ToString() + }; + return isReadOnly ? $"[{typeText}, Read Only]" : $"[{typeText}]"; } public string Header { get; set; } diff --git a/AssetEditor/ViewModels/UpdaterViewModel.cs b/AssetEditor/ViewModels/UpdaterViewModel.cs index f4595d4c0..e523d3c9d 100644 --- a/AssetEditor/ViewModels/UpdaterViewModel.cs +++ b/AssetEditor/ViewModels/UpdaterViewModel.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Octokit; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.Services; using Application = System.Windows.Application; diff --git a/AssetEditor/Views/MenuBarView.xaml b/AssetEditor/Views/MenuBarView.xaml index 0cce5aa0c..6d5866eeb 100644 --- a/AssetEditor/Views/MenuBarView.xaml +++ b/AssetEditor/Views/MenuBarView.xaml @@ -23,7 +23,7 @@ - + @@ -31,7 +31,19 @@ - + + + + + + + + + + + + + @@ -55,14 +67,7 @@ - - - - - - - - + diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..9486e0c7e --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + + enable + + + + + + + + diff --git a/Editors/AnimationEditor/AnimationKeyframeEditor/AnimationKeyframeEditorViewModel.cs b/Editors/AnimationEditor/AnimationKeyframeEditor/AnimationKeyframeEditorViewModel.cs index fb3b74d37..8b08969fe 100644 --- a/Editors/AnimationEditor/AnimationKeyframeEditor/AnimationKeyframeEditorViewModel.cs +++ b/Editors/AnimationEditor/AnimationKeyframeEditor/AnimationKeyframeEditorViewModel.cs @@ -18,7 +18,7 @@ using Shared.Core.Events; using Shared.Core.Misc; using Shared.Core.PackFiles; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.Animation; using Shared.GameFormats.AnimationPack; using Shared.Ui.Common; diff --git a/Editors/AnimationEditor/MountAnimationCreator/MountAnimationCreatorViewModel.cs b/Editors/AnimationEditor/MountAnimationCreator/MountAnimationCreatorViewModel.cs index 87f9993e2..fb193a9c5 100644 --- a/Editors/AnimationEditor/MountAnimationCreator/MountAnimationCreatorViewModel.cs +++ b/Editors/AnimationEditor/MountAnimationCreator/MountAnimationCreatorViewModel.cs @@ -25,8 +25,8 @@ using Editors.Shared.Core.Common.ReferenceModel; using Editors.AnimationVisualEditors.MountAnimationCreator.Services; using GameWorld.Core.Services; -using Shared.Core.Services; -using Shared.Core.Events.Global; +using Shared.Core.Commands; +using Shared.Core.PackFiles.Utility; namespace AnimationEditor.MountAnimationCreator diff --git a/Editors/AnimationEditor/MountAnimationCreator/Services/BatchProcessorService.cs b/Editors/AnimationEditor/MountAnimationCreator/Services/BatchProcessorService.cs index bf9808620..4c864a19e 100644 --- a/Editors/AnimationEditor/MountAnimationCreator/Services/BatchProcessorService.cs +++ b/Editors/AnimationEditor/MountAnimationCreator/Services/BatchProcessorService.cs @@ -7,9 +7,8 @@ using GameWorld.Core.Animation; using GameWorld.Core.Services; using Shared.Core.ErrorHandling; -using Shared.Core.Misc; using Shared.Core.PackFiles; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.Animation; using Shared.GameFormats.AnimationPack; using Shared.GameFormats.AnimationPack.AnimPackFileTypes.Wh3; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationBatchExporter/AnimationBatchExportViewModel.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationBatchExporter/AnimationBatchExportViewModel.cs index 8f2709ab1..2e53e0398 100644 --- a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationBatchExporter/AnimationBatchExportViewModel.cs +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationBatchExporter/AnimationBatchExportViewModel.cs @@ -3,7 +3,6 @@ using System.Windows; using CommonControls.BaseDialogs.ErrorListDialog; using GameWorld.Core.Services; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/AnimPackViewModel.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/AnimPackViewModel.cs index a4a120bc9..362864464 100644 --- a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/AnimPackViewModel.cs +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/AnimPackViewModel.cs @@ -9,7 +9,7 @@ using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; using Shared.GameFormats.AnimationMeta.Parsing; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateEmptyWarhammer3AnimSetFileCommand.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateEmptyWarhammer3AnimSetFileCommand.cs index 91d003057..8aaf53fa7 100644 --- a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateEmptyWarhammer3AnimSetFileCommand.cs +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateEmptyWarhammer3AnimSetFileCommand.cs @@ -1,7 +1,7 @@ using CommonControls.BaseDialogs; using CommonControls.Editors.AnimationPack; using Shared.Core.Events; -using Shared.Core.Misc; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.AnimationPack.AnimPackFileTypes; using Shared.GameFormats.AnimationPack.AnimPackFileTypes.Wh3; using static Shared.GameFormats.AnimationPack.AnimPackFileTypes.Wh3.AnimationBinEntry; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateExampleAnimationDbCommand.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateExampleAnimationDbCommand.cs index 9fe1ca0bb..00e3f6376 100644 --- a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateExampleAnimationDbCommand.cs +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/AnimationPack/Commands/CreateExampleAnimationDbCommand.cs @@ -1,10 +1,9 @@ using System.Windows; using CommonControls.BaseDialogs; using Shared.Core.Events; -using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.AnimationPack; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/DevConfig/AnimPack_WH3.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/DevConfig/AnimPack_WH3.cs index 00ab4a7e9..37a8a0264 100644 --- a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/DevConfig/AnimPack_WH3.cs +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/DevConfig/AnimPack_WH3.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; diff --git a/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/GlobalUsings.cs b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/AnimationFragmentEditor/Editor.AnimationFragmentEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorMultiSelectTests.cs b/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorMultiSelectTests.cs index 4e3c31639..e640731a8 100644 --- a/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorMultiSelectTests.cs +++ b/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorMultiSelectTests.cs @@ -1,5 +1,5 @@ -using Editors.AnimationMeta.Presentation; -using Shared.Core.Events.Global; +using Editors.AnimationMeta.Presentation; +using Shared.Core.Commands; using Shared.GameFormats.AnimationMeta.Parsing; using Test.TestingUtility.Shared; using Test.TestingUtility.TestUtility; diff --git a/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorViewModelTests.cs b/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorViewModelTests.cs index 2c6f0d7fa..a4a96ca65 100644 --- a/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorViewModelTests.cs +++ b/Editors/AnimationMeta/Test.AnimationMeta/MetaDataEditorViewModelTests.cs @@ -1,5 +1,5 @@ using Editors.AnimationMeta.Presentation; -using Shared.Core.Events.Global; +using Shared.Core.Commands; using Shared.GameFormats.AnimationMeta.Definitions; using Shared.GameFormats.AnimationMeta.Parsing; using Test.TestingUtility.Shared; diff --git a/Editors/AnimationReTarget/Editors.AnimatioReTarget/Editor/Saving/SaveManager.cs b/Editors/AnimationReTarget/Editors.AnimatioReTarget/Editor/Saving/SaveManager.cs index 9b0969b0c..f9f1c48f6 100644 --- a/Editors/AnimationReTarget/Editors.AnimatioReTarget/Editor/Saving/SaveManager.cs +++ b/Editors/AnimationReTarget/Editors.AnimatioReTarget/Editor/Saving/SaveManager.cs @@ -6,6 +6,7 @@ using GameWorld.Core.Services; using Shared.Core.Misc; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.GameFormats.Animation; diff --git a/Editors/AnimationReTarget/Editors.AnimatioReTarget/GlobalUsings.cs b/Editors/AnimationReTarget/Editors.AnimatioReTarget/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/AnimationReTarget/Editors.AnimatioReTarget/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/AnimationReTarget/Test.AnimatioReTarget/AnimationRetargeTool_ComplexUseCase.cs b/Editors/AnimationReTarget/Test.AnimatioReTarget/AnimationRetargeTool_ComplexUseCase.cs index 623471d5a..5aaed373d 100644 --- a/Editors/AnimationReTarget/Test.AnimatioReTarget/AnimationRetargeTool_ComplexUseCase.cs +++ b/Editors/AnimationReTarget/Test.AnimatioReTarget/AnimationRetargeTool_ComplexUseCase.cs @@ -2,7 +2,7 @@ using Editors.AnimatioReTarget.Editor.BoneHandling; using Editors.Shared.Core.Common; using GameWorld.Core.SceneNodes; -using Shared.Core.Events.Global; +using Shared.Core.Commands; using Shared.Core.PackFiles.Models; using Shared.Core.ToolCreation; using Test.TestingUtility.Shared; diff --git a/Editors/Audio/AudioEditor/Commands/AudioProjectViewer/EditViewerRowsCommand.cs b/Editors/Audio/AudioEditor/Commands/AudioProjectViewer/EditViewerRowsCommand.cs index 7e7580575..e051413d4 100644 --- a/Editors/Audio/AudioEditor/Commands/AudioProjectViewer/EditViewerRowsCommand.cs +++ b/Editors/Audio/AudioEditor/Commands/AudioProjectViewer/EditViewerRowsCommand.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Data; +using System.Data; using Editors.Audio.AudioEditor.Core; using Editors.Audio.AudioEditor.Events.AudioProjectViewer.Table; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; namespace Editors.Audio.AudioEditor.Commands.AudioProjectViewer diff --git a/Editors/Audio/AudioEditor/Core/AudioEditorFileService.cs b/Editors/Audio/AudioEditor/Core/AudioEditorFileService.cs index 2ac72e216..3a1ae4c93 100644 --- a/Editors/Audio/AudioEditor/Core/AudioEditorFileService.cs +++ b/Editors/Audio/AudioEditor/Core/AudioEditorFileService.cs @@ -10,7 +10,7 @@ using Editors.Audio.Shared.Storage; using Shared.Core.Events; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; namespace Editors.Audio.AudioEditor.Core diff --git a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs index 68bbefa7a..b63220950 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs @@ -15,10 +15,10 @@ using Editors.Audio.WaveformVisualiser.Events; using Editors.Audio.AudioEditor.Presentation.Shared.Models; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Ui.Common; +using Shared.Core.PackFiles.Events; namespace Editors.Audio.AudioEditor.Presentation.AudioFilesExplorer { diff --git a/Editors/Audio/AudioEditor/Presentation/AudioProjectEditor/AudioProjectEditorViewModel.cs b/Editors/Audio/AudioEditor/Presentation/AudioProjectEditor/AudioProjectEditorViewModel.cs index b5c9e54ea..d8e2ae73e 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioProjectEditor/AudioProjectEditorViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/AudioProjectEditor/AudioProjectEditorViewModel.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Data; using System.IO; -using System.Linq; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -23,8 +20,6 @@ using Editors.Audio.Shared.AudioProject.Models; using Editors.Audio.Shared.GameInformation.Warhammer3; using Editors.Audio.Shared.Storage; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Ui.Common; diff --git a/Editors/Audio/AudioEditor/Presentation/AudioProjectViewer/AudioProjectViewerViewModel.cs b/Editors/Audio/AudioEditor/Presentation/AudioProjectViewer/AudioProjectViewerViewModel.cs index 5e2802c04..ea5ceb906 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioProjectViewer/AudioProjectViewerViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/AudioProjectViewer/AudioProjectViewerViewModel.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Data; -using System.Linq; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -17,8 +14,6 @@ using Editors.Audio.AudioEditor.Presentation.Shared.Table; using Editors.Audio.Shared.AudioProject.Models; using Editors.Audio.Shared.Storage; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Ui.Common; diff --git a/Editors/Audio/AudioEditor/Presentation/NewAudioProject/NewAudioProjectViewModel.cs b/Editors/Audio/AudioEditor/Presentation/NewAudioProject/NewAudioProjectViewModel.cs index f6fbf292f..03ad96db4 100644 --- a/Editors/Audio/AudioEditor/Presentation/NewAudioProject/NewAudioProjectViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/NewAudioProject/NewAudioProjectViewModel.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Editors.Audio.AudioEditor.Core; using Editors.Audio.Shared.AudioProject.Models; using Editors.Audio.Shared.GameInformation.Warhammer3; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.Services; using Shared.Core.Settings; diff --git a/Editors/Audio/AudioProjectConverter/AudioProjectConverterViewModel.cs b/Editors/Audio/AudioProjectConverter/AudioProjectConverterViewModel.cs index 86f908940..07a064f11 100644 --- a/Editors/Audio/AudioProjectConverter/AudioProjectConverterViewModel.cs +++ b/Editors/Audio/AudioProjectConverter/AudioProjectConverterViewModel.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; using System.Windows.Forms; using System.Xml.Linq; using CommunityToolkit.Mvvm.ComponentModel; @@ -12,9 +9,8 @@ using Editors.Audio.Shared.GameInformation.Warhammer3; using Editors.Audio.Shared.Storage; using Editors.Audio.Shared.Wwise.HircExploration; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.Core.Settings; using Shared.GameFormats.Audio.Formats.Pcm; diff --git a/Editors/Audio/DevConfig/AudioEditor_Wh3.cs b/Editors/Audio/DevConfig/AudioEditor_Wh3.cs index a35c379ff..63630d961 100644 --- a/Editors/Audio/DevConfig/AudioEditor_Wh3.cs +++ b/Editors/Audio/DevConfig/AudioEditor_Wh3.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/Audio/DevConfig/AudioExplorer_Attila.cs b/Editors/Audio/DevConfig/AudioExplorer_Attila.cs index b87c43391..b175d9f76 100644 --- a/Editors/Audio/DevConfig/AudioExplorer_Attila.cs +++ b/Editors/Audio/DevConfig/AudioExplorer_Attila.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/Audio/DevConfig/AudioExplorer_Wh3.cs b/Editors/Audio/DevConfig/AudioExplorer_Wh3.cs index f891e12b8..835ba39dc 100644 --- a/Editors/Audio/DevConfig/AudioExplorer_Wh3.cs +++ b/Editors/Audio/DevConfig/AudioExplorer_Wh3.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/Audio/DialogueEventMerger/DialogueEventMergerViewModel.cs b/Editors/Audio/DialogueEventMerger/DialogueEventMergerViewModel.cs index c1bf8c7d1..4145d5c02 100644 --- a/Editors/Audio/DialogueEventMerger/DialogueEventMergerViewModel.cs +++ b/Editors/Audio/DialogueEventMerger/DialogueEventMergerViewModel.cs @@ -1,15 +1,12 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Editors.Audio.Shared.AudioProject.Compiler; using Editors.Audio.Shared.GameInformation.Warhammer3; using Editors.Audio.Shared.Storage; using Editors.Audio.Shared.Wwise.Generators; -using Serilog; -using Shared.Core.ErrorHandling; namespace Editors.Audio.DialogueEventMerger { diff --git a/Editors/Audio/GlobalUsings.cs b/Editors/Audio/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/Audio/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/Audio/Shared/AudioProject/AudioProjectFileService.cs b/Editors/Audio/Shared/AudioProject/AudioProjectFileService.cs index 10907f6a3..e5c032a8b 100644 --- a/Editors/Audio/Shared/AudioProject/AudioProjectFileService.cs +++ b/Editors/Audio/Shared/AudioProject/AudioProjectFileService.cs @@ -5,6 +5,7 @@ using Editors.Audio.Shared.AudioProject.Models; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; namespace Editors.Audio.Shared.AudioProject diff --git a/Editors/Audio/Shared/AudioProject/Compiler/AudioProjectCompilerService.cs b/Editors/Audio/Shared/AudioProject/Compiler/AudioProjectCompilerService.cs index 6eea25689..63eda6e19 100644 --- a/Editors/Audio/Shared/AudioProject/Compiler/AudioProjectCompilerService.cs +++ b/Editors/Audio/Shared/AudioProject/Compiler/AudioProjectCompilerService.cs @@ -1,13 +1,9 @@ -using System.Collections.Generic; -using System.Data; +using System.Data; using System.IO; -using System.Linq; using Editors.Audio.Shared.AudioProject.Models; using Editors.Audio.Shared.Dat; using Editors.Audio.Shared.GameInformation.Warhammer3; using Editors.Audio.Shared.Wwise.Generators; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.GameFormats.Wwise; diff --git a/Editors/Audio/Shared/Dat/DatGeneratorService.cs b/Editors/Audio/Shared/Dat/DatGeneratorService.cs index afba94103..fb395921c 100644 --- a/Editors/Audio/Shared/Dat/DatGeneratorService.cs +++ b/Editors/Audio/Shared/Dat/DatGeneratorService.cs @@ -2,7 +2,7 @@ using Editors.Audio.Shared.AudioProject.Models; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.Dat; namespace Editors.Audio.Shared.Dat diff --git a/Editors/Audio/Shared/Storage/BnkLoader.cs b/Editors/Audio/Shared/Storage/BnkLoader.cs index 79a14a7ac..0f68fa965 100644 --- a/Editors/Audio/Shared/Storage/BnkLoader.cs +++ b/Editors/Audio/Shared/Storage/BnkLoader.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Serilog; -using Shared.Core.ErrorHandling; +using System.Text; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; diff --git a/Editors/Audio/Shared/Wwise/Generators/Hirc/V136/ActionHircGenerator_V136.cs b/Editors/Audio/Shared/Wwise/Generators/Hirc/V136/ActionHircGenerator_V136.cs index a84db3646..b59e5327c 100644 --- a/Editors/Audio/Shared/Wwise/Generators/Hirc/V136/ActionHircGenerator_V136.cs +++ b/Editors/Audio/Shared/Wwise/Generators/Hirc/V136/ActionHircGenerator_V136.cs @@ -4,6 +4,7 @@ using Shared.GameFormats.Wwise.Hirc; using Shared.GameFormats.Wwise.Hirc.V136; using static Shared.GameFormats.Wwise.Hirc.V136.Shared.AkPropBundle_V136; +using Action = Editors.Audio.Shared.AudioProject.Models.Action; namespace Editors.Audio.Shared.Wwise.Generators.Hirc.V136 { diff --git a/Editors/Audio/Shared/Wwise/Generators/SoundBankGeneratorService.cs b/Editors/Audio/Shared/Wwise/Generators/SoundBankGeneratorService.cs index bbaf026c5..297665255 100644 --- a/Editors/Audio/Shared/Wwise/Generators/SoundBankGeneratorService.cs +++ b/Editors/Audio/Shared/Wwise/Generators/SoundBankGeneratorService.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Data; +using System.Data; using System.IO; -using System.Linq; using Editors.Audio.AudioEditor.Core; using Editors.Audio.Shared.AudioProject.Models; using Editors.Audio.Shared.GameInformation.Warhammer3; using Editors.Audio.Shared.Storage; using Editors.Audio.Shared.Wwise.Generators.Bkhd; using Editors.Audio.Shared.Wwise.Generators.Hirc; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.Wwise; using Shared.GameFormats.Wwise.Bkhd; diff --git a/Editors/Audio/Shared/Wwise/Generators/WemGeneratorService.cs b/Editors/Audio/Shared/Wwise/Generators/WemGeneratorService.cs index 609c74ab4..dbf87aed2 100644 --- a/Editors/Audio/Shared/Wwise/Generators/WemGeneratorService.cs +++ b/Editors/Audio/Shared/Wwise/Generators/WemGeneratorService.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; using Editors.Audio.Shared.AudioProject.Models; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Utility; diff --git a/Editors/BmdEditor/GlobalUsings.cs b/Editors/BmdEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/BmdEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/BmdEditor/ViewModels/BmdEditorViewModel.cs b/Editors/BmdEditor/ViewModels/BmdEditorViewModel.cs index e58b1f88f..e701745ad 100644 --- a/Editors/BmdEditor/ViewModels/BmdEditorViewModel.cs +++ b/Editors/BmdEditor/ViewModels/BmdEditorViewModel.cs @@ -1,20 +1,21 @@ -using System; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.IO; using System.Windows.Input; using CommunityToolkit.Mvvm.Input; +using Editors.BmdEditor.Services; +using GameWorld.Core.Components; +using GameWorld.Core.Components.Selection; +using GameWorld.Core.Rendering.Materials; +using GameWorld.Core.SceneNodes; +using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; +using Shared.Core.Commands; using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.ToolCreation; using Shared.Core.Services; +using Shared.Core.ToolCreation; using Shared.GameFormats.Bmd; -using GameWorld.Core.Services; -using GameWorld.Core.Rendering.Materials; -using GameWorld.Core.Components; -using GameWorld.Core.Components.Selection; -using GameWorld.Core.SceneNodes; -using Editors.BmdEditor.Services; namespace Editors.BmdEditor.ViewModels { @@ -235,7 +236,7 @@ private void NavigateToReferencedFile(string? fileName) if (referencedFile != null) { // Open the referenced file in the appropriate editor - var openCommand = new Shared.Core.Events.Global.OpenEditorCommand(_editorCreator, _packFileService); + var openCommand = new OpenEditorCommand(_editorCreator, _packFileService); openCommand.Execute(referencedFile); } else diff --git a/Editors/BmdEditor/ViewModels/BmdSceneViewModel.cs b/Editors/BmdEditor/ViewModels/BmdSceneViewModel.cs index ef9f39d78..fe8bd4176 100644 --- a/Editors/BmdEditor/ViewModels/BmdSceneViewModel.cs +++ b/Editors/BmdEditor/ViewModels/BmdSceneViewModel.cs @@ -1,16 +1,16 @@ -using System; +using System; using System.Windows.Input; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.ToolCreation; -using Shared.Core.Services; using Shared.GameFormats.Bmd; using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Services; using Editors.BmdEditor.Services; using Serilog; +using GameWorld.Core.WpfWindow; namespace Editors.BmdEditor.ViewModels { diff --git a/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommand.cs b/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommand.cs index c52f17d10..d0389503a 100644 --- a/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommand.cs +++ b/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommand.cs @@ -1,5 +1,6 @@ using GameWorld.Core.Animation; using Shared.Core.Events; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.GameFormats.Animation; diff --git a/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/GlobalUsings.cs b/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/CampaignAnimationCreator/Editor.CampaignAnimationCreator/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/CampaignAnimationCreator/Test.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommandTests.cs b/Editors/CampaignAnimationCreator/Test.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommandTests.cs index 83f43f5bd..746ef1e48 100644 --- a/Editors/CampaignAnimationCreator/Test.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommandTests.cs +++ b/Editors/CampaignAnimationCreator/Test.CampaignAnimationCreator/CampaignAnimationCreator/Commands/SaveCampaignAnimationCommandTests.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using Moq; using Shared.ByteParsing; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.GameFormats.Animation; using Shared.GameFormats.RigidModel.Transforms; diff --git a/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfExporter.cs b/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfExporter.cs index fff17d7a8..aa7949fd6 100644 --- a/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfExporter.cs +++ b/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfExporter.cs @@ -1,12 +1,8 @@ -using System.Windows; -using Editors.ImportExport.Common; +using System.Windows; using Editors.ImportExport.Exporting.Exporters.RmvToGltf.Helpers; using Editors.ImportExport.Misc; using GameWorld.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; -using Shared.GameFormats.Animation; using Shared.GameFormats.RigidModel; using SharpGLTF.Geometry; using SharpGLTF.Materials; diff --git a/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfStaticExporter.cs b/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfStaticExporter.cs index 3a95e71b2..d3455e3fa 100644 --- a/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfStaticExporter.cs +++ b/Editors/ImportExportEditor/Editors.ImportExport/Exporting/Exporters/RmvToGltf/RmvToGltfStaticExporter.cs @@ -1,10 +1,5 @@ -using System.IO; -using Editors.ImportExport.Common; -using Editors.ImportExport.Exporting.Exporters.RmvToGltf.Helpers; +using Editors.ImportExport.Exporting.Exporters.RmvToGltf.Helpers; using Editors.ImportExport.Misc; -using GameWorld.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.GameFormats.RigidModel; using SharpGLTF.Geometry; diff --git a/Editors/ImportExportEditor/Editors.ImportExport/GlobalUsings.cs b/Editors/ImportExportEditor/Editors.ImportExport/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/ImportExportEditor/Editors.ImportExport/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/ImportExportEditor/Editors.ImportExport/Importing/Importers/GltfToRmv/GltfImporter.cs b/Editors/ImportExportEditor/Editors.ImportExport/Importing/Importers/GltfToRmv/GltfImporter.cs index a26d2c980..5afcb7997 100644 --- a/Editors/ImportExportEditor/Editors.ImportExport/Importing/Importers/GltfToRmv/GltfImporter.cs +++ b/Editors/ImportExportEditor/Editors.ImportExport/Importing/Importers/GltfToRmv/GltfImporter.cs @@ -2,11 +2,7 @@ using CommonControls.BaseDialogs.ErrorListDialog; using Editors.ImportExport.Importing.Importers.GltfToRmv.Helper; using Editors.ImportExport.Misc; -using Editors.Shared.Core.Services; -using GameWorld.Core.SceneNodes; using GameWorld.Core.Services; -using Octokit; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; diff --git a/Editors/ImportExportEditor/Test.ImportExport/Importing/Importers/GltfToRmvImporter/GltfIToRmvImporterTests.cs b/Editors/ImportExportEditor/Test.ImportExport/Importing/Importers/GltfToRmvImporter/GltfIToRmvImporterTests.cs index 90a0513c0..b64d01e00 100644 --- a/Editors/ImportExportEditor/Test.ImportExport/Importing/Importers/GltfToRmvImporter/GltfIToRmvImporterTests.cs +++ b/Editors/ImportExportEditor/Test.ImportExport/Importing/Importers/GltfToRmvImporter/GltfIToRmvImporterTests.cs @@ -59,7 +59,7 @@ public void Test() var sceneLoader = new GltfSceneLoader(standardDialog.Object); var materialBuilder = new RmvMaterialBuilder(pfs, standardDialog.Object); var importer = new GltfImporter(pfs, standardDialog.Object, skeletontonLookupHelper, materialBuilder); - var packFileContainer = new PackFileContainer("new"); + var packFileContainer = PackFileContainer.CreatePackFile("new", "new.pack"); var settings = new GltfImporterSettings(TestData.InputGtlfFile, "skeletons", packFileContainer, Shared.Core.Settings.GameTypeEnum.Warhammer3, true, true, true, true, true, 20.0f, true); // Act @@ -101,7 +101,7 @@ public void TestGltfApiLoad() var materialBuilder = new RmvMaterialBuilder(pfs, standardDialog.Object); var sceneLoader = new GltfSceneLoader(standardDialog.Object); var skeletonFile = skeletontonLookupHelper.GetSkeletonFileFromName(TestData.Rmv2Expected.skeletonName); - var packFileContainer = new PackFileContainer("new"); + var packFileContainer = PackFileContainer.CreatePackFile("new", "new.pack"); var settings = new GltfImporterSettings(TestData.InputGtlfFile, "skeletons", packFileContainer, Shared.Core.Settings.GameTypeEnum.Warhammer3, true, true, true, true, true, 20.0f, true); // Act.... @@ -135,7 +135,7 @@ public void TestRmvMesgBuilder() var materialBuilder = new RmvMaterialBuilder(pfs, standardDialog.Object); var sceneLoader = new GltfSceneLoader(standardDialog.Object); var skeletonFile = skeletontonLookupHelper.GetSkeletonFileFromName(TestData.Rmv2Expected.skeletonName); - var packFileContainer = new PackFileContainer("new"); + var packFileContainer = PackFileContainer.CreatePackFile("new", "new.pack"); var settings = new GltfImporterSettings(TestData.InputGtlfFile, "skeletons", packFileContainer, Shared.Core.Settings.GameTypeEnum.Warhammer3, true, true, true, true, true, 20.0f, true); // Act diff --git a/Editors/Ipc/IpcEditor/AssetEditorIpcServer.cs b/Editors/Ipc/IpcEditor/AssetEditorIpcServer.cs index 023198e6a..0f8e58998 100644 --- a/Editors/Ipc/IpcEditor/AssetEditorIpcServer.cs +++ b/Editors/Ipc/IpcEditor/AssetEditorIpcServer.cs @@ -3,8 +3,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.DependencyInjection; -using Serilog; -using Shared.Core.ErrorHandling; namespace Editors.Ipc { diff --git a/Editors/Ipc/IpcEditor/ExternalFileOpenExecutor.cs b/Editors/Ipc/IpcEditor/ExternalFileOpenExecutor.cs index 5ad1813e2..5ab90f41a 100644 --- a/Editors/Ipc/IpcEditor/ExternalFileOpenExecutor.cs +++ b/Editors/Ipc/IpcEditor/ExternalFileOpenExecutor.cs @@ -5,9 +5,8 @@ using System.Windows; using Editors.KitbasherEditor.UiCommands; using Editors.KitbasherEditor.ViewModels; -using Shared.Core.DependencyInjection; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles.Models; using Shared.Core.ToolCreation; diff --git a/Editors/Ipc/IpcEditor/ExternalPackLoader.cs b/Editors/Ipc/IpcEditor/ExternalPackLoader.cs index c26a745c8..12b887bfd 100644 --- a/Editors/Ipc/IpcEditor/ExternalPackLoader.cs +++ b/Editors/Ipc/IpcEditor/ExternalPackLoader.cs @@ -1,6 +1,4 @@ using System.Windows; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; diff --git a/Editors/Ipc/IpcEditor/GlobalUsings.cs b/Editors/Ipc/IpcEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/Ipc/IpcEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/Ipc/IpcEditor/IpcRequestHandler.cs b/Editors/Ipc/IpcEditor/IpcRequestHandler.cs index 2b252b434..58f0564da 100644 --- a/Editors/Ipc/IpcEditor/IpcRequestHandler.cs +++ b/Editors/Ipc/IpcEditor/IpcRequestHandler.cs @@ -1,7 +1,4 @@ -using Serilog; -using Shared.Core.ErrorHandling; - -namespace Editors.Ipc +namespace Editors.Ipc { public class IpcRequestHandler : IIpcRequestHandler { diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/PinMeshToVertexCommand.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/PinMeshToVertexCommand.cs index af2b8e377..ec366ff4b 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/PinMeshToVertexCommand.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/PinMeshToVertexCommand.cs @@ -1,9 +1,7 @@ -using GameWorld.Core.Commands; -using GameWorld.Core.Components.Selection; +using GameWorld.Core.Components.Selection; using GameWorld.Core.Rendering.Geometry; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/SkinWrapRiggingCommand.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/SkinWrapRiggingCommand.cs index ab145d813..1fa0f89bb 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/SkinWrapRiggingCommand.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/Commands/SkinWrapRiggingCommand.cs @@ -1,8 +1,6 @@ -using GameWorld.Core.Commands; -using GameWorld.Core.Components.Selection; +using GameWorld.Core.Components.Selection; using GameWorld.Core.Rendering.Geometry; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinRiggingAlgorithm.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinRiggingAlgorithm.cs index e5ce3cf8e..5eef7b6c7 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinRiggingAlgorithm.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinRiggingAlgorithm.cs @@ -1,7 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Editors.KitbasherEditor.ChildEditors.PinTool.Commands; -using GameWorld.Core.Commands; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinToolViewModel.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinToolViewModel.cs index f5fa17abc..3d9d02fc3 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinToolViewModel.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/PinToolViewModel.cs @@ -1,11 +1,9 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using GameWorld.Core.Commands; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Services; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/SkinWrapAlgorithm.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/SkinWrapAlgorithm.cs index 0cc591133..d50be6b07 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/SkinWrapAlgorithm.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/PinTool/SkinWrapAlgorithm.cs @@ -2,11 +2,9 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Editors.KitbasherEditor.ChildEditors.PinTool.Commands; -using GameWorld.Core.Commands; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Services; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerViewModel.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerViewModel.cs index e8fb6736d..318705fcf 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerViewModel.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerViewModel.cs @@ -6,7 +6,6 @@ using GameWorld.Core.Rendering.Geometry; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Ui.BaseDialogs.MathViews; diff --git a/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerWindow.xaml.cs b/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerWindow.xaml.cs index e5165a009..1b40d7461 100644 --- a/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerWindow.xaml.cs +++ b/Editors/Kitbashing/KitbasherEditor/ChildEditors/VertexDebugger/VertexDebuggerWindow.xaml.cs @@ -1,5 +1,5 @@ using System.Windows; -using Shared.Core.Services; +using GameWorld.Core.WpfWindow; using WindowHandling; namespace Editors.KitbasherEditor.ChildEditors.VertexDebugger diff --git a/Editors/Kitbashing/KitbasherEditor/Commands/AssignMaterialFromOtherMeshCommand.cs b/Editors/Kitbashing/KitbasherEditor/Commands/AssignMaterialFromOtherMeshCommand.cs index bac174f2c..37af146aa 100644 --- a/Editors/Kitbashing/KitbasherEditor/Commands/AssignMaterialFromOtherMeshCommand.cs +++ b/Editors/Kitbashing/KitbasherEditor/Commands/AssignMaterialFromOtherMeshCommand.cs @@ -1,8 +1,6 @@ using CommunityToolkit.Diagnostics; -using GameWorld.Core.Commands; using GameWorld.Core.Rendering.Materials.Shaders; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/Editors/Kitbashing/KitbasherEditor/Core/KitbasherViewModel.cs b/Editors/Kitbashing/KitbasherEditor/Core/KitbasherViewModel.cs index beee8e0dc..284fa9ec0 100644 --- a/Editors/Kitbashing/KitbasherEditor/Core/KitbasherViewModel.cs +++ b/Editors/Kitbashing/KitbasherEditor/Core/KitbasherViewModel.cs @@ -7,13 +7,12 @@ using Editors.KitbasherEditor.ViewModels.SceneNodeEditor; using GameWorld.Core.Components; using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; using KitbasherEditor.ViewModels.MenuBarViews; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Events.Scoped; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; using Shared.Core.ToolCreation; using Shared.Ui.Common; diff --git a/Editors/Kitbashing/KitbasherEditor/Core/SceneExplorer/MultiSelectTreeView.cs b/Editors/Kitbashing/KitbasherEditor/Core/SceneExplorer/MultiSelectTreeView.cs index 0e50bf68c..be0926868 100644 --- a/Editors/Kitbashing/KitbasherEditor/Core/SceneExplorer/MultiSelectTreeView.cs +++ b/Editors/Kitbashing/KitbasherEditor/Core/SceneExplorer/MultiSelectTreeView.cs @@ -1,5 +1,4 @@ using System.Collections.ObjectModel; -using System.Runtime.Intrinsics.Arm; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -10,8 +9,6 @@ using GameWorld.Core.Components; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; namespace KitbasherEditor.Views diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl.cs index c04354270..06febf720 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl.cs @@ -1,7 +1,8 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl_WH2.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl_WH2.cs index 792a30605..aa63e0279 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl_WH2.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Karl_WH2.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.Settings; diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_MeshFitter.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_MeshFitter.cs index a2d65923c..5f1be9035 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_MeshFitter.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_MeshFitter.cs @@ -1,9 +1,9 @@ using Editors.KitbasherEditor.UiCommands; -using Shared.Core.DependencyInjection; +using Shared.Core.Commands; using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Ox.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Ox.cs index 6fe72502e..365ac6c7e 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Ox.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Ox.cs @@ -1,7 +1,8 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.EmbeddedResources; diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Rat.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Rat.cs index bb6395206..0777151a5 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Rat.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_Rat.cs @@ -1,11 +1,12 @@ using Editors.KitbasherEditor.UiCommands; -using Shared.Core.DependencyInjection; +using Shared.Core.Commands; using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; +using Shared.Core.ToolCreation; using Shared.EmbeddedResources; namespace Editors.KitbasherEditor.DevConfig diff --git a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_RomeShield.cs b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_RomeShield.cs index 012d39321..ffadc966c 100644 --- a/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_RomeShield.cs +++ b/Editors/Kitbashing/KitbasherEditor/DevConfig/Kitbash_RomeShield.cs @@ -1,6 +1,6 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; diff --git a/Editors/Kitbashing/KitbasherEditor/GlobalUsings.cs b/Editors/Kitbashing/KitbasherEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/Kitbashing/KitbasherEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/Kitbashing/KitbasherEditor/Services/KitbashSceneCreator.cs b/Editors/Kitbashing/KitbasherEditor/Services/KitbashSceneCreator.cs index 4c66d2b5e..c09add6cc 100644 --- a/Editors/Kitbashing/KitbasherEditor/Services/KitbashSceneCreator.cs +++ b/Editors/Kitbashing/KitbasherEditor/Services/KitbashSceneCreator.cs @@ -5,7 +5,6 @@ using GameWorld.Core.Services; using GameWorld.Core.Services.SceneSaving; using GameWorld.Core.Utility; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; diff --git a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSaveBase.cs b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSaveBase.cs index dba89ab50..57cd3bca1 100644 --- a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSaveBase.cs +++ b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSaveBase.cs @@ -4,8 +4,8 @@ using GameWorld.Core.Rendering.Materials.Shaders.SpecGloss; using GameWorld.Core.SceneNodes; using GameWorld.Core.Services.SceneSaving; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.MaterialHeaders; diff --git a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_Geometry.cs b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_Geometry.cs index e176980aa..5240d9597 100644 --- a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_Geometry.cs +++ b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_Geometry.cs @@ -1,8 +1,8 @@ using Editors.KitbasherEditor.ChildEditors.SaveDialog; using GameWorld.Core.Services.SceneSaving; using GameWorld.Core.Services.SceneSaving.Lod; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.GameFormats.RigidModel; using Test.TestingUtility.Shared; diff --git a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_WsModel.cs b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_WsModel.cs index 21431f827..cb803cf78 100644 --- a/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_WsModel.cs +++ b/Editors/Kitbashing/Test.KitbashEditor/LoadAndSave/LoadAndSave_WsModel.cs @@ -1,7 +1,7 @@ using Editors.KitbasherEditor.ChildEditors.SaveDialog; using GameWorld.Core.Services.SceneSaving; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.GameFormats.RigidModel; using Test.TestingUtility.Shared; diff --git a/Editors/Kitbashing/Test.KitbashEditor/MeshFitter/MeshFitter_EndToEndTests.cs b/Editors/Kitbashing/Test.KitbashEditor/MeshFitter/MeshFitter_EndToEndTests.cs index 4e341a4e1..3193ffb3e 100644 --- a/Editors/Kitbashing/Test.KitbashEditor/MeshFitter/MeshFitter_EndToEndTests.cs +++ b/Editors/Kitbashing/Test.KitbashEditor/MeshFitter/MeshFitter_EndToEndTests.cs @@ -8,11 +8,11 @@ using GameWorld.Core.Services; using Microsoft.Xna.Framework; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.GameFormats.Animation; using Shared.Ui.Editors.BoneMapping; using Test.KitbashEditor.LoadAndSave; using Test.TestingUtility.Shared; +using Shared.Core.Commands; namespace Test.KitbashEditor.MeshFitter { diff --git a/Editors/MetaDataEditor/AnimationMeta/DevConfig/AnimMetaTool.cs b/Editors/MetaDataEditor/AnimationMeta/DevConfig/AnimMetaTool.cs index 263b437cb..49586c8b1 100644 --- a/Editors/MetaDataEditor/AnimationMeta/DevConfig/AnimMetaTool.cs +++ b/Editors/MetaDataEditor/AnimationMeta/DevConfig/AnimMetaTool.cs @@ -1,5 +1,6 @@ using Shared.Core.DevConfig; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/MetaDataEditor/AnimationMeta/DevConfig/SuperView_Rat.cs b/Editors/MetaDataEditor/AnimationMeta/DevConfig/SuperView_Rat.cs index bdd07184a..5135b3e82 100644 --- a/Editors/MetaDataEditor/AnimationMeta/DevConfig/SuperView_Rat.cs +++ b/Editors/MetaDataEditor/AnimationMeta/DevConfig/SuperView_Rat.cs @@ -2,6 +2,7 @@ using Editors.Shared.Core.Common.BaseControl; using Shared.Core.DevConfig; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/MetaDataEditor/AnimationMeta/GlobalUsings.cs b/Editors/MetaDataEditor/AnimationMeta/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/MetaDataEditor/AnimationMeta/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/MetaDataEditor/AnimationMeta/MetaEditor/AttributeViewModel.cs b/Editors/MetaDataEditor/AnimationMeta/MetaEditor/AttributeViewModel.cs index 482124fd4..c2b7d5460 100644 --- a/Editors/MetaDataEditor/AnimationMeta/MetaEditor/AttributeViewModel.cs +++ b/Editors/MetaDataEditor/AnimationMeta/MetaEditor/AttributeViewModel.cs @@ -2,9 +2,7 @@ using CommunityToolkit.Diagnostics; using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.Xna.Framework; -using Serilog; using Shared.ByteParsing.Parsers; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Misc; using Shared.Ui.BaseDialogs.MathViews; diff --git a/Editors/MetaDataEditor/AnimationMeta/MetaEditor/Commands/SaveCommand.cs b/Editors/MetaDataEditor/AnimationMeta/MetaEditor/Commands/SaveCommand.cs index a6f986b20..0286a9d3a 100644 --- a/Editors/MetaDataEditor/AnimationMeta/MetaEditor/Commands/SaveCommand.cs +++ b/Editors/MetaDataEditor/AnimationMeta/MetaEditor/Commands/SaveCommand.cs @@ -1,10 +1,9 @@ using CommunityToolkit.Diagnostics; using Editors.AnimationMeta.Presentation; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Events.Scoped; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.GameFormats.AnimationMeta.Parsing; diff --git a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Instances/DrawableMetaInstance.cs b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Instances/DrawableMetaInstance.cs index e5056c8d2..a5b895540 100644 --- a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Instances/DrawableMetaInstance.cs +++ b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Instances/DrawableMetaInstance.cs @@ -2,8 +2,6 @@ using GameWorld.Core.Animation; using GameWorld.Core.SceneNodes; using GameWorld.Core.Utility; -using Serilog; -using Shared.Core.ErrorHandling; namespace Editors.AnimationMeta.SuperView.Visualisation.Instances { diff --git a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/MetaDataBuilder.cs b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/MetaDataBuilder.cs index 225687fa8..6b55b7efe 100644 --- a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/MetaDataBuilder.cs +++ b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/MetaDataBuilder.cs @@ -10,8 +10,6 @@ using GameWorld.Core.SceneNodes; using GameWorld.Core.Services; using Microsoft.Xna.Framework; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.GameFormats.Animation; diff --git a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/CopyRootTransform.cs b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/CopyRootTransform.cs index fa912b81f..2fbae8411 100644 --- a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/CopyRootTransform.cs +++ b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/CopyRootTransform.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.ObjectModel; -using GameWorld.Core.Animation; +using GameWorld.Core.Animation; using GameWorld.Core.Animation.AnimationChange; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; -using Shared.Core.ErrorHandling; namespace Editors.AnimationMeta.SuperView.Visualisation.Rules { diff --git a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/DockEquipmentRule.cs b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/DockEquipmentRule.cs index 478f18dec..9130e32af 100644 --- a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/DockEquipmentRule.cs +++ b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/DockEquipmentRule.cs @@ -2,9 +2,6 @@ using GameWorld.Core.Animation.AnimationChange; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; -using Shared.Core.ErrorHandling; -using System; namespace Editors.AnimationMeta.SuperView.Visualisation.Rules { diff --git a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/TransformBoneRule.cs b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/TransformBoneRule.cs index 6bd55c046..7763d3735 100644 --- a/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/TransformBoneRule.cs +++ b/Editors/MetaDataEditor/AnimationMeta/SuperView/Visualisation/Rules/TransformBoneRule.cs @@ -1,9 +1,6 @@ -using System; -using GameWorld.Core.Animation; +using GameWorld.Core.Animation; using GameWorld.Core.Animation.AnimationChange; using Microsoft.Xna.Framework; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.GameFormats.AnimationMeta.Definitions; namespace Editors.AnimationMeta.SuperView.Visualisation.Rules diff --git a/Editors/Reports/Animation/AnimMetaDataJsonGenerator.cs b/Editors/Reports/Animation/AnimMetaDataJsonGenerator.cs index cb659d6ed..6b3cd53bf 100644 --- a/Editors/Reports/Animation/AnimMetaDataJsonGenerator.cs +++ b/Editors/Reports/Animation/AnimMetaDataJsonGenerator.cs @@ -2,8 +2,6 @@ using System.Windows; using Editors.AnimationFragmentEditor.AnimationPack.Converters.AnimationBinWh3Converter; using Newtonsoft.Json; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Misc; using Shared.Core.PackFiles; diff --git a/Editors/Reports/Animation/AnimMetaDataReportGenerator.cs b/Editors/Reports/Animation/AnimMetaDataReportGenerator.cs index 74f8596bc..1dffc1a1d 100644 --- a/Editors/Reports/Animation/AnimMetaDataReportGenerator.cs +++ b/Editors/Reports/Animation/AnimMetaDataReportGenerator.cs @@ -2,8 +2,6 @@ using System.Globalization; using System.Windows; using CsvHelper; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Misc; using Shared.Core.PackFiles; diff --git a/Editors/Reports/DeepSearch/DeepSearchReport.cs b/Editors/Reports/DeepSearch/DeepSearchReport.cs index 3110236f0..1bceebc2b 100644 --- a/Editors/Reports/DeepSearch/DeepSearchReport.cs +++ b/Editors/Reports/DeepSearch/DeepSearchReport.cs @@ -1,16 +1,13 @@ using System.Text; using CommonControls.BaseDialogs; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; -using Shared.Core.PackFiles.Serialization; using Shared.Core.PackFiles.Utility; namespace Editors.Reports.DeepSearch { - public class DeepSearchCommand(DeepSearchReport deepSearchReport) : IAeCommand { public void Execute() diff --git a/Editors/Reports/Files/FileListReportGenerator.cs b/Editors/Reports/Files/FileListReportGenerator.cs index b152ebe70..f01e8fbde 100644 --- a/Editors/Reports/Files/FileListReportGenerator.cs +++ b/Editors/Reports/Files/FileListReportGenerator.cs @@ -3,8 +3,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Windows; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Misc; using Shared.Core.PackFiles; diff --git a/Editors/Reports/GlobalUsings.cs b/Editors/Reports/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/Reports/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHost.cs b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHost.cs index 3d0e58104..74cd40bf2 100644 --- a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHost.cs +++ b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHost.cs @@ -6,10 +6,10 @@ using Editors.Shared.Core.Common.ReferenceModel; using GameWorld.Core.Components; using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; using GameWorld.Core.WpfWindow.Events; using Shared.Core.Events; using Shared.Core.Misc; -using Shared.Core.Services; using Shared.Core.ToolCreation; using ICommand = System.Windows.Input.ICommand; diff --git a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHostBase.cs b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHostBase.cs index bc7d1d13a..6fc370ca0 100644 --- a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHostBase.cs +++ b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/EditorHostBase.cs @@ -5,7 +5,7 @@ using Editors.Shared.Core.Common.AnimationPlayer; using Editors.Shared.Core.Common.ReferenceModel; using GameWorld.Core.Services; -using Shared.Core.Services; +using GameWorld.Core.WpfWindow; using Shared.Core.ToolCreation; namespace Editors.Shared.Core.Common.BaseControl diff --git a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/IEditorHostParameters.cs b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/IEditorHostParameters.cs index 28893ad5e..696448e47 100644 --- a/Editors/Shared/Editors.Shared.Core/Common/BaseControl/IEditorHostParameters.cs +++ b/Editors/Shared/Editors.Shared.Core/Common/BaseControl/IEditorHostParameters.cs @@ -1,7 +1,7 @@ using Editors.Shared.Core.Common.AnimationPlayer; using GameWorld.Core.Components; using GameWorld.Core.Services; -using Shared.Core.Services; +using GameWorld.Core.WpfWindow; namespace Editors.Shared.Core.Common.BaseControl { diff --git a/Editors/Shared/Editors.Shared.Core/Common/ReferenceModel/BinAnimationViewModel.cs b/Editors/Shared/Editors.Shared.Core/Common/ReferenceModel/BinAnimationViewModel.cs index 5a63a1528..3f3402578 100644 --- a/Editors/Shared/Editors.Shared.Core/Common/ReferenceModel/BinAnimationViewModel.cs +++ b/Editors/Shared/Editors.Shared.Core/Common/ReferenceModel/BinAnimationViewModel.cs @@ -3,8 +3,8 @@ using CommunityToolkit.Mvvm.ComponentModel; using GameWorld.Core.Animation; using GameWorld.Core.Services; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; diff --git a/Editors/Shared/Editors.Shared.Core/Common/SceneObjectEditor.cs b/Editors/Shared/Editors.Shared.Core/Common/SceneObjectEditor.cs index 9c0e0b4bb..d6ba256ad 100644 --- a/Editors/Shared/Editors.Shared.Core/Common/SceneObjectEditor.cs +++ b/Editors/Shared/Editors.Shared.Core/Common/SceneObjectEditor.cs @@ -3,13 +3,11 @@ using GameWorld.Core.SceneNodes; using GameWorld.Core.Services; using GameWorld.Core.Utility; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; using Shared.GameFormats.Animation; namespace Editors.Shared.Core.Common diff --git a/Editors/Shared/Editors.Shared.Core/Editors/TextEditor/TextEditorViewModel.cs b/Editors/Shared/Editors.Shared.Core/Editors/TextEditor/TextEditorViewModel.cs index b880fc7ef..ec1518b41 100644 --- a/Editors/Shared/Editors.Shared.Core/Editors/TextEditor/TextEditorViewModel.cs +++ b/Editors/Shared/Editors.Shared.Core/Editors/TextEditor/TextEditorViewModel.cs @@ -5,7 +5,7 @@ using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.ToolCreation; namespace Shared.Ui.Editors.TextEditor diff --git a/Editors/Shared/Editors.Shared.Core/GlobalUsings.cs b/Editors/Shared/Editors.Shared.Core/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/Shared/Editors.Shared.Core/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/Shared/Editors.Shared.Core/Services/TextureConvWrapper.cs b/Editors/Shared/Editors.Shared.Core/Services/TextureConvWrapper.cs index a30baeee7..82d67eeb7 100644 --- a/Editors/Shared/Editors.Shared.Core/Services/TextureConvWrapper.cs +++ b/Editors/Shared/Editors.Shared.Core/Services/TextureConvWrapper.cs @@ -1,7 +1,5 @@ using System.IO; using System.Reflection; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.GameFormats.RigidModel.Types; diff --git a/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/DevConfig/SkeletonTool.cs b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/DevConfig/SkeletonTool.cs index 2e2c144f1..d663c7ca6 100644 --- a/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/DevConfig/SkeletonTool.cs +++ b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/DevConfig/SkeletonTool.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Diagnostics; using Shared.Core.DevConfig; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.Core.ToolCreation; diff --git a/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/GlobalUsings.cs b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/SkeletonEditor/SkeletonEditorViewModel.cs b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/SkeletonEditor/SkeletonEditorViewModel.cs index db74f4bc4..fa70672a5 100644 --- a/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/SkeletonEditor/SkeletonEditorViewModel.cs +++ b/Editors/SkeletonEditor/Editor.VisualSkeletonEditor/SkeletonEditor/SkeletonEditorViewModel.cs @@ -10,6 +10,7 @@ using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.Core.ToolCreation; using Shared.GameFormats.Animation; diff --git a/Editors/SkeletonEditor/Test.SkeletonEditor/SkeletonTool_ComplexUsecase.cs b/Editors/SkeletonEditor/Test.SkeletonEditor/SkeletonTool_ComplexUsecase.cs index 854ddbdec..039cc634f 100644 --- a/Editors/SkeletonEditor/Test.SkeletonEditor/SkeletonTool_ComplexUsecase.cs +++ b/Editors/SkeletonEditor/Test.SkeletonEditor/SkeletonTool_ComplexUsecase.cs @@ -1,7 +1,7 @@ using Editor.VisualSkeletonEditor.SkeletonEditor; using Editors.Shared.Core.Common.ReferenceModel; using Moq; -using Shared.Core.Events.Global; +using Shared.Core.Commands; using Shared.Core.Services; using Shared.Core.ToolCreation; using Shared.GameFormats.Animation; diff --git a/Editors/TextureEditor/DevConfig/Texture_Karl.cs b/Editors/TextureEditor/DevConfig/Texture_Karl.cs index a520503ea..fd0205213 100644 --- a/Editors/TextureEditor/DevConfig/Texture_Karl.cs +++ b/Editors/TextureEditor/DevConfig/Texture_Karl.cs @@ -1,7 +1,8 @@ -using Shared.Core.DevConfig; +using Shared.Core.Commands; +using Shared.Core.DevConfig; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.EmbeddedResources; diff --git a/Editors/TextureEditor/ViewModels/TextureBuilder.cs b/Editors/TextureEditor/ViewModels/TextureBuilder.cs index 35171743d..b693fed39 100644 --- a/Editors/TextureEditor/ViewModels/TextureBuilder.cs +++ b/Editors/TextureEditor/ViewModels/TextureBuilder.cs @@ -7,8 +7,8 @@ using System.Windows.Media.Imaging; using GameWorld.Core.Rendering; using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework.Graphics; -using Shared.Core.Services; namespace Editors.TextureEditor.ViewModels { diff --git a/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiPreviewBuilder.cs b/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiPreviewBuilder.cs index 4e22e0ad9..ae66a9608 100644 --- a/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiPreviewBuilder.cs +++ b/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiPreviewBuilder.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Shared.Core.Services; using Shared.GameFormats.Twui.Data; //https://github.com/Apostolique/Apos.Gui/blob/main/Source/Dock.cs diff --git a/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiRenderComponent.cs b/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiRenderComponent.cs index d9e72478f..2f94810f6 100644 --- a/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiRenderComponent.cs +++ b/Editors/TwuiEditor/Editor.Twui/Editor/Rendering/TwuiRenderComponent.cs @@ -1,9 +1,9 @@ using GameWorld.Core.Components; using GameWorld.Core.Services; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Shared.Core.Events; -using Shared.Core.Services; //https://github.com/Apostolique/Apos.Gui/blob/main/Source/Dock.cs diff --git a/Editors/TwuiEditor/Editor.Twui/Editor/TwuiEditor.cs b/Editors/TwuiEditor/Editor.Twui/Editor/TwuiEditor.cs index 3074fc258..514b61650 100644 --- a/Editors/TwuiEditor/Editor.Twui/Editor/TwuiEditor.cs +++ b/Editors/TwuiEditor/Editor.Twui/Editor/TwuiEditor.cs @@ -2,10 +2,10 @@ using CommunityToolkit.Mvvm.ComponentModel; using Editors.Twui.Editor.ComponentEditor; using Editors.Twui.Editor.Rendering; +using GameWorld.Core.WpfWindow; using Shared.Core.Events; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; using Shared.Core.ToolCreation; namespace Editors.Twui.Editor diff --git a/Editors/TwuiEditor/Editor.Twui/GlobalUsings.cs b/Editors/TwuiEditor/Editor.Twui/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Editors/TwuiEditor/Editor.Twui/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Animation/AnimationSampler.cs b/GameWorld/GameWorldCore/GameWorld.Core/Animation/AnimationSampler.cs index dbe045ce1..2e6408fbc 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Animation/AnimationSampler.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Animation/AnimationSampler.cs @@ -1,6 +1,5 @@ using GameWorld.Core.Animation.AnimationChange; using Microsoft.Xna.Framework; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; namespace GameWorld.Core.Animation diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Face/FaceSelectionCommand.cs b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Face/FaceSelectionCommand.cs index 894bd23ad..524309889 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Face/FaceSelectionCommand.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Face/FaceSelectionCommand.cs @@ -1,5 +1,4 @@ using GameWorld.Core.Components.Selection; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DivideObjectIntoSubmeshesCommand.cs b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DivideObjectIntoSubmeshesCommand.cs index 57cffcaba..32e9c796b 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DivideObjectIntoSubmeshesCommand.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DivideObjectIntoSubmeshesCommand.cs @@ -1,13 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using GameWorld.Core.Commands.Face; -using GameWorld.Core.Components.Selection; +using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; using GameWorld.Core.Services; -using Serilog; using Shared.Core.ErrorHandling; -using Shared.Ui.Common; using Shared.Core.Events; +using Shared.Ui.Common; namespace GameWorld.Core.Commands.Object { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DuplicateObjectCommand.cs b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DuplicateObjectCommand.cs index bf1277927..ba4e9151d 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DuplicateObjectCommand.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/DuplicateObjectCommand.cs @@ -1,7 +1,5 @@ -using GameWorld.Core.Commands.Face; -using GameWorld.Core.Components.Selection; +using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/ObjectSelectionCommand.cs b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/ObjectSelectionCommand.cs index 15ba97b28..e4c8bcc60 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/ObjectSelectionCommand.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Object/ObjectSelectionCommand.cs @@ -1,7 +1,6 @@ using CommunityToolkit.Diagnostics; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Vertex/VertexSelectionCommand.cs b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Vertex/VertexSelectionCommand.cs index cf3b95841..b9ca75854 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Commands/Vertex/VertexSelectionCommand.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Commands/Vertex/VertexSelectionCommand.cs @@ -1,8 +1,5 @@ -using GameWorld.Core.Commands; -using GameWorld.Core.Components.Selection; -using Serilog; +using GameWorld.Core.Components.Selection; using Shared.Core.ErrorHandling; -using System.Collections.Generic; using Shared.Core.Events; namespace GameWorld.Core.Commands.Vertex diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Components/ComponentInserter.cs b/GameWorld/GameWorldCore/GameWorld.Core/Components/ComponentInserter.cs index b80a6239b..b8807e472 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Components/ComponentInserter.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Components/ComponentInserter.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework; -using Shared.Core.Services; namespace GameWorld.Core.Components { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/KeyboardComponent.cs b/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/KeyboardComponent.cs index 4aff8ca49..e3540eaea 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/KeyboardComponent.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/KeyboardComponent.cs @@ -1,7 +1,7 @@ -using GameWorld.Core.WpfWindow.Input; +using GameWorld.Core.WpfWindow; +using GameWorld.Core.WpfWindow.Input; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; -using Shared.Core.Services; namespace GameWorld.Core.Components.Input { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/MouseComponent.cs b/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/MouseComponent.cs index 3531346fb..ff06b4adf 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/MouseComponent.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Components/Input/MouseComponent.cs @@ -1,9 +1,8 @@ -using GameWorld.Core.WpfWindow.Input; +using GameWorld.Core.WpfWindow; +using GameWorld.Core.WpfWindow.Input; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; -using Serilog; using Shared.Core.ErrorHandling; -using Shared.Core.Services; namespace GameWorld.Core.Components.Input { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Components/Rendering/RenderEngineComponent.cs b/GameWorld/GameWorldCore/GameWorld.Core/Components/Rendering/RenderEngineComponent.cs index 7e67de866..dedc299b7 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Components/Rendering/RenderEngineComponent.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Components/Rendering/RenderEngineComponent.cs @@ -4,13 +4,12 @@ using GameWorld.Core.Rendering; using GameWorld.Core.Services; using GameWorld.Core.Utility; +using GameWorld.Core.WpfWindow; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; using Shared.Core.Misc; -using Shared.Core.Services; using Shared.Core.Settings; namespace GameWorld.Core.Components.Rendering diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Components/SceneManager.cs b/GameWorld/GameWorldCore/GameWorld.Core/Components/SceneManager.cs index 998d9a7d2..8a3793ccd 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Components/SceneManager.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Components/SceneManager.cs @@ -1,14 +1,10 @@ -using GameWorld.Core.Components.Rendering; +using System.Text; +using GameWorld.Core.Components.Rendering; using GameWorld.Core.SceneNodes; using GameWorld.Core.Utility; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace GameWorld.Core.Components { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/DependencyInjectionContainer.cs b/GameWorld/GameWorldCore/GameWorld.Core/DependencyInjectionContainer.cs index 18ccea328..92cc4715b 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/DependencyInjectionContainer.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/DependencyInjectionContainer.cs @@ -30,7 +30,7 @@ using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling; using Shared.Core.ErrorHandling.Exceptions; -using Shared.Core.Services; +using Shared.Core.ToolCreation; namespace GameWorld.Core { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/GlobalUsings.cs b/GameWorld/GameWorldCore/GameWorld.Core/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/GameWorld/GameWorldCore/GameWorld.Core/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/MaterialToWsMaterialSerializer.cs b/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/MaterialToWsMaterialSerializer.cs index e36144ccd..2d2e9dc63 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/MaterialToWsMaterialSerializer.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/MaterialToWsMaterialSerializer.cs @@ -2,7 +2,7 @@ using System.Text; using GameWorld.Core.Rendering.Materials.Shaders; using Shared.Core.PackFiles; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/WsMaterialRepository.cs b/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/WsMaterialRepository.cs index a24fdeb8d..240193a80 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/WsMaterialRepository.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Rendering/Materials/Serialization/WsMaterialRepository.cs @@ -1,9 +1,5 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; using System.Text.RegularExpressions; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Utility; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/SceneNodes/Rmv2ModelNodeLoader.cs b/GameWorld/GameWorldCore/GameWorld.Core/SceneNodes/Rmv2ModelNodeLoader.cs index d201c43dc..4b87bacd4 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/SceneNodes/Rmv2ModelNodeLoader.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/SceneNodes/Rmv2ModelNodeLoader.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Services; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.Services; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/ComplexMeshLoader.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/ComplexMeshLoader.cs index 8f0e6818a..d99a145e0 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/ComplexMeshLoader.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/ComplexMeshLoader.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using System.Linq; +using System.IO; using GameWorld.Core.Animation; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/FocusSelectableObjectService.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/FocusSelectableObjectService.cs index b14044add..21de1f790 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/FocusSelectableObjectService.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/FocusSelectableObjectService.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using GameWorld.Core.Components; +using GameWorld.Core.Components; using GameWorld.Core.Components.Rendering; using GameWorld.Core.Components.Selection; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; namespace GameWorld.Core.Services diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/GraphicsResourceCreator.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/GraphicsResourceCreator.cs index bb632246a..067899d5a 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/GraphicsResourceCreator.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/GraphicsResourceCreator.cs @@ -2,9 +2,8 @@ using System.Runtime.CompilerServices; using System.Text; using Microsoft.Xna.Framework.Graphics; -using Serilog; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling; +using Shared.Core.ToolCreation; namespace GameWorld.Core.Services { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/ResourceLibary.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/ResourceLibary.cs index b150dfd11..7ff80a9d8 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/ResourceLibary.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/ResourceLibary.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using GameWorld.Core.Utility; +using GameWorld.Core.Utility; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; namespace GameWorld.Core.Services diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Geometry/NodeToRmvSaveHelper.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Geometry/NodeToRmvSaveHelper.cs index fba8a4c3a..fd698421a 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Geometry/NodeToRmvSaveHelper.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Geometry/NodeToRmvSaveHelper.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; using System.Windows; using GameWorld.Core.Animation; using GameWorld.Core.Rendering.Geometry; @@ -9,9 +6,8 @@ using GameWorld.Core.Rendering.Materials.Shaders; using GameWorld.Core.SceneNodes; using Microsoft.Xna.Framework; -using Serilog; using Shared.Core.ErrorHandling; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.LodHeader; using Shared.GameFormats.RigidModel.Types; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Lod/Strategies/LodGeneratorBase.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Lod/Strategies/LodGeneratorBase.cs index 3fa809654..e49fba8ae 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Lod/Strategies/LodGeneratorBase.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Lod/Strategies/LodGeneratorBase.cs @@ -1,7 +1,6 @@ using GameWorld.Core.Rendering.Materials.Capabilities; using GameWorld.Core.SceneNodes; using GameWorld.Core.Utility; -using Serilog; using Shared.Core.ErrorHandling; using Shared.GameFormats.RigidModel; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Material/WsModelGeneratorService.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Material/WsModelGeneratorService.cs index 1297e9ec5..e89b9afe7 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Material/WsModelGeneratorService.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/SceneSaving/Material/WsModelGeneratorService.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; using System.Text; using System.Windows; using GameWorld.Core.Rendering.Materials.Serialization; using GameWorld.Core.Rendering.Materials.Shaders; using GameWorld.Core.SceneNodes; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.GameFormats.RigidModel; namespace GameWorld.Core.Services.SceneSaving.Material diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/ScopedResourceLibrary.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/ScopedResourceLibrary.cs index f8d90ae21..d38d5e920 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/ScopedResourceLibrary.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/ScopedResourceLibrary.cs @@ -2,7 +2,7 @@ using GameWorld.Core.WpfWindow.Events; using Microsoft.Xna.Framework.Graphics; using Shared.Core.Events; -using Shared.Core.Events.Global; +using Shared.Core.PackFiles.Events; using Shared.Core.Services; namespace GameWorld.Core.Services diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Services/SkeletonAnimationLookUpHelper.cs b/GameWorld/GameWorldCore/GameWorld.Core/Services/SkeletonAnimationLookUpHelper.cs index c2b37a92b..be843a89e 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Services/SkeletonAnimationLookUpHelper.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Services/SkeletonAnimationLookUpHelper.cs @@ -1,17 +1,15 @@ -using System.Collections.ObjectModel; -using System.Collections.Concurrent; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; -using System.Threading.Tasks; using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.PackFiles.Utility; using Shared.GameFormats.Animation; -using Shared.Core.PackFiles.Models.FileSources; namespace GameWorld.Core.Services { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Utility/DeviceResolver.cs b/GameWorld/GameWorldCore/GameWorld.Core/Utility/DeviceResolver.cs index 45feba288..1a954423d 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Utility/DeviceResolver.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Utility/DeviceResolver.cs @@ -1,5 +1,5 @@ -using Microsoft.Xna.Framework.Graphics; -using Shared.Core.Services; +using GameWorld.Core.WpfWindow; +using Microsoft.Xna.Framework.Graphics; namespace GameWorld.Core.Utility { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Utility/GraphicsResourceExceptionInfoProvider.cs b/GameWorld/GameWorldCore/GameWorld.Core/Utility/GraphicsResourceExceptionInfoProvider.cs index d0db998ac..c99764e8a 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Utility/GraphicsResourceExceptionInfoProvider.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Utility/GraphicsResourceExceptionInfoProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using GameWorld.Core.Services; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling.Exceptions; using Shared.Core.ToolCreation; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Utility/ImageLoader.cs b/GameWorld/GameWorldCore/GameWorld.Core/Utility/ImageLoader.cs index ec1754e9b..cbf917ad5 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Utility/ImageLoader.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Utility/ImageLoader.cs @@ -2,8 +2,6 @@ using GameWorld.Core.Services; using Microsoft.Xna.Framework.Graphics; using Pfim; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; namespace GameWorld.Core.Utility diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Utility/TextureConverter.cs b/GameWorld/GameWorldCore/GameWorld.Core/Utility/TextureConverter.cs index 4e3977179..1cadd6e9e 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Utility/TextureConverter.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Utility/TextureConverter.cs @@ -1,11 +1,6 @@ -using System; -using System.IO; +using System.IO; using System.Reflection; -using System.Threading; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; -using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.GameFormats.RigidModel.Types; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/Utility/UserInterface/ShaderTextureViewModel.cs b/GameWorld/GameWorldCore/GameWorld.Core/Utility/UserInterface/ShaderTextureViewModel.cs index 48c42390d..f1d6a2fc8 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/Utility/UserInterface/ShaderTextureViewModel.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/Utility/UserInterface/ShaderTextureViewModel.cs @@ -8,8 +8,8 @@ using CommunityToolkit.Mvvm.Input; using GameWorld.Core.Rendering.Materials.Capabilities.Utility; using GameWorld.Core.Services; +using Shared.Core.Commands; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; using Shared.Core.Services; diff --git a/Shared/SharedCore/Shared.Core/Services/IWpfGame.cs b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/IWpfGame.cs similarity index 93% rename from Shared/SharedCore/Shared.Core/Services/IWpfGame.cs rename to GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/IWpfGame.cs index b1442b4d7..180812f32 100644 --- a/Shared/SharedCore/Shared.Core/Services/IWpfGame.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/IWpfGame.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; -namespace Shared.Core.Services +namespace GameWorld.Core.WpfWindow { public interface IWpfGame { diff --git a/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfKeyboard.cs b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfKeyboard.cs index 589314a36..342c59b80 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfKeyboard.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfKeyboard.cs @@ -1,6 +1,4 @@ -using GameWorld.Core.WpfWindow; -using Microsoft.Xna.Framework.Input; -using Shared.Core.Services; +using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfMouse.cs b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfMouse.cs index c8f4eee27..ddc74745a 100644 --- a/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfMouse.cs +++ b/GameWorld/GameWorldCore/GameWorld.Core/WpfWindow/Input/WpfMouse.cs @@ -6,7 +6,6 @@ using System.Windows.Media; using GameWorld.Core.WpfWindow.Internals; using Microsoft.Xna.Framework.Input; -using Shared.Core.Services; namespace GameWorld.Core.WpfWindow.Input { diff --git a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Materials/Serialization/MaterialToWsMaterialSerializerTests.cs b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Materials/Serialization/MaterialToWsMaterialSerializerTests.cs index f2a60f31d..199992ecc 100644 --- a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Materials/Serialization/MaterialToWsMaterialSerializerTests.cs +++ b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Materials/Serialization/MaterialToWsMaterialSerializerTests.cs @@ -1,16 +1,16 @@ -using Test.TestingUtility.TestUtility; -using GameWorld.Core.Rendering.Materials; +using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Rendering.Materials.Capabilities; using GameWorld.Core.Rendering.Materials.Serialization; using GameWorld.Core.Rendering.Materials.Shaders; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.Types; using Shared.GameFormats.WsModel; using Shared.TestUtility; +using Test.TestingUtility.TestUtility; namespace GameWorld.Core.Test.Rendering.Materials.Serialization { diff --git a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/DefaultMaterialTest.cs b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/DefaultMaterialTest.cs index 1d8b2f71f..bad114105 100644 --- a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/DefaultMaterialTest.cs +++ b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/DefaultMaterialTest.cs @@ -1,5 +1,4 @@ -using Test.TestingUtility.TestUtility; -using GameWorld.Core.Rendering.Materials; +using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Rendering.Materials.Capabilities; using GameWorld.Core.Rendering.Materials.Serialization; using GameWorld.Core.Rendering.Materials.Shaders; @@ -8,12 +7,13 @@ using Microsoft.Xna.Framework; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.MaterialHeaders; using Shared.GameFormats.RigidModel.Types; using Shared.GameFormats.WsModel; +using Test.TestingUtility.TestUtility; namespace GameWorld.Core.Test.Rendering.Shaders.MetalRough { diff --git a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/EmissiveMaterialTest.cs b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/EmissiveMaterialTest.cs index bf301062f..1803bbb56 100644 --- a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/EmissiveMaterialTest.cs +++ b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/MetalRough/EmissiveMaterialTest.cs @@ -1,16 +1,16 @@ -using Test.TestingUtility.TestUtility; -using GameWorld.Core.Rendering.Materials; +using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Rendering.Materials.Capabilities; using GameWorld.Core.Rendering.Materials.Serialization; using GameWorld.Core.Test.TestUtility.Material; using Microsoft.Xna.Framework; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.Types; using Shared.GameFormats.WsModel; +using Test.TestingUtility.TestUtility; namespace GameWorld.Core.Test.Rendering.Shaders.MetalRough { diff --git a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/SpecGloss/DefaultMaterialTests.cs b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/SpecGloss/DefaultMaterialTests.cs index 1d5eb10c2..d235ac14d 100644 --- a/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/SpecGloss/DefaultMaterialTests.cs +++ b/GameWorld/GameWorldCore/GameWorld.CoreTest/Rendering/Shaders/SpecGloss/DefaultMaterialTests.cs @@ -1,17 +1,17 @@ -using Test.TestingUtility.TestUtility; -using GameWorld.Core.Rendering.Materials; +using GameWorld.Core.Rendering.Materials; using GameWorld.Core.Rendering.Materials.Capabilities; using GameWorld.Core.Rendering.Materials.Serialization; using GameWorld.Core.Rendering.Materials.Shaders; using GameWorld.Core.Test.TestUtility; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using Shared.Core.Services; +using Shared.Core.PackFiles.Utility; using Shared.Core.Settings; using Shared.GameFormats.RigidModel; using Shared.GameFormats.RigidModel.MaterialHeaders; using Shared.GameFormats.RigidModel.Types; using Shared.GameFormats.WsModel; +using Test.TestingUtility.TestUtility; namespace GameWorld.Core.Test.Rendering.Shaders.SpecGloss { diff --git a/GameWorld/GameWorldCore/GameWorld.CoreTest/Services/SkeletonAnimationLookUpHelperKarlPackTests.cs b/GameWorld/GameWorldCore/GameWorld.CoreTest/Services/SkeletonAnimationLookUpHelperKarlPackTests.cs index b768714de..5281622ff 100644 --- a/GameWorld/GameWorldCore/GameWorld.CoreTest/Services/SkeletonAnimationLookUpHelperKarlPackTests.cs +++ b/GameWorld/GameWorldCore/GameWorld.CoreTest/Services/SkeletonAnimationLookUpHelperKarlPackTests.cs @@ -5,6 +5,7 @@ using Shared.Core.Events; using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Serialization.CacheDatabase; using Shared.Core.PackFiles.Utility; @@ -33,7 +34,7 @@ public void LoadAndUnload_KarlPack_UpdatesSkeletonAndAnimationLookup() .Returns(() => containers.ToList()); var settingsService = new ApplicationSettingsService(GameTypeEnum.Warhammer3); - var loader = new PackFileContainerLoader(settingsService, new Mock().Object, new LocalizationManager(), new PackFileContainerCacheHelper()); + var loader = new PackFileContainerLoader(settingsService, new Mock().Object, new LocalizationManager(), new PackFileContainerCacheHelper(), new SimpleSystemFolderContainerFactory()); var karlPackPath = PathHelper.GetDataFile("Karl_and_celestialgeneral.pack"); var karlContainer = loader.CreateFromPackFile(PackFileContainerType.Normal, karlPackPath, false); diff --git a/Shared/EmbeddedResources/IconLibrary.cs b/Shared/EmbeddedResources/IconLibrary.cs index 0559a172b..fbf7c9ad8 100644 --- a/Shared/EmbeddedResources/IconLibrary.cs +++ b/Shared/EmbeddedResources/IconLibrary.cs @@ -9,10 +9,15 @@ namespace Shared.EmbeddedResources public static class IconLibrary { [AllowNull] public static BitmapImage FolderIcon { get; private set; } - [AllowNull]public static BitmapImage CollectionIcon { get; private set; } [AllowNull]public static BitmapImage FileIcon { get; private set; } [AllowNull] public static BitmapImage LockIcon { get; private set; } + // Container/root-node icons, selected by pack-file type. + [AllowNull] public static BitmapImage NormalModPackIcon { get; private set; } + [AllowNull] public static BitmapImage SystemFolderModPackIcon { get; private set; } + [AllowNull] public static BitmapImage DatabaseModPackIcon { get; private set; } + [AllowNull] public static BitmapImage MissingIcon { get => VertexDebuggerIcon; } + [AllowNull]public static BitmapImage VmdIcon { get; private set; } [AllowNull]public static BitmapImage Rmv2ModelIcon { get; private set; } [AllowNull]public static BitmapImage MeshIcon { get; private set; } @@ -63,10 +68,14 @@ public static class IconLibrary public static void Load() { FolderIcon = BitmapToImageSource("icons8-folder-48.png"); - CollectionIcon = BitmapToImageSource("icons8-collectibles-48.png"); FileIcon = BitmapToImageSource("icons8-file-48.png"); LockIcon = BitmapToImageSource("icons8-lock-50.png"); + // TODO: replace these placeholders with dedicated pack-type icon assets. + NormalModPackIcon = BitmapToImageSource("icons8-collectibles-48.png"); + SystemFolderModPackIcon = BitmapToImageSource("icons8-computer-40.png"); + DatabaseModPackIcon = BitmapToImageSource("icons8-database-2-48.png"); + VmdIcon = BitmapToImageSource("icons8-man-50.png"); Rmv2ModelIcon = BitmapToImageSource("icons8-orthogonal-view-50.png"); MeshIcon = BitmapToImageSource("icons8-mesh-50.png"); diff --git a/Shared/EmbeddedResources/ResourceLoader.cs b/Shared/EmbeddedResources/ResourceLoader.cs index 12cfa7344..d13ce0870 100644 --- a/Shared/EmbeddedResources/ResourceLoader.cs +++ b/Shared/EmbeddedResources/ResourceLoader.cs @@ -1,6 +1,6 @@ using System.Reflection; using System.Windows.Media.Imaging; -using Shared.Core.ErrorHandling; +using Shared.CoreLog; namespace Shared.EmbeddedResources { diff --git a/Shared/EmbeddedResources/Resources/Icons/icons8-computer-40.png b/Shared/EmbeddedResources/Resources/Icons/icons8-computer-40.png new file mode 100644 index 000000000..d8456f866 Binary files /dev/null and b/Shared/EmbeddedResources/Resources/Icons/icons8-computer-40.png differ diff --git a/Shared/EmbeddedResources/Resources/Icons/icons8-database-2-48.png b/Shared/EmbeddedResources/Resources/Icons/icons8-database-2-48.png new file mode 100644 index 000000000..e93b69bcd Binary files /dev/null and b/Shared/EmbeddedResources/Resources/Icons/icons8-database-2-48.png differ diff --git a/Shared/EmbeddedResources/Shared.EmbeddedResources.csproj b/Shared/EmbeddedResources/Shared.EmbeddedResources.csproj index 8d627fa82..2eb415abf 100644 --- a/Shared/EmbeddedResources/Shared.EmbeddedResources.csproj +++ b/Shared/EmbeddedResources/Shared.EmbeddedResources.csproj @@ -25,6 +25,8 @@ + + @@ -117,6 +119,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Shared/GameFiles/Animation/AnimationFile.cs b/Shared/GameFiles/Animation/AnimationFile.cs index 92d88744a..8b7575471 100644 --- a/Shared/GameFiles/Animation/AnimationFile.cs +++ b/Shared/GameFiles/Animation/AnimationFile.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Text; using Shared.ByteParsing; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.GameFormats.RigidModel.Transforms; diff --git a/Shared/GameFiles/AnimationMeta/Parsing/IMetaDataDatabase.cs b/Shared/GameFiles/AnimationMeta/Parsing/IMetaDataDatabase.cs index c30cba349..67b7417a4 100644 --- a/Shared/GameFiles/AnimationMeta/Parsing/IMetaDataDatabase.cs +++ b/Shared/GameFiles/AnimationMeta/Parsing/IMetaDataDatabase.cs @@ -1,7 +1,4 @@ using System.Reflection; -using Serilog; -using Shared.Core.ErrorHandling; -using SharpDX.MediaFoundation; namespace Shared.GameFormats.AnimationMeta.Parsing { diff --git a/Shared/GameFiles/AnimationMeta/Parsing/MetaDataFileParser.cs b/Shared/GameFiles/AnimationMeta/Parsing/MetaDataFileParser.cs index 774ae3210..80862adf7 100644 --- a/Shared/GameFiles/AnimationMeta/Parsing/MetaDataFileParser.cs +++ b/Shared/GameFiles/AnimationMeta/Parsing/MetaDataFileParser.cs @@ -1,9 +1,7 @@ using System.Diagnostics; using System.Reflection; using CommunityToolkit.Diagnostics; -using Serilog; using Shared.ByteParsing; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles.Models; diff --git a/Shared/GameFiles/Audio/Containers/Wav/RiffChunk.cs b/Shared/GameFiles/Audio/Containers/Wav/RiffChunk.cs index 66ef3a314..ef47b809a 100644 --- a/Shared/GameFiles/Audio/Containers/Wav/RiffChunk.cs +++ b/Shared/GameFiles/Audio/Containers/Wav/RiffChunk.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.ByteParsing; -using Shared.Core.ErrorHandling; +using Shared.ByteParsing; namespace Shared.GameFormats.Audio.Containers.Wav { diff --git a/Shared/GameFiles/Bmd/BmdParser.cs b/Shared/GameFiles/Bmd/BmdParser.cs index 30bc69e29..4af3c732e 100644 --- a/Shared/GameFiles/Bmd/BmdParser.cs +++ b/Shared/GameFiles/Bmd/BmdParser.cs @@ -1,6 +1,4 @@ -using System.Text; -using Serilog; -using Shared.Core.ErrorHandling; +using System.Text; using Microsoft.Xna.Framework; using Shared.GameFormats.RigidModel.Transforms; diff --git a/Shared/GameFiles/FastBin/FastBinParser.cs b/Shared/GameFiles/FastBin/FastBinParser.cs index be6883e27..eb827f935 100644 --- a/Shared/GameFiles/FastBin/FastBinParser.cs +++ b/Shared/GameFiles/FastBin/FastBinParser.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.ByteParsing; -using Shared.Core.ErrorHandling; +using Shared.ByteParsing; using Shared.Core.PackFiles.Models; using static Shared.ByteParsing.ByteChunk; diff --git a/Shared/GameFiles/FastBin/FastBinParser2.cs b/Shared/GameFiles/FastBin/FastBinParser2.cs index 6d67eff1a..460e9bd95 100644 --- a/Shared/GameFiles/FastBin/FastBinParser2.cs +++ b/Shared/GameFiles/FastBin/FastBinParser2.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.ByteParsing; -using Shared.Core.ErrorHandling; +using Shared.ByteParsing; using Shared.Core.PackFiles.Models; using static Shared.ByteParsing.ByteChunk; diff --git a/Shared/GameFiles/GlobalUsings.cs b/Shared/GameFiles/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Shared/GameFiles/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Shared/GameFiles/RigidModel/MaterialHeaders/MaterialFactory.cs b/Shared/GameFiles/RigidModel/MaterialHeaders/MaterialFactory.cs index 6ff75c290..e013af1b8 100644 --- a/Shared/GameFiles/RigidModel/MaterialHeaders/MaterialFactory.cs +++ b/Shared/GameFiles/RigidModel/MaterialHeaders/MaterialFactory.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.Core.ErrorHandling; -using Shared.Core.Misc; +using Shared.Core.Misc; namespace Shared.GameFormats.RigidModel.MaterialHeaders { diff --git a/Shared/GameFiles/RigidModel/ModelFactory.cs b/Shared/GameFiles/RigidModel/ModelFactory.cs index a65bcad99..7ebdd22a1 100644 --- a/Shared/GameFiles/RigidModel/ModelFactory.cs +++ b/Shared/GameFiles/RigidModel/ModelFactory.cs @@ -1,11 +1,8 @@ using System.Runtime.InteropServices; -using Serilog; using Shared.ByteParsing; -using Shared.Core.ErrorHandling; using Shared.GameFormats.RigidModel.LodHeader; using Shared.GameFormats.RigidModel.MaterialHeaders; using Shared.GameFormats.RigidModel.Vertex; -using SharpDX.MediaFoundation; namespace Shared.GameFormats.RigidModel { diff --git a/Shared/GameFiles/Wwise/Hirc/HircItem.cs b/Shared/GameFiles/Wwise/Hirc/HircItem.cs index 2a98242ac..b7ec95105 100644 --- a/Shared/GameFiles/Wwise/Hirc/HircItem.cs +++ b/Shared/GameFiles/Wwise/Hirc/HircItem.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.ByteParsing; -using Shared.Core.ErrorHandling; +using Shared.ByteParsing; using Shared.GameFormats.Wwise.Enums; namespace Shared.GameFormats.Wwise.Hirc diff --git a/Shared/GameFiles/Wwise/Wem/V132/RiffChunk.cs b/Shared/GameFiles/Wwise/Wem/V132/RiffChunk.cs index c25189617..bc1ff83d7 100644 --- a/Shared/GameFiles/Wwise/Wem/V132/RiffChunk.cs +++ b/Shared/GameFiles/Wwise/Wem/V132/RiffChunk.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.ByteParsing; -using Shared.Core.ErrorHandling; +using Shared.ByteParsing; namespace Shared.GameFormats.Wwise.Wem.V132 { diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/CustomLoggingSink.cs b/Shared/Logging/Shared.Core.Logging/CustomLoggingSink.cs similarity index 96% rename from Shared/SharedCore/Shared.Core/ErrorHandling/CustomLoggingSink.cs rename to Shared/Logging/Shared.Core.Logging/CustomLoggingSink.cs index f89dadfda..8cdacc055 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/CustomLoggingSink.cs +++ b/Shared/Logging/Shared.Core.Logging/CustomLoggingSink.cs @@ -2,7 +2,7 @@ using Serilog.Events; using Serilog.Formatting.Display; -namespace Shared.Core.ErrorHandling +namespace Shared.CoreLog { public class CustomLoggingSink : ILogEventSink { @@ -17,7 +17,6 @@ public CustomLoggingSink(LogEventLevel logLevel, string outputTemplate) _logLevel = logLevel; } - public void Emit(LogEvent logEvent) { if (logEvent.Level >= _logLevel) diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/Logging.cs b/Shared/Logging/Shared.Core.Logging/Logging.cs similarity index 95% rename from Shared/SharedCore/Shared.Core/ErrorHandling/Logging.cs rename to Shared/Logging/Shared.Core.Logging/Logging.cs index 8ceb23ecf..e34f6c423 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/Logging.cs +++ b/Shared/Logging/Shared.Core.Logging/Logging.cs @@ -1,9 +1,9 @@ using System.Runtime.CompilerServices; +using Serilog; using Serilog.Events; using Serilog.Sinks.SystemConsole.Themes; -using Shared.Core.Misc; -namespace Shared.Core.ErrorHandling +namespace Shared.CoreLog { public static class LoggerExtensions { @@ -28,12 +28,12 @@ public class Logging public static string LogName { get; private set; } = string.Empty; public static CustomLoggingSink? CustomSink; - public static void Configure(LogEventLevel logEventLevel) + public static void Configure(LogEventLevel logEventLevel, string logDirectory) { if (IsConfigured) return; - var logDirectory = DirectoryHelper.LogDirectory; + var logDate = DateTime.Now.Year + "_" + DateTime.Now.Month + "_" + DateTime.Now.Day; var fileName = logDirectory + "\\" + logDate + ".log"; fileName = getNextFileName(fileName); diff --git a/Shared/Logging/Shared.Core.Logging/Shared.CoreLog.csproj b/Shared/Logging/Shared.Core.Logging/Shared.CoreLog.csproj new file mode 100644 index 000000000..592428e7c --- /dev/null +++ b/Shared/Logging/Shared.Core.Logging/Shared.CoreLog.csproj @@ -0,0 +1,16 @@ + + + + net10.0 + enable + enable + + + + + + + + + + diff --git a/Shared/SharedCore/Shared.Core/Events/Global/OpenEditorCommand.cs b/Shared/SharedCore/Shared.Core/Commands/OpenEditorCommand.cs similarity index 97% rename from Shared/SharedCore/Shared.Core/Events/Global/OpenEditorCommand.cs rename to Shared/SharedCore/Shared.Core/Commands/OpenEditorCommand.cs index bd71be8a6..1ae6341a3 100644 --- a/Shared/SharedCore/Shared.Core/Events/Global/OpenEditorCommand.cs +++ b/Shared/SharedCore/Shared.Core/Commands/OpenEditorCommand.cs @@ -1,10 +1,11 @@ using System.Diagnostics; using CommunityToolkit.Diagnostics; +using Shared.Core.Events; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.ToolCreation; -namespace Shared.Core.Events.Global +namespace Shared.Core.Commands { public class OpenEditorCommand : IAeCommand { diff --git a/Shared/SharedCore/Shared.Core/DependencyInjectionContainer.cs b/Shared/SharedCore/Shared.Core/DependencyInjectionContainer.cs index c91e23ecf..475f18e0d 100644 --- a/Shared/SharedCore/Shared.Core/DependencyInjectionContainer.cs +++ b/Shared/SharedCore/Shared.Core/DependencyInjectionContainer.cs @@ -6,6 +6,7 @@ using Shared.Core.Events; using Shared.Core.Misc; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.ErrorHandling; using Shared.Core.PackFiles.Serialization.CacheDatabase; using Shared.Core.PackFiles.Utility; using Shared.Core.Services; @@ -30,7 +31,6 @@ public override void Register(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddSingleton(); - services.AddSingleton(); services.AddScoped(); services.AddScoped(); @@ -40,12 +40,16 @@ public override void Register(IServiceCollection services) services.AddSingleton(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddTransient(); services.AddSingleton(); services.AddScoped(); services.AddTransient(); + services.AddTransient(); + services.AddSingleton>(sp => () => sp.GetRequiredService()); + services.AddSingleton(); } } diff --git a/Shared/SharedCore/Shared.Core/DevConfig/DevelopmentConfigurationManager.cs b/Shared/SharedCore/Shared.Core/DevConfig/DevelopmentConfigurationManager.cs index 859d0f57d..4a2760b14 100644 --- a/Shared/SharedCore/Shared.Core/DevConfig/DevelopmentConfigurationManager.cs +++ b/Shared/SharedCore/Shared.Core/DevConfig/DevelopmentConfigurationManager.cs @@ -1,5 +1,4 @@ using System.Windows; -using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/BasicExceptionInformationProvider.cs b/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/BasicExceptionInformationProvider.cs index e82a5ee92..f4d73bfcd 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/BasicExceptionInformationProvider.cs +++ b/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/BasicExceptionInformationProvider.cs @@ -1,5 +1,4 @@ using System.Globalization; -using Shared.Core.PackFiles; using Shared.Core.Services; using Shared.Core.Settings; @@ -8,34 +7,20 @@ namespace Shared.Core.ErrorHandling.Exceptions class BasicExceptionInformationProvider : IExceptionInformationProvider { private readonly ApplicationSettingsService _settingsService; - private readonly IPackFileService _pfs; - public BasicExceptionInformationProvider(ApplicationSettingsService service, IPackFileService pfs) + public BasicExceptionInformationProvider(ApplicationSettingsService service) { _settingsService = service; - _pfs = pfs; } public void HydrateExcetion(ExceptionInformation exceptionInformation) { - CreatePackFileInfo(exceptionInformation); CreateGeneralInfo(exceptionInformation); CreateContext(exceptionInformation); CreateSystemInformation(exceptionInformation); } - void CreatePackFileInfo(ExceptionInformation extendedException) - { - var packfiles = _pfs.GetAllPackfileContainers(); - foreach (var db in packfiles) - { - var isMainEditable = _pfs.GetEditablePack() == db; - var info = new ExceptionPackFileContainerInfo(isMainEditable, db.IsCaPackFile, db.Name, db.SystemFilePath); - extendedException.ActivePackFiles.Add(info); - } - } - void CreateGeneralInfo(ExceptionInformation extendedException) { extendedException.CurrentGame = _settingsService.CurrentSettings.CurrentGame; diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/ExceptionHelper.cs b/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/ExceptionHelper.cs index af7ebd924..28e2d3060 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/ExceptionHelper.cs +++ b/Shared/SharedCore/Shared.Core/ErrorHandling/Exceptions/ExceptionHelper.cs @@ -1,16 +1,9 @@ using System.Text; -using System.Windows; namespace Shared.Core.ErrorHandling.Exceptions { public class ExceptionHelper { - public static void ShowErrorBox(Exception e) - { - var errorStr = GetErrorString(e); - MessageBox.Show(errorStr, "Error"); - } - public static string GetErrorString(Exception e, string seperator = "\n") { var ss = new StringBuilder(); @@ -56,6 +49,4 @@ public static Exception GetInnerMostException(Exception e) } } - - } diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/ScopedLogger.cs b/Shared/SharedCore/Shared.Core/ErrorHandling/ScopedLogger.cs index 0ce11df39..b592ecf54 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/ScopedLogger.cs +++ b/Shared/SharedCore/Shared.Core/ErrorHandling/ScopedLogger.cs @@ -1,4 +1,4 @@ -using Shared.Core.DependencyInjection; +using Shared.Core.ToolCreation; namespace Shared.Core.ErrorHandling { diff --git a/Shared/SharedCore/Shared.Core/Events/CommandBuilder.cs b/Shared/SharedCore/Shared.Core/Events/CommandBuilder.cs index 0b48b1910..87dc0e402 100644 --- a/Shared/SharedCore/Shared.Core/Events/CommandBuilder.cs +++ b/Shared/SharedCore/Shared.Core/Events/CommandBuilder.cs @@ -2,19 +2,19 @@ { public class CommandBuilder where T : IAeUndoCommandCommand { - private readonly CommandManager _commandExecutor; + private readonly CommandManager _commandManager; private readonly T _command; private bool _isUndoable = true; public CommandBuilder(CommandManager commandExecutor, T command) { - _commandExecutor = commandExecutor; + _commandManager = commandExecutor; _command = command; } public T Build() => _command; - public void BuildAndExecute() => _commandExecutor.ExecuteCommand(_command, _isUndoable); + public void BuildAndExecute() => _commandManager.ExecuteCommand(_command, _isUndoable); public CommandBuilder Configure(Action predicate) { diff --git a/Shared/SharedCore/Shared.Core/Events/EventHub.cs b/Shared/SharedCore/Shared.Core/Events/EventHub.cs index 1262afb97..59945fc21 100644 --- a/Shared/SharedCore/Shared.Core/Events/EventHub.cs +++ b/Shared/SharedCore/Shared.Core/Events/EventHub.cs @@ -1,5 +1,5 @@ -using Shared.Core.DependencyInjection; -using Shared.Core.ErrorHandling; +using Shared.Core.ErrorHandling; +using Shared.Core.ToolCreation; namespace Shared.Core.Events { diff --git a/Shared/SharedCore/Shared.Core/GlobalUsings.cs b/Shared/SharedCore/Shared.Core/GlobalUsings.cs new file mode 100644 index 000000000..41498e2a6 --- /dev/null +++ b/Shared/SharedCore/Shared.Core/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Shared.CoreLog; +global using Serilog; diff --git a/Shared/SharedCore/Shared.Core/Misc/MemoryOptimiser.cs b/Shared/SharedCore/Shared.Core/Misc/MemoryOptimiser.cs index db7e90c8d..b72b2c36b 100644 --- a/Shared/SharedCore/Shared.Core/Misc/MemoryOptimiser.cs +++ b/Shared/SharedCore/Shared.Core/Misc/MemoryOptimiser.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Runtime; using System.Runtime.InteropServices; -using Shared.Core.ErrorHandling; namespace Shared.Core.Misc { diff --git a/Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileExceptionInformationProvider.cs b/Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileExceptionInformationProvider.cs new file mode 100644 index 000000000..24b22eef2 --- /dev/null +++ b/Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileExceptionInformationProvider.cs @@ -0,0 +1,27 @@ +using Shared.Core.ErrorHandling.Exceptions; + +namespace Shared.Core.PackFiles.ErrorHandling +{ + class PackFileExceptionInformationProvider : IExceptionInformationProvider + { + private readonly IPackFileService _pfs; + + public PackFileExceptionInformationProvider(IPackFileService pfs) + { + ; + _pfs = pfs; + } + + public void HydrateExcetion(ExceptionInformation exceptionInformation) + { + var packfiles = _pfs.GetAllPackfileContainers(); + foreach (var db in packfiles) + { + var isMainEditable = _pfs.GetEditablePack() == db; + var info = new ExceptionPackFileContainerInfo(isMainEditable, db.IsCaPackFile, db.Name, db.SystemFilePath); + exceptionInformation.ActivePackFiles.Add(info); + } + + } + } +} diff --git a/Shared/SharedCore/Shared.Core/ErrorHandling/PackFileLog.cs b/Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileLog.cs similarity index 99% rename from Shared/SharedCore/Shared.Core/ErrorHandling/PackFileLog.cs rename to Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileLog.cs index 09066e0ce..78eea220d 100644 --- a/Shared/SharedCore/Shared.Core/ErrorHandling/PackFileLog.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/ErrorHandling/PackFileLog.cs @@ -2,7 +2,7 @@ using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.PackFiles.Utility; -namespace Shared.Core.ErrorHandling +namespace Shared.Core.PackFiles.ErrorHandling { public class CompressionInformation(long diskSize = 0, long uncompressedSize = 0) { diff --git a/Shared/SharedCore/Shared.Core/Events/Global/PackFileSavedEvent.cs b/Shared/SharedCore/Shared.Core/PackFiles/Events/PackFileSavedEvent.cs similarity index 97% rename from Shared/SharedCore/Shared.Core/Events/Global/PackFileSavedEvent.cs rename to Shared/SharedCore/Shared.Core/PackFiles/Events/PackFileSavedEvent.cs index 57bc733f8..1a65a9d3e 100644 --- a/Shared/SharedCore/Shared.Core/Events/Global/PackFileSavedEvent.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Events/PackFileSavedEvent.cs @@ -1,6 +1,6 @@ using Shared.Core.PackFiles.Models; -namespace Shared.Core.Events.Global +namespace Shared.Core.PackFiles.Events { public record PackFileSavedEvent(PackFile File); public record PackFileContainerSavedEvent(IPackFileContainer Container); diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/CachedPackFileContainer.cs b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/CachedPackFileContainer.cs index 321a53d53..eaa02d826 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/CachedPackFileContainer.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/CachedPackFileContainer.cs @@ -1,8 +1,7 @@ -using System.Diagnostics; +using System.Diagnostics; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.PackFiles.Serialization.CacheDatabase; using Shared.Core.PackFiles.Utility; @@ -29,8 +28,10 @@ internal class CachedPackFileContainer : IPackFileContainerInternal, IDisposable public CacheStorageMode StorageMode { get; } public string? DbFilePath { get; } public string Name { get; set; } - public bool IsCaPackFile { get => true; set { } } + public bool IsReadOnly { get => true; set { } } + public bool IsCaPackFile { get; set; } = false; public string SystemFilePath { get; set; } + public PackFileContainerType ContainerType => PackFileContainerType.Database; public HashSet SourcePackFilePaths { get; set; } = []; /// @@ -260,11 +261,7 @@ public static CachedPackFileContainer CreateFromFileList( { var packParent = new PackedFileSourceParent { FilePath = sourcePackFilePath ?? @"c:\game\data\pack1.pack" }; - var source = new PackFileContainer(containerName) - { - IsCaPackFile = true, - SystemFilePath = systemFilePath - }; + var source = PackFileContainer.CreateReadOnlyPackFile(containerName, systemFilePath); if (sourcePackFilePath != null) source.SourcePackFilePaths.Add(sourcePackFilePath); @@ -427,27 +424,16 @@ public SortedDictionary> GetAllFilesByFolder() var time = Stopwatch.StartNew(); lock (_dbLock) { - - var rows = _db.Files - .Select(f => new - { - f.FolderPath, - f.FileName - }) - .OrderBy(f => f.FolderPath) - .ThenBy(f => f.FileName) - .ToList(); - - var result = rows - .GroupBy(f => f.FolderPath) + var rows = _db.Files + .Select(f => new { f.FolderPath, f.FileName }) .ToList(); var sorted = new SortedDictionary>(StringComparer.InvariantCultureIgnoreCase); - foreach (var re in result) + foreach (var group in rows.GroupBy(f => f.FolderPath)) { - sorted[re.Key] = re.Select(f => f.FileName).OrderByDescending(f=>f).ToList(); - - + var files = group.Select(f => f.FileName).ToList(); + PackFileSortHelper.SortFileList(files); + sorted[group.Key] = files; } _logger.Here().Information("Getting all files from cached container took {ElapsedMilliseconds} ms", time.ElapsedMilliseconds); diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/PackFileContainer.cs b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/PackFileContainer.cs index f65101db8..3227c842f 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/PackFileContainer.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/PackFileContainer.cs @@ -1,5 +1,4 @@ using CommunityToolkit.Diagnostics; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.PackFiles.Serialization; @@ -14,18 +13,59 @@ internal class PackFileContainer : IPackFileContainerInternal public string Name { get; set; } public PFHeader Header { get; set; } + public bool IsReadOnly { get { return IsCaPackFile || field; } set; } = false; public bool IsCaPackFile { get; set; } = false; public string? SystemFilePath { get; set; } + public PackFileContainerType ContainerType => PackFileContainerType.Normal; public long OriginalLoadByteSize { get; set; } = -1; public HashSet SourcePackFilePaths { get; set; } = []; - + public Dictionary FileList { get; set; } = []; - public PackFileContainer(string name) + protected PackFileContainer(string name, PackFileVersion version) { Name = name; - var v = PackFileVersionConverter.ToString(PackFileVersion.PFH5); - Header = new PFHeader(v, PackFileCAType.MOD); + var versionStr = PackFileVersionConverter.ToString(version); + Header = new PFHeader(versionStr, PackFileCAType.MOD); + } + + public static PackFileContainer CreateCaPackFile(string name, string? systemFilePath = null, PackFileVersion version = PackFileVersion.PFH5) + { + return new PackFileContainer(name, version) + { + IsCaPackFile = true, + IsReadOnly = true, + SystemFilePath = systemFilePath + }; + } + + public static PackFileContainer CreateReadOnlyPackFile(string name, string? systemFilePath = null, PackFileVersion version = PackFileVersion.PFH5) + { + return new PackFileContainer(name, version) + { + IsCaPackFile = false, + IsReadOnly = true, + SystemFilePath = systemFilePath + }; + } + + public static PackFileContainer CreatePackFile(string name, string? systemFilePath = null, PackFileVersion version = PackFileVersion.PFH5) + { + return new PackFileContainer(name, version) + { + IsCaPackFile = false, + IsReadOnly = false, + SystemFilePath = systemFilePath + }; + } + + public static PackFileContainer CreatePackFile(string name, string systemFilePath, PFHeader header) + { + return new PackFileContainer(name, header.Version) + { + Header = header, + SystemFilePath = systemFilePath + }; } public int GetFileCount() => FileList.Count; @@ -295,24 +335,7 @@ private static string BuildPackPath(string? directoryPath, string fileName) public SortedDictionary> GetAllFilesByFolder() { - var result = new SortedDictionary>(StringComparer.InvariantCultureIgnoreCase); - - foreach (var fullPath in FileList.Keys) - { - var lastSep = fullPath.LastIndexOf('\\'); - var folder = lastSep == -1 ? string.Empty : fullPath.Substring(0, lastSep); - var fileName = lastSep == -1 ? fullPath : fullPath.Substring(lastSep + 1); - - if (!result.TryGetValue(folder, out var files)) - { - files = new List(); - result[folder] = files; - } - - files.Add(fileName); - } - - return result; + return PackFileSortHelper.BuildSortedFilesByFolder(FileList.Keys); } } } diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/SystemFolderContainer.cs b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/SystemFolderContainer.cs new file mode 100644 index 000000000..930ac3aad --- /dev/null +++ b/Shared/SharedCore/Shared.Core/PackFiles/Models/Containers/SystemFolderContainer.cs @@ -0,0 +1,596 @@ +using Shared.Core.Events; +using Shared.Core.Misc; +using Shared.Core.PackFiles.Events; +using Shared.Core.PackFiles.Models.FileSources; +using Shared.Core.PackFiles.Serialization; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; +using Shared.Core.Settings; + +namespace Shared.Core.PackFiles.Models.Containers +{ + public class SystemFolderContainer : IPackFileContainerInternal, IDisposable + { + private static readonly ILogger _logger = Logging.Create(); + + private readonly IFileSystemAccess _fileSystemAccess; + private readonly IFileSystemWatcher? _watcher; + private readonly IGlobalEventHub? _eventHub; + private readonly SynchronizationContext? _syncContext; + private readonly Dictionary _fileList = new(); + private readonly List _pendingEvents = new(); + private Timer? _debounceTimer; + internal volatile bool _suppressWatcher = false; + private bool _disposed = false; + + public string Name { get; set; } + public bool IsReadOnly { get; set; } = false; + public bool IsCaPackFile { get; set; } = false; + public string? SystemFilePath { get; } + public PackFileContainerType ContainerType => PackFileContainerType.SystemFolder; + + public SystemFolderContainer(string folderPath, IFileSystemAccess fileSystemAccess, IFileSystemWatcher? watcher = null, IGlobalEventHub? eventHub = null, SynchronizationContext? syncContext = null) + { + if (string.IsNullOrWhiteSpace(folderPath)) + throw new ArgumentException("Folder path cannot be empty.", nameof(folderPath)); + if (!fileSystemAccess.DirectoryExists(folderPath)) + throw new DirectoryNotFoundException($"Directory not found: {folderPath}"); + + _fileSystemAccess = fileSystemAccess; + _watcher = watcher; + _eventHub = eventHub; + _syncContext = syncContext ?? SynchronizationContext.Current; + SystemFilePath = folderPath; + Name = Path.GetFileName(folderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + + ScanFolder(); + StartWatching(); + } + + private void ScanFolder() + { + var files = _fileSystemAccess.DirectoryGetFiles(SystemFilePath!, "*.*", SearchOption.AllDirectories); + foreach (var absolutePath in files) + { + var relativePath = Path.GetRelativePath(SystemFilePath!, absolutePath); + var normalizedPath = PathNormalization.NormalizeFileName(relativePath); + var fileName = Path.GetFileName(absolutePath); + var packFile = new PackFile(fileName, new FileSystemSource(absolutePath)); + _fileList[normalizedPath] = packFile; + } + } + + // --- IPackFileContainer read operations --- + + public int GetFileCount() => _fileList.Count; + + public PackFile? FindFile(string path) + { + var normalizedPath = PathNormalization.NormalizeFileName(path); + return _fileList.TryGetValue(normalizedPath, out var value) ? value : null; + } + + public bool ContainsFile(string path) + { + var normalizedPath = PathNormalization.NormalizeFileName(path); + return _fileList.ContainsKey(normalizedPath); + } + + public string? GetFullPath(PackFile file) + { + var pathByReference = _fileList.FirstOrDefault(x => ReferenceEquals(x.Value, file)).Key; + if (!string.IsNullOrWhiteSpace(pathByReference)) + return pathByReference; + + // Fallback: match by name, but only when it is unambiguous. Matching by name + // when several files share the same name could resolve to the wrong file. + var matchesByName = _fileList.Where(x => string.Equals(x.Value.Name, file.Name, StringComparison.OrdinalIgnoreCase)).ToList(); + return matchesByName.Count == 1 ? matchesByName[0].Key : null; + } + + public Dictionary GetAllFiles() => _fileList; + + public SortedDictionary> GetAllFilesByFolder() + { + return PackFileSortHelper.BuildSortedFilesByFolder(_fileList.Keys); + } + + public List<(string FileName, PackFile Pack)> FindAllWithExtention(string extention) + { + extention = extention.ToLower(); + var output = new List<(string, PackFile)>(); + foreach (var file in _fileList) + { + if (Path.GetExtension(file.Key) == extention) + output.Add((file.Key, file.Value)); + } + return output; + } + + public List<(string Path, PackFile File)> SearchFiles(string? textFilter, IReadOnlyList? extensions) + { + var results = new List<(string Path, PackFile File)>(); + + foreach (var (path, packFile) in _fileList) + { + if (extensions != null && extensions.Count > 0) + { + var matchesExtension = false; + foreach (var ext in extensions) + { + if (packFile.Name.Contains(ext, StringComparison.OrdinalIgnoreCase)) + { + matchesExtension = true; + break; + } + } + if (!matchesExtension) + continue; + } + + if (!string.IsNullOrWhiteSpace(textFilter)) + { + if (!packFile.Name.Contains(textFilter, StringComparison.OrdinalIgnoreCase)) + continue; + } + + results.Add((path, packFile)); + } + + results.Sort((a, b) => PackFileSortHelper.PathComparer.Compare(a.Path, b.Path)); + return results; + } + + public List<(string Path, PackFile File)> GetDirectoryContent(string directoryPath) + { + var prefix = string.IsNullOrEmpty(directoryPath) ? "" : directoryPath + "\\"; + var results = new List<(string Path, PackFile File)>(); + var directFileSlashCount = string.IsNullOrEmpty(directoryPath) ? 0 : directoryPath.Count(c => c == '\\') + 1; + + foreach (var (path, packFile) in _fileList) + { + if ((string.IsNullOrEmpty(prefix) || path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + && path.Count(c => c == '\\') == directFileSlashCount) + results.Add((path, packFile)); + } + + results.Sort((a, b) => PackFileSortHelper.PathComparer.Compare(a.Path, b.Path)); + return results; + } + + // --- IPackFileContainerInternal write operations --- + + public void AddOrUpdateFile(string path, PackFile file) + { + if (string.IsNullOrWhiteSpace(file.Name)) + throw new Exception("PackFile name can not be empty"); + + var normalizedPath = PathNormalization.NormalizeFileName(path); + var absolutePath = Path.Combine(SystemFilePath!, normalizedPath); + var directory = Path.GetDirectoryName(absolutePath); + if (!string.IsNullOrEmpty(directory) && !_fileSystemAccess.DirectoryExists(directory)) + _fileSystemAccess.DirectoryCreateDirectory(directory); + + var data = file.DataSource.ReadData(); + using (SuppressWatcher()) + _fileSystemAccess.FileWriteAllBytes(absolutePath, data); + + var newPackFile = new PackFile(file.Name, new FileSystemSource(absolutePath)); + _fileList[normalizedPath] = newPackFile; + } + + public List AddFiles(List newFiles) + { + foreach (var file in newFiles) + { + if (string.IsNullOrWhiteSpace(file.PackFile.Name)) + throw new Exception("PackFile name can not be empty"); + } + + var added = new List(); + using (SuppressWatcher()) + { + foreach (var file in newFiles) + { + var fileName = file.PackFile.Name.Trim(); + var normalizedDir = PathNormalization.NormalizeDirectoryPath(file.DirectoyPath); + var normalizedPath = string.IsNullOrEmpty(normalizedDir) + ? PathNormalization.NormalizeFileName(fileName) + : PathNormalization.NormalizeFileName(normalizedDir + "\\" + fileName); + + var absolutePath = Path.Combine(SystemFilePath!, normalizedPath); + var directory = Path.GetDirectoryName(absolutePath); + if (!string.IsNullOrEmpty(directory) && !_fileSystemAccess.DirectoryExists(directory)) + _fileSystemAccess.DirectoryCreateDirectory(directory); + + var data = file.PackFile.DataSource.ReadData(); + _fileSystemAccess.FileWriteAllBytes(absolutePath, data); + + var newPackFile = new PackFile(fileName, new FileSystemSource(absolutePath)); + _fileList[normalizedPath] = newPackFile; + added.Add(newPackFile); + } + } + + return added; + } + + public PackFile? DeleteFile(PackFile file) + { + var key = _fileList.FirstOrDefault(x => ReferenceEquals(x.Value, file)).Key; + if (key == null) + return null; + + var absolutePath = Path.Combine(SystemFilePath!, key); + using (SuppressWatcher()) + { + if (_fileSystemAccess.FileExists(absolutePath)) + _fileSystemAccess.FileDelete(absolutePath); + } + + _fileList.Remove(key); + return file; + } + + public void DeleteFolder(string folder) + { + var normalizedFolder = PathNormalization.NormalizeFileName(folder); + // Guard against empty / root input which would otherwise resolve to the + // source folder itself and recursively delete the entire container. + if (string.IsNullOrWhiteSpace(normalizedFolder) || normalizedFolder.Trim('\\').Length == 0) + return; + var absoluteFolderPath = Path.Combine(SystemFilePath!, normalizedFolder); + + using (SuppressWatcher()) + { + if (_fileSystemAccess.DirectoryExists(absoluteFolderPath)) + _fileSystemAccess.DirectoryDelete(absoluteFolderPath, true); + } + + var keysToRemove = _fileList.Keys + .Where(k => k.Equals(normalizedFolder, StringComparison.InvariantCultureIgnoreCase) + || k.StartsWith(normalizedFolder + "\\", StringComparison.InvariantCultureIgnoreCase)) + .ToList(); + + foreach (var key in keysToRemove) + _fileList.Remove(key); + } + + public void MoveFile(PackFile file, string newFolderPath) + { + var oldKey = _fileList.FirstOrDefault(x => ReferenceEquals(x.Value, file)).Key; + if (oldKey == null) + throw new Exception($"File '{file.Name}' not found in container."); + + var normalizedDir = PathNormalization.NormalizeDirectoryPath(newFolderPath); + var newRelativePath = string.IsNullOrEmpty(normalizedDir) + ? PathNormalization.NormalizeFileName(file.Name) + : PathNormalization.NormalizeFileName(normalizedDir + "\\" + file.Name); + + var oldAbsolutePath = Path.Combine(SystemFilePath!, oldKey); + var newAbsolutePath = Path.Combine(SystemFilePath!, newRelativePath); + + var newDirectory = Path.GetDirectoryName(newAbsolutePath); + if (!string.IsNullOrEmpty(newDirectory) && !_fileSystemAccess.DirectoryExists(newDirectory)) + _fileSystemAccess.DirectoryCreateDirectory(newDirectory); + + using (SuppressWatcher()) + _fileSystemAccess.FileMove(oldAbsolutePath, newAbsolutePath); + + // Keep the caller's PackFile instance so events that publish 'file' + // can still be resolved by reference (avoids ambiguous name matching). + _fileList.Remove(oldKey); + file.DataSource = new FileSystemSource(newAbsolutePath); + _fileList[newRelativePath] = file; + } + + public string RenameDirectory(string currentNodeName, string newName) + { + var oldNodePath = PathNormalization.NormalizeFileName(currentNodeName); + var newNodePath = newName; + var lastSeparatorIndex = currentNodeName.LastIndexOf(Path.DirectorySeparatorChar); + if (lastSeparatorIndex != -1) + { + var parentPath = currentNodeName.Substring(0, lastSeparatorIndex); + newNodePath = parentPath + Path.DirectorySeparatorChar + newName; + } + newNodePath = PathNormalization.NormalizeFileName(newNodePath); + + var oldAbsolutePath = Path.Combine(SystemFilePath!, oldNodePath); + var newAbsolutePath = Path.Combine(SystemFilePath!, newNodePath); + + using (SuppressWatcher()) + _fileSystemAccess.DirectoryMove(oldAbsolutePath, newAbsolutePath); + + var oldPathPrefix = oldNodePath + "\\"; + var filesToUpdate = _fileList + .Where(x => x.Key.Equals(oldNodePath, StringComparison.InvariantCultureIgnoreCase) + || x.Key.StartsWith(oldPathPrefix, StringComparison.InvariantCultureIgnoreCase)) + .ToList(); + + foreach (var (path, packFile) in filesToUpdate) + { + _fileList.Remove(path); + var newPath = newNodePath; + if (path.Length > oldNodePath.Length) + newPath = newNodePath + path.Substring(oldNodePath.Length); + newPath = PathNormalization.NormalizeFileName(newPath); + + var newFileAbsolutePath = Path.Combine(SystemFilePath!, newPath); + var updatedPackFile = new PackFile(packFile.Name, new FileSystemSource(newFileAbsolutePath)); + _fileList[newPath] = updatedPackFile; + } + + return newNodePath; + } + + public void RenameFile(PackFile file, string newName) + { + var key = _fileList.FirstOrDefault(x => ReferenceEquals(x.Value, file)).Key; + if (key == null) + throw new Exception($"File '{file.Name}' not found in container."); + + var dir = Path.GetDirectoryName(key); + var newRelativePath = string.IsNullOrEmpty(dir) + ? PathNormalization.NormalizeFileName(newName) + : PathNormalization.NormalizeFileName(dir + "\\" + newName); + + var oldAbsolutePath = Path.Combine(SystemFilePath!, key); + var newAbsolutePath = Path.Combine(SystemFilePath!, newRelativePath); + + using (SuppressWatcher()) + _fileSystemAccess.FileMove(oldAbsolutePath, newAbsolutePath); + + // Keep the caller's PackFile instance so events that publish 'file' + // can still be resolved by reference (avoids ambiguous name matching). + _fileList.Remove(key); + file.Name = newName; + file.DataSource = new FileSystemSource(newAbsolutePath); + _fileList[newRelativePath] = file; + } + + public void SaveFileData(PackFile file, byte[] data) + { + var key = _fileList.FirstOrDefault(x => ReferenceEquals(x.Value, file)).Key; + if (key == null) + throw new Exception($"File '{file.Name}' not found in container."); + + var absolutePath = Path.Combine(SystemFilePath!, key); + using (SuppressWatcher()) + _fileSystemAccess.FileWriteAllBytes(absolutePath, data); + + file.DataSource = new FileSystemSource(absolutePath); + } + + public void SaveToDisk(string path, bool createBackup, GameInformation gameInformation) + { + if (_fileSystemAccess.FileExists(path) && DirectoryHelper.IsFileLocked(path)) + throw new IOException($"Cannot access {path} because another process has locked it."); + + var tempPath = path + "_temp"; + if (_fileSystemAccess.FileExists(tempPath) && DirectoryHelper.IsFileLocked(tempPath)) + throw new IOException($"Cannot access {tempPath} because another process has locked it."); + + if (createBackup) + SaveUtility.CreateFileBackup(path); + + // Build a transient PackFileContainer with in-memory data for serialization + var versionString = PackFileVersionConverter.ToString(PackFileVersion.PFH5); + var transientContainer = PackFileContainer.CreatePackFile(Name, path, PackFileVersion.PFH5); + + foreach (var (relativePath, packFile) in _fileList) + { + var data = packFile.DataSource.ReadData(); + var memFile = new PackFile(packFile.Name, new MemorySource(data)); + transientContainer.AddOrUpdateFile(relativePath, memFile); + } + + using (SuppressWatcher()) + { + using (var fileStream = new FileStream(tempPath, FileMode.Create)) + { + using var writer = new BinaryWriter(fileStream); + PackFileSerializerWriter.SaveToByteArray(path, transientContainer, writer, gameInformation); + } + + _fileSystemAccess.FileMove(tempPath, path); + } + } + + // --- FileSystemWatcher integration --- + + private void StartWatching() + { + if (_watcher == null) + return; + + _watcher.Path = SystemFilePath!; + _watcher.IncludeSubdirectories = true; + _watcher.Created += OnExternalFileCreated; + _watcher.Deleted += OnExternalFileDeleted; + _watcher.Renamed += OnExternalFileRenamed; + _watcher.EnableRaisingEvents = true; + _debounceTimer = new Timer(OnDebounceElapsed, null, Timeout.Infinite, Timeout.Infinite); + } + + private void OnExternalFileCreated(object? sender, FileSystemEventArgs e) + { + if (_suppressWatcher) return; + lock (_pendingEvents) { _pendingEvents.Add(e); } + ResetDebounceTimer(); + } + + private void OnExternalFileDeleted(object? sender, FileSystemEventArgs e) + { + if (_suppressWatcher) return; + lock (_pendingEvents) { _pendingEvents.Add(e); } + ResetDebounceTimer(); + } + + private void OnExternalFileRenamed(object? sender, RenamedEventArgs e) + { + if (_suppressWatcher) return; + lock (_pendingEvents) { _pendingEvents.Add(e); } + ResetDebounceTimer(); + } + + private void ResetDebounceTimer() + { + _debounceTimer?.Change(300, Timeout.Infinite); + } + + private void OnDebounceElapsed(object? state) + { + if (_syncContext != null) + _syncContext.Post(_ => ProcessPendingEvents(null), null); + else + ProcessPendingEvents(null); + } + + internal void ProcessPendingEvents(object? state) + { + if (_disposed) + return; + + List events; + lock (_pendingEvents) + { + events = new List(_pendingEvents); + _pendingEvents.Clear(); + } + + var addedFiles = new List(); + var keysToRemove = new List(); + + foreach (var e in events) + { + switch (e.ChangeType) + { + case WatcherChangeTypes.Created: + HandleExternalCreated(e.FullPath, addedFiles); + break; + case WatcherChangeTypes.Deleted: + CollectExternalDeleted(e.FullPath, keysToRemove); + break; + case WatcherChangeTypes.Renamed: + var renamedArgs = (RenamedEventArgs)e; + CollectExternalDeleted(renamedArgs.OldFullPath, keysToRemove); + HandleExternalCreated(renamedArgs.FullPath, addedFiles); + break; + } + } + + // Publish removed event BEFORE removing from _fileList so that + // GetFullPath can still resolve paths for the tree view update. + // Deduplicate keys: chatty/duplicate watcher events (or a folder delete + // combined with a child delete) can collect the same key more than once. + var distinctKeysToRemove = keysToRemove.Distinct().ToList(); + var removedFiles = distinctKeysToRemove.Select(k => _fileList[k]).ToList(); + if (removedFiles.Count > 0) + _eventHub?.PublishGlobalEvent(new PackFileContainerFilesRemovedEvent(this, removedFiles)); + + // Now actually remove the entries + foreach (var key in distinctKeysToRemove) + _fileList.Remove(key); + + if (addedFiles.Count > 0) + _eventHub?.PublishGlobalEvent(new PackFileContainerFilesAddedEvent(this, addedFiles)); + } + + private void HandleExternalCreated(string absolutePath, List addedFiles) + { + // If this is a directory, scan and add all files within it + if (Directory.Exists(absolutePath) && !File.Exists(absolutePath)) + { + try + { + var filesInDir = Directory.GetFiles(absolutePath, "*.*", SearchOption.AllDirectories); + foreach (var filePath in filesInDir) + { + HandleExternalCreated(filePath, addedFiles); + } + } + catch (Exception ex) + { + _logger.Here().Warning($"Failed to scan externally created directory '{absolutePath}': {ex.Message}"); + } + return; + } + + var relativePath = Path.GetRelativePath(SystemFilePath!, absolutePath); + var normalizedPath = PathNormalization.NormalizeFileName(relativePath); + + if (_fileList.ContainsKey(normalizedPath)) + return; // Already tracked + + try + { + var fileName = Path.GetFileName(absolutePath); + var packFile = new PackFile(fileName, new FileSystemSource(absolutePath)); + _fileList[normalizedPath] = packFile; + addedFiles.Add(packFile); + } + catch (Exception ex) + { + // File may be locked or still being written by another process + _logger.Here().Warning($"Failed to add externally created file '{absolutePath}': {ex.Message}"); + } + } + + private void CollectExternalDeleted(string absolutePath, List keysToRemove) + { + var relativePath = Path.GetRelativePath(SystemFilePath!, absolutePath); + var normalizedPath = PathNormalization.NormalizeFileName(relativePath); + + // Check if this is an exact file match + if (_fileList.ContainsKey(normalizedPath)) + { + keysToRemove.Add(normalizedPath); + return; + } + + // Handle folder deletion — collect all files with this prefix + var folderPrefix = normalizedPath + "\\"; + var matchingKeys = _fileList.Keys + .Where(k => k.StartsWith(folderPrefix, StringComparison.InvariantCultureIgnoreCase)) + .ToList(); + + keysToRemove.AddRange(matchingKeys); + } + + // --- Watcher suppression --- + + private WatcherSuppression SuppressWatcher() => new(this); + + private readonly struct WatcherSuppression : IDisposable + { + private readonly SystemFolderContainer _container; + + public WatcherSuppression(SystemFolderContainer container) + { + _container = container; + _container._suppressWatcher = true; + } + + public void Dispose() => _container._suppressWatcher = false; + } + + // --- IDisposable --- + + public void Dispose() + { + if (_disposed) + return; + _disposed = true; + + if (_watcher != null) + { + _watcher.EnableRaisingEvents = false; + _watcher.Dispose(); + } + _debounceTimer?.Dispose(); + _debounceTimer = null; + _fileList.Clear(); + } + } +} diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Models/IDataSource.cs b/Shared/SharedCore/Shared.Core/PackFiles/Models/FileSources/IDataSource.cs similarity index 85% rename from Shared/SharedCore/Shared.Core/PackFiles/Models/IDataSource.cs rename to Shared/SharedCore/Shared.Core/PackFiles/Models/FileSources/IDataSource.cs index f4e465f1f..cd621489d 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Models/IDataSource.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Models/FileSources/IDataSource.cs @@ -1,7 +1,7 @@ using Shared.ByteParsing; using Shared.Core.PackFiles.Utility; -namespace Shared.Core.PackFiles.Models +namespace Shared.Core.PackFiles.Models.FileSources { public interface IDataSource { @@ -12,6 +12,4 @@ public interface IDataSource CompressionFormat CompressionFormat { get; } } - - } diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Models/IPackFileContainer.cs b/Shared/SharedCore/Shared.Core/PackFiles/Models/IPackFileContainer.cs index 37c37a8b4..c17e63297 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Models/IPackFileContainer.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Models/IPackFileContainer.cs @@ -1,10 +1,19 @@ namespace Shared.Core.PackFiles.Models { + public enum PackFileContainerType + { + Database, + Normal, + SystemFolder + } + public interface IPackFileContainer { string Name { get; } + bool IsReadOnly { get; set; } bool IsCaPackFile { get; set; } string? SystemFilePath { get; } + PackFileContainerType ContainerType { get; } int GetFileCount(); diff --git a/Shared/SharedCore/Shared.Core/PackFiles/PackFileService.cs b/Shared/SharedCore/Shared.Core/PackFiles/PackFileService.cs index 81fef8f6f..5add8242f 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/PackFileService.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/PackFileService.cs @@ -1,7 +1,6 @@ using System.Windows; -using Shared.Core.ErrorHandling; using Shared.Core.Events; -using Shared.Core.Events.Global; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Models.FileSources; @@ -47,14 +46,22 @@ public PackFileService(IGlobalEventHub? globalEventHub) } } - // Check if already added! - foreach (var packFile in _packFileContainers) + // Check if already added! Normalize paths so the same pack loaded via a + // differently-cased or non-canonical path is still detected as a duplicate. + if (string.IsNullOrEmpty(pf.SystemFilePath) == false) { - if (packFile.SystemFilePath != null && packFile.SystemFilePath == pf.SystemFilePath) + var newPath = NormalizeSystemPath(pf.SystemFilePath); + foreach (var packFile in _packFileContainers) { - _logger.Here().Warning($"Rejected loading duplicate pack file '{packFile.SystemFilePath}'"); - MessageBoxProvider.ShowDialogBox($"Pack file \"{packFile.SystemFilePath}\" is already loaded.", "Error"); - return null; + if (string.IsNullOrEmpty(packFile.SystemFilePath)) + continue; + + if (NormalizeSystemPath(packFile.SystemFilePath) == newPath) + { + _logger.Here().Warning($"Rejected loading duplicate pack file '{packFile.SystemFilePath}'"); + MessageBoxProvider.ShowDialogBox($"Pack file \"{packFile.SystemFilePath}\" is already loaded.", "Error"); + return null; + } } } @@ -82,10 +89,7 @@ public IPackFileContainer CreateNewPackFileContainer(string name, PackFileVersio throw new Exception("Name can not be empty"); var versionString = PackFileVersionConverter.ToString(packFileVersion); - var newPackFile = new PackFileContainer(name) - { - Header = new PFHeader(versionString, type), - }; + var newPackFile = PackFileContainer.CreatePackFile(name, null, packFileVersion); _logger.Here().Information($"Creating new pack file container '{name}' with version '{versionString}' and type '{type}'"); AddContainerInternal(newPackFile, setEditablePack); @@ -96,8 +100,8 @@ public IPackFileContainer CreateNewPackFileContainer(string name, PackFileVersio public void AddFilesToPack(IPackFileContainer container, List newFiles) { var pf = CastContainer(container); - if (pf.IsCaPackFile) - throw new Exception("Can not add files to ca pack file"); + if (pf.IsReadOnly) + throw new Exception("Can not add files to readonly pack file"); _logger.Here().Information($"Adding {newFiles.Count} file(s) to '{DescribeContainer(pf)}'"); var addedFiles = pf.AddFiles(newFiles); @@ -108,8 +112,8 @@ public void AddFilesToPack(IPackFileContainer container, List public void CopyFileFromOtherPackFile(IPackFileContainer source, string path, IPackFileContainer target) { var pf = CastContainer(target); - if (pf.IsCaPackFile) - throw new Exception("Can not add files to ca pack file"); + if (pf.IsReadOnly) + throw new Exception("Can not add files to readonly pack file"); var sourceContainer = CastContainer(source); var targetContainer = CastContainer(target); @@ -121,9 +125,11 @@ public void CopyFileFromOtherPackFile(IPackFileContainer source, string path, IP var newFile = new PackFile(file.Name, new MemorySource(data)); targetContainer.AddOrUpdateFile(lowerPath, newFile); + var storedFile = targetContainer.FindFile(lowerPath)!; + _logger.Here().Information($"Copied '{lowerPath}' from '{DescribeContainer(sourceContainer)}' to '{DescribeContainer(targetContainer)}'"); - _globalEventHub?.PublishGlobalEvent(new PackFileContainerFilesAddedEvent(targetContainer, [newFile])); + _globalEventHub?.PublishGlobalEvent(new PackFileContainerFilesAddedEvent(targetContainer, [storedFile])); } else { @@ -133,8 +139,8 @@ public void CopyFileFromOtherPackFile(IPackFileContainer source, string path, IP public void SetEditablePack(IPackFileContainer? pf) { - if (pf != null && pf.IsCaPackFile) - throw new Exception("Trying to set CA packfile container to be editable - this is not legal!"); + if (pf != null && pf.IsReadOnly) + throw new Exception("Trying to set readonly packfile container to be editable - this is not legal!"); _packFileContainerSelectedForEdit = pf != null ? CastContainer(pf) : null; _logger.Here().Information($"Editable pack set to '{DescribeContainer(_packFileContainerSelectedForEdit)}'"); _globalEventHub?.PublishGlobalEvent(new PackFileContainerSetAsMainEditableEvent(pf)); @@ -161,6 +167,9 @@ public void UnloadPackContainer(IPackFileContainer pf) _logger.Here().Information($"Unloaded pack file container '{DescribeContainer(container)}'. Remaining containers: {_packFileContainers.Count}"); _globalEventHub?.PublishGlobalEvent(new PackFileContainerRemovedEvent(container)); + + if (container is IDisposable disposable) + disposable.Dispose(); } public List<(string FileName, PackFile Pack)> FindAllWithExtention(string extention, IPackFileContainer? pf = null) @@ -180,8 +189,8 @@ public void UnloadPackContainer(IPackFileContainer pf) public void DeleteFolder(IPackFileContainer pf, string folder) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not delete folder inside CA pack file"); + if (container.IsReadOnly) + throw new Exception("Can not delete folder inside readonly pack file"); _logger.Here().Information($"Deleting folder '{folder}' from '{DescribeContainer(container)}'"); _globalEventHub?.PublishGlobalEvent(new PackFileContainerFolderRemovedEvent(container, folder)); @@ -191,8 +200,8 @@ public void DeleteFolder(IPackFileContainer pf, string folder) public void DeleteFile(IPackFileContainer pf, PackFile file) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not delete files inside CA pack file"); + if (container.IsReadOnly) + throw new Exception("Can not delete files inside readonly pack file"); _logger.Here().Information($"Deleting file '{DescribeFile(container, file)}' from '{DescribeContainer(container)}'"); _globalEventHub?.PublishGlobalEvent(new PackFileContainerFilesRemovedEvent(container, [file])); @@ -202,8 +211,8 @@ public void DeleteFile(IPackFileContainer pf, PackFile file) public void MoveFile(IPackFileContainer pf, PackFile file, string newFolderPath) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not move files inside CA pack file"); + if (container.IsReadOnly) + throw new Exception("Can not move files inside readonly pack file"); var key = container.GetFullPath(file); _globalEventHub?.PublishGlobalEvent(new PackFileContainerFilesRemovedEvent(container, [file])); @@ -215,8 +224,8 @@ public void MoveFile(IPackFileContainer pf, PackFile file, string newFolderPath) public void RenameDirectory(IPackFileContainer pf, string currentNodeName, string newName) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not rename in ca pack file"); + if (container.IsReadOnly) + throw new Exception("Can not rename in readonly pack file"); if (string.IsNullOrWhiteSpace(newName)) throw new Exception("Name can not be empty"); @@ -229,8 +238,8 @@ public void RenameDirectory(IPackFileContainer pf, string currentNodeName, strin public void RenameFile(IPackFileContainer pf, PackFile file, string newName) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not rename file in ca pack file"); + if (container.IsReadOnly) + throw new Exception("Can not rename file in readonly pack file"); if (string.IsNullOrWhiteSpace(newName)) throw new Exception("Name can not be empty"); @@ -246,8 +255,8 @@ public void SaveFile(PackFile file, byte[] data) var pf = _packFileContainerSelectedForEdit; if (pf == null) throw new Exception("No editable pack file is set"); - if (pf.IsCaPackFile) - throw new Exception("Can not save ca pack file"); + if (pf.IsReadOnly) + throw new Exception("Can not save readonly pack file"); _logger.Here().Information($"Saving file '{DescribeFile(pf, file)}' to '{DescribeContainer(pf)}' ({data.Length} bytes)"); pf.SaveFileData(file, data); @@ -258,8 +267,8 @@ public void SaveFile(PackFile file, byte[] data) public void SavePackContainer(IPackFileContainer pf, string path, bool createBackup, GameInformation gameInformation) { var container = CastContainer(pf); - if (container.IsCaPackFile) - throw new Exception("Can not save ca pack file"); + if (container.IsReadOnly) + throw new Exception("Can not save readonly pack file"); _logger.Here().Information($"Saving pack file container '{DescribeContainer(container)}' to '{path}' (CreateBackup:{createBackup}, Game:{gameInformation.DisplayName})"); container.SaveToDisk(path, createBackup, gameInformation); @@ -343,6 +352,18 @@ private static string DescribeContainer(IPackFileContainer? container) return concreteContainer.SystemFilePath ?? concreteContainer.Name; } + private static string NormalizeSystemPath(string path) + { + try + { + return Path.GetFullPath(path).TrimEnd('\\', '/').ToLowerInvariant(); + } + catch + { + return path.Replace('/', '\\').TrimEnd('\\').Trim().ToLowerInvariant(); + } + } + private static string DescribeFile(IPackFileContainer container, PackFile file) { var concreteContainer = CastContainer(container); diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/CacheDatabase/PackFileContainerCacheHelper.cs b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/CacheDatabase/PackFileContainerCacheHelper.cs index 9b20bd1a9..a6affe447 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/CacheDatabase/PackFileContainerCacheHelper.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/CacheDatabase/PackFileContainerCacheHelper.cs @@ -1,6 +1,5 @@ -using System.Security.Cryptography; +using System.Security.Cryptography; using System.Text; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles.Models.Containers; diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerLoader.cs b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerLoader.cs index dc8e7ed8d..6c0e2c78a 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerLoader.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerLoader.cs @@ -1,5 +1,4 @@ using Shared.ByteParsing; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Models.FileSources; @@ -34,13 +33,9 @@ public static PackFileContainer Load(string packFileSystemPath, long packFileSiz { var fileNameBuffer = new byte[1024]; var name = Path.GetFileNameWithoutExtension(packFileSystemPath); - var output = new PackFileContainer(name) - { - SystemFilePath = packFileSystemPath, - Name = Path.GetFileNameWithoutExtension(packFileSystemPath), - Header = ReadHeader(reader), - OriginalLoadByteSize = packFileSize, - }; + var header = ReadHeader(reader); + var output = PackFileContainer.CreatePackFile(name, packFileSystemPath, header); + output.OriginalLoadByteSize = packFileSize; // If larger then int.max throw error if (output.Header.FileCount > int.MaxValue) diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerWriter.cs b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerWriter.cs index 42f6c78bd..0b71a7b1e 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerWriter.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Serialization/PackFileSerializerWriter.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Text; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Models.FileSources; @@ -34,7 +33,7 @@ public static void SaveToByteArray(string outputFileName, PackFileContainer cont if (container.Header.HasEncryptedData || container.Header.HasEncryptedIndex) throw new InvalidOperationException("Saving encrypted packs is not supported."); - var sortedFiles = container.GetAllFiles().OrderBy(x => x.Key, StringComparer.Ordinal).ToList(); + var sortedFiles = container.GetAllFiles().OrderBy(x => x.Key, PackFileSortHelper.PathComparer).ToList(); var headerSpecificBytes = ComputeFileHeaderSpecificByte(container); var fileNamesOffset = ComputeFileNameOffset(headerSpecificBytes, sortedFiles); diff --git a/Shared/SharedCore/Shared.Core/Services/FileSaveService.cs b/Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSaveService.cs similarity index 98% rename from Shared/SharedCore/Shared.Core/Services/FileSaveService.cs rename to Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSaveService.cs index 01f80cc66..f72590b8c 100644 --- a/Shared/SharedCore/Shared.Core/Services/FileSaveService.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSaveService.cs @@ -1,9 +1,9 @@ -using Shared.Core.PackFiles; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; -using Shared.Core.ErrorHandling; +using Shared.Core.Services; -namespace Shared.Core.Services +namespace Shared.Core.PackFiles.Utility { public interface IFileSaveService { diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSystemWatcherWrapper.cs b/Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSystemWatcherWrapper.cs new file mode 100644 index 000000000..f91bda584 --- /dev/null +++ b/Shared/SharedCore/Shared.Core/PackFiles/Utility/FileSystemWatcherWrapper.cs @@ -0,0 +1,37 @@ +namespace Shared.Core.PackFiles.Utility +{ + public interface IFileSystemWatcher : IDisposable + { + string Path { get; set; } + bool IncludeSubdirectories { get; set; } + bool EnableRaisingEvents { get; set; } + + event FileSystemEventHandler? Created; + event FileSystemEventHandler? Deleted; + event RenamedEventHandler? Renamed; + } + + public class FileSystemWatcherWrapper : IFileSystemWatcher + { + private readonly FileSystemWatcher _watcher; + + public FileSystemWatcherWrapper() + { + _watcher = new FileSystemWatcher(); + _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; + _watcher.Created += (s, e) => Created?.Invoke(s, e); + _watcher.Deleted += (s, e) => Deleted?.Invoke(s, e); + _watcher.Renamed += (s, e) => Renamed?.Invoke(s, e); + } + + public string Path { get => _watcher.Path; set => _watcher.Path = value; } + public bool IncludeSubdirectories { get => _watcher.IncludeSubdirectories; set => _watcher.IncludeSubdirectories = value; } + public bool EnableRaisingEvents { get => _watcher.EnableRaisingEvents; set => _watcher.EnableRaisingEvents = value; } + + public event FileSystemEventHandler? Created; + public event FileSystemEventHandler? Deleted; + public event RenamedEventHandler? Renamed; + + public void Dispose() => _watcher.Dispose(); + } +} diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader2.cs b/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader.cs similarity index 79% rename from Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader2.cs rename to Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader.cs index b0daa31e9..87ae5b48f 100644 --- a/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader2.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileContainerLoader.cs @@ -1,7 +1,8 @@ using System.Collections.Concurrent; using System.Reflection; using System.Text; -using Shared.Core.ErrorHandling; +using Shared.Core.Events; +using Shared.Core.PackFiles.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Serialization; @@ -10,19 +11,36 @@ using Shared.Core.Settings; namespace Shared.Core.PackFiles.Utility -{ - public enum PackFileContainerType +{ + public interface ISystemFolderContainerFactory { - Cached, - Normal, + IPackFileContainer Create(string folderPath); } + public class SystemFolderContainerFactory : ISystemFolderContainerFactory + { + private readonly IFileSystemAccess _fileSystemAccess; + private readonly IGlobalEventHub _globalEventHub; + private readonly Func _watcherFactory; + + public SystemFolderContainerFactory(IFileSystemAccess fileSystemAccess, IGlobalEventHub globalEventHub, Func watcherFactory) + { + _fileSystemAccess = fileSystemAccess; + _globalEventHub = globalEventHub; + _watcherFactory = watcherFactory; + } + + public IPackFileContainer Create(string folderPath) + { + return new SystemFolderContainer(folderPath, _fileSystemAccess, _watcherFactory(), _globalEventHub); + } + } + + public interface IPackFileContainerLoader { IPackFileContainer CreateFromPackFile(PackFileContainerType type, string packFilePath, bool loadAsReadOnly); - // IPackFileContainer CreateFromCollection(PackFileContainerType type, string packFileSystemPath, List fullPackFilePaths, string createdPackFileName, bool loadAsReadOnly, IDuplicateFileResolver duplicateFileResolver); IPackFileContainer? CreateFromGameEnum(PackFileContainerType type, GameTypeEnum game); - IPackFileContainer CreateFromSystemFolder(string folderPath); } @@ -33,15 +51,15 @@ class PackFileContainerLoader : IPackFileContainerLoader private readonly IStandardDialogs _standardDialogs; private readonly LocalizationManager _localizationManager; private readonly IPackFileContainerCacheHelper _packFileContainerCacheHelper; + private readonly ISystemFolderContainerFactory _systemFolderContainerFactory; - public PackFileContainerLoader(ApplicationSettingsService settingsService, IStandardDialogs standardDialogs, LocalizationManager localizationManager, IPackFileContainerCacheHelper packFileContainerCacheHelper) + public PackFileContainerLoader(ApplicationSettingsService settingsService, IStandardDialogs standardDialogs, LocalizationManager localizationManager, IPackFileContainerCacheHelper packFileContainerCacheHelper, ISystemFolderContainerFactory systemFolderContainerFactory) { - // TODO : Handle context for logger - _settingsService = settingsService; _standardDialogs = standardDialogs; _localizationManager = localizationManager; _packFileContainerCacheHelper = packFileContainerCacheHelper; + _systemFolderContainerFactory = systemFolderContainerFactory; } public IPackFileContainer CreateFromSystemFolder(string packFileSystemPath) @@ -53,33 +71,10 @@ public IPackFileContainer CreateFromSystemFolder(string packFileSystemPath) throw new Exception($"Unable to find folder {packFileSystemPath}. Curret systempath is {loactionDir}"); } - var containerName = Path.GetFileName(packFileSystemPath); - var container = new PackFileContainer(containerName) - { - SystemFilePath = packFileSystemPath, - }; - AddFolderContentToPackFile(container, packFileSystemPath, packFileSystemPath.ToLower() + "\\"); + var container = _systemFolderContainerFactory.Create(packFileSystemPath); return container; } - private static void AddFolderContentToPackFile(PackFileContainer container, string folderPath, string rootPath) - { - var files = Directory.GetFiles(folderPath); - foreach (var filePath in files) - { - var sanatizedFilePath = filePath.ToLower(); - var relativePath = sanatizedFilePath.Replace(rootPath, ""); - var fileName = Path.GetFileName(sanatizedFilePath); - - container.AddOrUpdateFile(relativePath, PackFile.CreateFromFileSystem(fileName, sanatizedFilePath)); - } - - var folders = Directory.GetDirectories(folderPath); - foreach (var folder in folders) - AddFolderContentToPackFile(container, folder, rootPath); - } - - public IPackFileContainer CreateFromPackFile(PackFileContainerType type, string packFilePath, bool loadAsReadOnly) { var packfileName = Path.GetFileNameWithoutExtension(packFilePath); @@ -112,21 +107,25 @@ public IPackFileContainer CreateFromPackFile(PackFileContainerType type, string packfileResolver = new CustomPackDuplicateFileResolver(); } - return CreateFromCollection(PackFileContainerType.Cached, gameDataFolder, fullPackFilePaths, $"All Game Packs - {gameName}", true, packfileResolver); + var container = CreateFromCollection(PackFileContainerType.Database, gameDataFolder, fullPackFilePaths, $"All Game Packs - {gameName}", true, packfileResolver); + container.IsCaPackFile = true; + return container; } public IPackFileContainer CreateFromCollection(PackFileContainerType type, string packFileSystemPath, List fullPackFilePaths, string createdPackFileName, bool loadAsReadOnly, IDuplicateFileResolver duplicateFileResolver) { - if(type == PackFileContainerType.Cached && loadAsReadOnly == false) + if(type == PackFileContainerType.Database && loadAsReadOnly == false) throw new InvalidOperationException($"Cannot load as writable if loading from cache. Caching is only supported for read-only containers. PackFile {createdPackFileName}"); - - var fingerprint = _packFileContainerCacheHelper.ComputeFingerprint(fullPackFilePaths); - var cachePrefix = createdPackFileName; - var cacheFilePath = _packFileContainerCacheHelper.GetCacheFilePath(cachePrefix, fingerprint); - - if (type == PackFileContainerType.Cached) + + var fingerprint = string.Empty; + var cacheFilePath = string.Empty; + if (type == PackFileContainerType.Database) { + fingerprint = _packFileContainerCacheHelper.ComputeFingerprint(fullPackFilePaths); + var cachePrefix = createdPackFileName; + cacheFilePath = _packFileContainerCacheHelper.GetCacheFilePath(cachePrefix, fingerprint); + var cached = _packFileContainerCacheHelper.TryLoadFromCache(cacheFilePath, fingerprint); if (cached != null) return cached; @@ -136,17 +135,17 @@ public IPackFileContainer CreateFromCollection(PackFileContainerType type, strin //var reasonMessage = string.Format(_localizationManager.Get("PackFileCache.InvalidReason." + cacheInvalidReason), gameName); //var buildingMessage = string.Format(_localizationManager.Get("PackFileCache.BuildingCache"), gameName); //var cacheDescription = _localizationManager.Get("PackFileCache.Description"); - _standardDialogs.ShowDialogBox("Failed to load from cache - make better error later"); + _standardDialogs.ShowDialogBox("Failed to load from cache - Generating new cache"); } using (_standardDialogs.ShowWaitCursor()) { var container = LoadPackFilesFromDisk(createdPackFileName, fullPackFilePaths, duplicateFileResolver); container.Name = createdPackFileName; - container.IsCaPackFile = loadAsReadOnly; + container.IsReadOnly = loadAsReadOnly; container.SystemFilePath = packFileSystemPath; - if (type == PackFileContainerType.Cached) + if (type == PackFileContainerType.Database) { return _packFileContainerCacheHelper.SaveAndLoadCache(fingerprint, container, cacheFilePath); } @@ -204,7 +203,7 @@ private static PackFileContainer LoadPackFilesFromDisk(string createdPackFileNam if (packList.Count == 1) return packList.First(); - var mergedPackFile = new PackFileContainer(createdPackFileName); + var mergedPackFile = PackFileContainer.CreatePackFile(createdPackFileName); var packFilesOrderedByGroup = packList.GroupBy(x => x.Header.LoadOrder).OrderBy(x => x.Key); foreach (var group in packFilesOrderedByGroup) diff --git a/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileSortHelper.cs b/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileSortHelper.cs new file mode 100644 index 000000000..b0a19ba68 --- /dev/null +++ b/Shared/SharedCore/Shared.Core/PackFiles/Utility/PackFileSortHelper.cs @@ -0,0 +1,37 @@ +namespace Shared.Core.PackFiles.Utility +{ + public static class PackFileSortHelper + { + public static readonly StringComparer PathComparer = StringComparer.Ordinal; + + public static void SortFileList(List fileNames) + { + fileNames.Sort(PathComparer); + } + + public static SortedDictionary> BuildSortedFilesByFolder(IEnumerable fullPaths) + { + var result = new SortedDictionary>(StringComparer.InvariantCultureIgnoreCase); + + foreach (var fullPath in fullPaths) + { + var lastSep = fullPath.LastIndexOf('\\'); + var folder = lastSep == -1 ? string.Empty : fullPath.Substring(0, lastSep); + var fileName = lastSep == -1 ? fullPath : fullPath.Substring(lastSep + 1); + + if (!result.TryGetValue(folder, out var files)) + { + files = new List(); + result[folder] = files; + } + + files.Add(fileName); + } + + foreach (var files in result.Values) + SortFileList(files); + + return result; + } + } +} diff --git a/Shared/SharedCore/Shared.Core/Misc/SaveUtility.cs b/Shared/SharedCore/Shared.Core/PackFiles/Utility/SaveUtility.cs similarity index 97% rename from Shared/SharedCore/Shared.Core/Misc/SaveUtility.cs rename to Shared/SharedCore/Shared.Core/PackFiles/Utility/SaveUtility.cs index 2c42aa2a4..3959cb9c1 100644 --- a/Shared/SharedCore/Shared.Core/Misc/SaveUtility.cs +++ b/Shared/SharedCore/Shared.Core/PackFiles/Utility/SaveUtility.cs @@ -1,6 +1,6 @@ using Shared.Core.PackFiles; -namespace Shared.Core.Misc +namespace Shared.Core.PackFiles.Utility { public static class SaveUtility { diff --git a/Shared/SharedCore/Shared.Core/Services/FileSystemAccess.cs b/Shared/SharedCore/Shared.Core/Services/FileSystemAccess.cs index 9cd4260a9..28ad1f1b7 100644 --- a/Shared/SharedCore/Shared.Core/Services/FileSystemAccess.cs +++ b/Shared/SharedCore/Shared.Core/Services/FileSystemAccess.cs @@ -1,7 +1,4 @@ -using System.Diagnostics; -using System.IO; -using Serilog; -using Shared.Core.ErrorHandling; +using System.Diagnostics; namespace Shared.Core.Services { @@ -27,8 +24,38 @@ public byte[] FileReadAllBytes(string path) public bool FileExists(string path) => File.Exists(path); + public void FileDelete(string path) + { + File.Delete(path); + _logger.Here().Information($"Deleted file '{path}'"); + } + + public void FileMove(string sourceFileName, string destFileName) + { + File.Move(sourceFileName, destFileName, overwrite: true); + _logger.Here().Information($"Moved file '{sourceFileName}' to '{destFileName}'"); + } + public bool DirectoryExists(string path) => Directory.Exists(path); + public void DirectoryCreateDirectory(string path) + { + Directory.CreateDirectory(path); + _logger.Here().Information($"Created directory '{path}'"); + } + + public void DirectoryDelete(string path, bool recursive) + { + Directory.Delete(path, recursive); + _logger.Here().Information($"Deleted directory '{path}' (recursive={recursive})"); + } + + public void DirectoryMove(string sourceDirName, string destDirName) + { + Directory.Move(sourceDirName, destDirName); + _logger.Here().Information($"Moved directory '{sourceDirName}' to '{destDirName}'"); + } + public string[] DirectoryGetFiles(string path, string searchPattern, SearchOption searchOption) { var files = Directory.GetFiles(path, searchPattern, searchOption); diff --git a/Shared/SharedCore/Shared.Core/Services/IFileSystemAccess.cs b/Shared/SharedCore/Shared.Core/Services/IFileSystemAccess.cs index 066d94640..cd91bcb71 100644 --- a/Shared/SharedCore/Shared.Core/Services/IFileSystemAccess.cs +++ b/Shared/SharedCore/Shared.Core/Services/IFileSystemAccess.cs @@ -11,7 +11,13 @@ public interface IFileSystemAccess byte[] FileReadAllBytes(string path); bool FileExists(string path); + void FileDelete(string path); + void FileMove(string sourceFileName, string destFileName); + bool DirectoryExists(string path); + void DirectoryCreateDirectory(string path); + void DirectoryDelete(string path, bool recursive); + void DirectoryMove(string sourceDirName, string destDirName); string[] DirectoryGetFiles(string path, string searchPattern, SearchOption searchOption); DirectoryInfo CreateDirectoryInfo(string path); diff --git a/Shared/SharedCore/Shared.Core/Services/IStandardDialogs.cs b/Shared/SharedCore/Shared.Core/Services/IStandardDialogs.cs index 648c420c2..1ea74dc63 100644 --- a/Shared/SharedCore/Shared.Core/Services/IStandardDialogs.cs +++ b/Shared/SharedCore/Shared.Core/Services/IStandardDialogs.cs @@ -1,7 +1,6 @@ using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; -using System.Collections.Generic; namespace Shared.Core.Services { diff --git a/Shared/SharedCore/Shared.Core/Services/LocalizationManager.cs b/Shared/SharedCore/Shared.Core/Services/LocalizationManager.cs index 4ccca8347..d1e83bf84 100644 --- a/Shared/SharedCore/Shared.Core/Services/LocalizationManager.cs +++ b/Shared/SharedCore/Shared.Core/Services/LocalizationManager.cs @@ -1,6 +1,5 @@ using System.Text.Json; using System.Windows; -using Shared.Core.ErrorHandling; namespace Shared.Core.Services { diff --git a/Shared/SharedCore/Shared.Core/Services/VersionChecker.cs b/Shared/SharedCore/Shared.Core/Services/VersionChecker.cs index 56eace4da..9ba511fe6 100644 --- a/Shared/SharedCore/Shared.Core/Services/VersionChecker.cs +++ b/Shared/SharedCore/Shared.Core/Services/VersionChecker.cs @@ -1,6 +1,5 @@ using System.Reflection; using Octokit; -using Shared.Core.ErrorHandling; namespace Shared.Core.Services { diff --git a/Shared/SharedCore/Shared.Core/Settings/ApplicationSettingsService.cs b/Shared/SharedCore/Shared.Core/Settings/ApplicationSettingsService.cs index 6b865a3fe..cbd89e678 100644 --- a/Shared/SharedCore/Shared.Core/Settings/ApplicationSettingsService.cs +++ b/Shared/SharedCore/Shared.Core/Settings/ApplicationSettingsService.cs @@ -1,8 +1,8 @@ using System.Collections.ObjectModel; using System.Text.Json; using Microsoft.Xna.Framework; -using Shared.Core.ErrorHandling; using Shared.Core.Misc; +using Shared.Core.PackFiles.Models; namespace Shared.Core.Settings { @@ -12,11 +12,13 @@ public enum CameraControlMode AssetEditorStyle, } + public record RecentPackFileInfo(string Path, PackFileContainerType ContainerType, bool IsReadOnly); + public class ApplicationSettings { public record GamePathPair(GameTypeEnum Game, string Path); - public ObservableCollection RecentPackFilePaths { get; set; } = []; + public ObservableCollection RecentPackFiles { get; set; } = []; public ThemeType Theme { get; set; } = ThemeType.DarkTheme; public BackgroundColour RenderEngineBackgroundColour { get; set; } = BackgroundColour.DarkGrey; public bool StartMaximised { get; set; } = false; @@ -62,20 +64,22 @@ public ApplicationSettingsService(GameTypeEnum currentGame = GameTypeEnum.Unknow return GetGamePathForGame(game); } - public void AddRecentlyOpenedPackFile(string path) + public void AddRecentlyOpenedPackFile(string path, PackFileContainerType containerType, bool isReadOnly) { - var recentPackFilePaths = CurrentSettings.RecentPackFilePaths; + var recentPackFiles = CurrentSettings.RecentPackFiles; + var newEntry = new RecentPackFileInfo(path, containerType, isReadOnly); - if (recentPackFilePaths.Any() && recentPackFilePaths.Last() == path) + if (recentPackFiles.Any() && recentPackFiles.Last() == newEntry) return; - if (recentPackFilePaths.Contains(path)) - recentPackFilePaths.Remove(path); + var existing = recentPackFiles.FirstOrDefault(x => x.Path == path); + if (existing != null) + recentPackFiles.Remove(existing); - recentPackFilePaths.Add(path); + recentPackFiles.Add(newEntry); - if (recentPackFilePaths.Count > 15) - recentPackFilePaths.RemoveAt(0); + if (recentPackFiles.Count > 15) + recentPackFiles.RemoveAt(0); } public string? GetGamePathForGame(GameTypeEnum game) @@ -128,11 +132,11 @@ public void Load() void ValidateRecentPackFilePaths() { - var recentPackfilePaths = CurrentSettings.RecentPackFilePaths; - var invalidPacks = recentPackfilePaths.Where(path => !File.Exists(path)).ToList(); + var recentPackFiles = CurrentSettings.RecentPackFiles; + var invalidPacks = recentPackFiles.Where(x => !File.Exists(x.Path) && !Directory.Exists(x.Path)).ToList(); - foreach (var invalidPath in invalidPacks) - recentPackfilePaths.Remove(invalidPath); + foreach (var invalid in invalidPacks) + recentPackFiles.Remove(invalid); } diff --git a/Shared/SharedCore/Shared.Core/Shared.Core.csproj b/Shared/SharedCore/Shared.Core/Shared.Core.csproj index 5b41f4569..122fad211 100644 --- a/Shared/SharedCore/Shared.Core/Shared.Core.csproj +++ b/Shared/SharedCore/Shared.Core/Shared.Core.csproj @@ -14,10 +14,7 @@ - - - - + @@ -38,6 +35,7 @@ + diff --git a/Shared/SharedCore/Shared.Core/ToolCreation/EditorDatabase.cs b/Shared/SharedCore/Shared.Core/ToolCreation/EditorDatabase.cs index 0ee51d78e..9036503e2 100644 --- a/Shared/SharedCore/Shared.Core/ToolCreation/EditorDatabase.cs +++ b/Shared/SharedCore/Shared.Core/ToolCreation/EditorDatabase.cs @@ -1,10 +1,5 @@ using System.Text.RegularExpressions; -using Microsoft.Extensions.DependencyInjection; -using Serilog; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling; -using Shared.Core.Events; -using Shared.Core.Events.Global; namespace Shared.Core.ToolCreation { diff --git a/Shared/SharedCore/Shared.Core/DependencyInjection/IScopeOwnerAware.cs b/Shared/SharedCore/Shared.Core/ToolCreation/IScopeOwnerAware.cs similarity index 68% rename from Shared/SharedCore/Shared.Core/DependencyInjection/IScopeOwnerAware.cs rename to Shared/SharedCore/Shared.Core/ToolCreation/IScopeOwnerAware.cs index 52bf3cc55..63b51299e 100644 --- a/Shared/SharedCore/Shared.Core/DependencyInjection/IScopeOwnerAware.cs +++ b/Shared/SharedCore/Shared.Core/ToolCreation/IScopeOwnerAware.cs @@ -1,4 +1,4 @@ -namespace Shared.Core.DependencyInjection +namespace Shared.Core.ToolCreation { public interface IScopeOwnerAware { diff --git a/Shared/SharedCore/Shared.Core/DependencyInjection/ScopeRepository.cs b/Shared/SharedCore/Shared.Core/ToolCreation/ScopeRepository.cs similarity index 98% rename from Shared/SharedCore/Shared.Core/DependencyInjection/ScopeRepository.cs rename to Shared/SharedCore/Shared.Core/ToolCreation/ScopeRepository.cs index eccb3867f..278fc8079 100644 --- a/Shared/SharedCore/Shared.Core/DependencyInjection/ScopeRepository.cs +++ b/Shared/SharedCore/Shared.Core/ToolCreation/ScopeRepository.cs @@ -1,13 +1,10 @@ using System.Collections; using System.Reflection; -using System.Runtime.Intrinsics.Arm; using System.Text; using CommunityToolkit.Diagnostics; using Microsoft.Extensions.DependencyInjection; -using Shared.Core.ErrorHandling; -using Shared.Core.ToolCreation; -namespace Shared.Core.DependencyInjection +namespace Shared.Core.ToolCreation { public interface IScopeRepository { diff --git a/Shared/SharedCore/Shared.CoreTest/Events/EventHubTests.cs b/Shared/SharedCore/Shared.CoreTest/Events/EventHubTests.cs index fc0585c46..e46316f8b 100644 --- a/Shared/SharedCore/Shared.CoreTest/Events/EventHubTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/Events/EventHubTests.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling; using Shared.Core.Events; +using Shared.Core.ToolCreation; using Test.TestingUtility.TestUtility; namespace Shared.CoreTest.Events diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddFiles.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddFiles.cs index 9a2daeb49..16307cd3e 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddFiles.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddFiles.cs @@ -1,11 +1,13 @@ using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_AddFiles : PackFileContainerTests_TestBase { public PackFileContainerTests_AddFiles(Type containerType) : base(containerType) { } @@ -15,8 +17,8 @@ public void AddFiles_AddsMultipleFilesOrThrowsOnCached() { var newFiles = new List { - new("dir", new PackFile("a.txt", null)), - new("", new PackFile("root.txt", null)) + new("dir", new PackFile("a.txt", new MemorySource([1]))), + new("", new PackFile("root.txt", new MemorySource([2]))) }; if (IsCachedContainer) @@ -37,12 +39,12 @@ public void AddFiles_EmptyFileName_Throws() if (IsCachedContainer) { Assert.Throws(() => - _container.AddFiles([new NewPackFileEntry("dir", new PackFile("", null))])); + _container.AddFiles([new NewPackFileEntry("dir", new PackFile("", new MemorySource([1])))])); return; } Assert.Throws(() => - _container.AddFiles([new NewPackFileEntry("dir", new PackFile("", null))])); + _container.AddFiles([new NewPackFileEntry("dir", new PackFile("", new MemorySource([1])))])); } } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddOrUpdateFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddOrUpdateFile.cs index 83e3d5950..def9b32d8 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddOrUpdateFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_AddOrUpdateFile.cs @@ -1,10 +1,12 @@ using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_AddOrUpdateFile : PackFileContainerTests_TestBase { public PackFileContainerTests_AddOrUpdateFile(Type containerType) : base(containerType) { } @@ -15,11 +17,11 @@ public void AddOrUpdateFile_NewPath_AddsFileOrThrowsOnCached() if (IsCachedContainer) { Assert.Throws(() => - _container.AddOrUpdateFile("new\\file.txt", new PackFile("file.txt", null))); + _container.AddOrUpdateFile("new\\file.txt", new PackFile("file.txt", new MemorySource([1])))); return; } - _container.AddOrUpdateFile("new\\file.txt", new PackFile("file.txt", null)); + _container.AddOrUpdateFile("new\\file.txt", new PackFile("file.txt", new MemorySource([1]))); Assert.That(_container.ContainsFile("new\\file.txt"), Is.True); } @@ -29,12 +31,12 @@ public void AddOrUpdateFile_ExistingPath_UpdatesWithoutIncreasingCount() if (IsCachedContainer) { Assert.Throws(() => - _container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", null))); + _container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", new MemorySource([1])))); return; } var before = _container.GetFileCount(); - _container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", null)); + _container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", new MemorySource([2]))); Assert.That(_container.GetFileCount(), Is.EqualTo(before)); } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_ContainsFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_ContainsFile.cs index fafa504c4..5665b0c4e 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_ContainsFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_ContainsFile.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_ContainsFile : PackFileContainerTests_TestBase { public PackFileContainerTests_ContainsFile(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFile.cs index 6bfd379fa..10571817c 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFile.cs @@ -1,10 +1,12 @@ using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_DeleteFile : PackFileContainerTests_TestBase { public PackFileContainerTests_DeleteFile(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFolder.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFolder.cs index 5ee657f27..3b83060e4 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFolder.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_DeleteFolder.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_DeleteFolder : PackFileContainerTests_TestBase { public PackFileContainerTests_DeleteFolder(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindAllWithExtention.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindAllWithExtention.cs index 4273c082a..d0a06f95f 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindAllWithExtention.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindAllWithExtention.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_FindAllWithExtention : PackFileContainerTests_TestBase { public PackFileContainerTests_FindAllWithExtention(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindFile.cs index 05fbefc7b..c68e19c3f 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_FindFile.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_FindFile : PackFileContainerTests_TestBase { public PackFileContainerTests_FindFile(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFiles.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFiles.cs index 5c00a013a..4e066187a 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFiles.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFiles.cs @@ -6,6 +6,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_GetAllFiles : PackFileContainerTests_TestBase { public PackFileContainerTests_GetAllFiles(Type containerType) : base(containerType) { } @@ -22,6 +23,12 @@ public void GetAllFiles_ReturnsExpectedCountAndKeys() [Test] public void GetAllFiles_PreservesCompressionMetadata() { + if (IsSystemFolderContainer) + { + Assert.That(_container.GetAllFiles()["compressed\\data.bin"].DataSource, Is.InstanceOf()); + return; + } + var source = (PackedFileSource)_container.GetAllFiles()["compressed\\data.bin"].DataSource; Assert.That(source.CompressionFormat, Is.EqualTo(CompressionFormat.Lz4)); Assert.That(source.UncompressedSize, Is.EqualTo(2000)); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFilesByFolder.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFilesByFolder.cs index 2a6b0ca61..0ba1690e4 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFilesByFolder.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetAllFilesByFolder.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_GetAllFilesByFolder : PackFileContainerTests_TestBase { public PackFileContainerTests_GetAllFilesByFolder(Type containerType) : base(containerType) { } @@ -127,5 +128,31 @@ public void GetAllFilesByFolder_TotalFileCountMatchesGetAllFiles() Assert.That(totalFiles, Is.EqualTo(allFiles.Count)); } + + [Test] + public void GetAllFilesByFolder_FilesWithinEachFolderAreSorted() + { + var result = _container.GetAllFilesByFolder(); + + foreach (var (folder, files) in result) + { + for (var i = 1; i < files.Count; i++) + { + Assert.That(StringComparer.Ordinal.Compare(files[i - 1], files[i]), Is.LessThan(0), + $"Files not sorted in folder '{folder}': '{files[i - 1]}' should come before '{files[i]}'"); + } + } + } + + [Test] + public void GetAllFilesByFolder_AudioFolder_FilesAreSortedByOrdinal() + { + var result = _container.GetAllFilesByFolder(); + var audioFiles = result["audio"]; + + // Ordinal sort: battle_sound.wem, music.wem, sound.wem, voice.txt, voice.wem.{sdf}, voice.wem_temp + var expected = new[] { "battle_sound.wem", "music.wem", "sound.wem", "voice.txt", "voice.wem.{sdf}", "voice.wem_temp" }; + Assert.That(audioFiles, Is.EqualTo(expected)); + } } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetDirectoryContent.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetDirectoryContent.cs index b0e09de0c..49d3c06f3 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetDirectoryContent.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetDirectoryContent.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_GetDirectoryContent : PackFileContainerTests_TestBase { public PackFileContainerTests_GetDirectoryContent(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFileCount.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFileCount.cs index 2c9714d35..d5a063ce3 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFileCount.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFileCount.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_GetFileCount : PackFileContainerTests_TestBase { public PackFileContainerTests_GetFileCount(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFullPath.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFullPath.cs index ba174af50..dd616a985 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFullPath.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_GetFullPath.cs @@ -6,6 +6,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_GetFullPath : PackFileContainerTests_TestBase { public PackFileContainerTests_GetFullPath(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_IsCaPackFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_IsCaPackFile.cs deleted file mode 100644 index 48627d47a..000000000 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_IsCaPackFile.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Shared.Core.PackFiles.Models.Containers; - -namespace Shared.CoreTest.PackFiles.Models.Containers -{ - [TestFixture(typeof(CachedPackFileContainer))] - [TestFixture(typeof(PackFileContainer))] - internal class PackFileContainerTests_IsCaPackFile : PackFileContainerTests_TestBase - { - public PackFileContainerTests_IsCaPackFile(Type containerType) : base(containerType) { } - - [Test] - public void IsCaPackFile_AlwaysStartsTrue() - { - Assert.That(_container.IsCaPackFile, Is.True); - } - - [Test] - public void IsCaPackFile_SetterBehavior_MatchesContainerType() - { - _container.IsCaPackFile = false; - Assert.That(_container.IsCaPackFile, Is.EqualTo(IsCachedContainer)); - } - } -} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_MoveFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_MoveFile.cs index 107e8a57a..768ad4af2 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_MoveFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_MoveFile.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_MoveFile : PackFileContainerTests_TestBase { public PackFileContainerTests_MoveFile(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_Name.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_Name.cs index 9e158ff34..6c315ba84 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_Name.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_Name.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_Name : PackFileContainerTests_TestBase { public PackFileContainerTests_Name(Type containerType) : base(containerType) { } @@ -11,6 +12,12 @@ public PackFileContainerTests_Name(Type containerType) : base(containerType) { } [Test] public void Name_IsSetFromContainerCreation() { + if (IsSystemFolderContainer) + { + Assert.That(_container.Name, Does.StartWith("PackFileContainerTests_")); + return; + } + Assert.That(_container.Name, Is.EqualTo("TestCache")); } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameDirectory.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameDirectory.cs index 0852a193f..5c84944ea 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameDirectory.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameDirectory.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_RenameDirectory : PackFileContainerTests_TestBase { public PackFileContainerTests_RenameDirectory(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameFile.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameFile.cs index dadfa08c4..ad7e598f3 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameFile.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_RenameFile.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_RenameFile : PackFileContainerTests_TestBase { public PackFileContainerTests_RenameFile(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveFileData.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveFileData.cs index 6673b0dce..1984b9b54 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveFileData.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveFileData.cs @@ -6,6 +6,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_SaveFileData : PackFileContainerTests_TestBase { public PackFileContainerTests_SaveFileData(Type containerType) : base(containerType) { } @@ -20,6 +21,17 @@ public void SaveFileData_UpdatesMemorySourceOrThrowsOnCached() return; } + if (IsSystemFolderContainer) + { + // SystemFolderContainer.AddOrUpdateFile creates a new PackFile internally, + // so we must re-fetch the stored reference after adding. + _container.AddOrUpdateFile("new.txt", new PackFile("new.txt", new MemorySource([1]))); + var storedFile = _container.FindFile("new.txt")!; + _container.SaveFileData(storedFile, [9, 8, 7]); + Assert.That(storedFile.DataSource.ReadData(), Is.EqualTo(new byte[] { 9, 8, 7 })); + return; + } + var file = new PackFile("new.txt", new MemorySource([1])); _container.AddOrUpdateFile("new.txt", file); _container.SaveFileData(file, [9, 8, 7]); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveToDisk.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveToDisk.cs index 332a2a23c..36a65ef0f 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveToDisk.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SaveToDisk.cs @@ -5,6 +5,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_SaveToDisk : PackFileContainerTests_TestBase { public PackFileContainerTests_SaveToDisk(Type containerType) : base(containerType) { } @@ -18,5 +19,30 @@ public void SaveToDisk_CachedContainer_Throws() var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); Assert.Throws(() => _container.SaveToDisk("path", false, gameInfo)); } + + [Test] + public void SaveToDisk_WritableContainer_ProducesValidPackFile() + { + if (IsCachedContainer) + Assert.Ignore("CachedPackFileContainer does not support SaveToDisk."); + + if (!IsSystemFolderContainer) + Assert.Ignore("PackFileContainer with PackedFileSource test data cannot SaveToDisk without a real pack file on disk."); + + var tempPath = Path.Combine(Path.GetTempPath(), "SaveToDiskTest_" + Guid.NewGuid().ToString("N") + ".pack"); + try + { + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + _container.SaveToDisk(tempPath, false, gameInfo); + + Assert.That(File.Exists(tempPath), Is.True); + Assert.That(new FileInfo(tempPath).Length, Is.GreaterThan(0)); + } + finally + { + if (File.Exists(tempPath)) + File.Delete(tempPath); + } + } } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SearchFiles.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SearchFiles.cs index ae091210b..86f6959b0 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SearchFiles.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SearchFiles.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_SearchFiles : PackFileContainerTests_TestBase { public PackFileContainerTests_SearchFiles(Type containerType) : base(containerType) { } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SystemFilePath.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SystemFilePath.cs index 9a2a83c28..d2e1330d3 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SystemFilePath.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_SystemFilePath.cs @@ -4,6 +4,7 @@ namespace Shared.CoreTest.PackFiles.Models.Containers { [TestFixture(typeof(CachedPackFileContainer))] [TestFixture(typeof(PackFileContainer))] + [TestFixture(typeof(SystemFolderContainer))] internal class PackFileContainerTests_SystemFilePath : PackFileContainerTests_TestBase { public PackFileContainerTests_SystemFilePath(Type containerType) : base(containerType) { } @@ -11,6 +12,12 @@ public PackFileContainerTests_SystemFilePath(Type containerType) : base(containe [Test] public void SystemFilePath_MatchesSeededPath() { + if (IsSystemFolderContainer) + { + Assert.That(_container.SystemFilePath, Does.StartWith(Path.GetTempPath().TrimEnd('\\'))); + return; + } + Assert.That(_container.SystemFilePath, Is.EqualTo(@"c:\game\data")); } } diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_TestBase.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_TestBase.cs index 29ab66839..25fa0bc8e 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_TestBase.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/PackFileContainerTests_TestBase.cs @@ -2,15 +2,19 @@ using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; namespace Shared.CoreTest.PackFiles.Models.Containers { internal abstract class PackFileContainerTests_TestBase { - private readonly bool _useCachedContainer; + private readonly Type _containerType; protected IPackFileContainerInternal _container = null!; - protected bool IsCachedContainer => _useCachedContainer; + protected bool IsCachedContainer => _containerType == typeof(CachedPackFileContainer); + protected bool IsSystemFolderContainer => _containerType == typeof(SystemFolderContainer); + + private string? _tempDir; protected static readonly (string RelativePath, string FileName, long Offset, long Size, bool IsEncrypted, bool IsCompressed, CompressionFormat CompressionFormat, uint UncompressedSize)[] TestFiles = [ @@ -36,24 +40,37 @@ protected static readonly (string RelativePath, string FileName, long Offset, lo protected PackFileContainerTests_TestBase(Type containerType) { - _useCachedContainer = containerType == typeof(CachedPackFileContainer); + _containerType = containerType; } [SetUp] public void Setup() { - if (_useCachedContainer) + if (_containerType == typeof(CachedPackFileContainer)) { _container = CachedPackFileContainer.CreateFromFileList("TestCache", TestFiles, useInMemoryDb: true, systemFilePath: @"c:\game\data", sourcePackFilePath: @"c:\game\data\pack1.pack"); } + else if (_containerType == typeof(SystemFolderContainer)) + { + _tempDir = Path.Combine(Path.GetTempPath(), "PackFileContainerTests_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + foreach (var file in TestFiles) + { + var absolutePath = Path.Combine(_tempDir, file.RelativePath); + Directory.CreateDirectory(Path.GetDirectoryName(absolutePath)!); + // Write content with size matching the test data + var content = new byte[file.Size]; + Array.Fill(content, (byte)'X'); + File.WriteAllBytes(absolutePath, content); + } + + _container = new SystemFolderContainer(_tempDir, new FileSystemAccess()); + } else { var parent = new PackedFileSourceParent { FilePath = @"c:\game\data\pack1.pack" }; - var sourceContainer = new PackFileContainer("TestCache") - { - IsCaPackFile = true, - SystemFilePath = @"c:\game\data" - }; + var sourceContainer = PackFileContainer.CreateCaPackFile("TestCache", @"c:\game\data"); sourceContainer.SourcePackFilePaths.Add(@"c:\game\data\pack1.pack"); foreach (var file in TestFiles) @@ -67,6 +84,9 @@ public void Setup() public void TearDown() { (_container as IDisposable)?.Dispose(); + + if (_tempDir != null && Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); } protected void IgnoreIfNotCached(string scenario) diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Dispose.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Dispose.cs new file mode 100644 index 000000000..8803f6aba --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Dispose.cs @@ -0,0 +1,91 @@ +using Moq; +using Shared.Core.Events; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_Dispose + { + private string _tempDir = null!; + private Mock _fileSystemAccess = null!; + private Mock _mockWatcher = null!; + private Mock _mockEventHub = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderDispose_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + var seedPath = Path.Combine(_tempDir, "file.txt"); + File.WriteAllText(seedPath, "content"); + + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(_tempDir)).Returns(true); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([seedPath]); + + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + } + + [TearDown] + public void TearDown() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + [Test] + public void Dispose_StopsRaisingEvents() + { + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + container.Dispose(); + + _mockWatcher.VerifySet(w => w.EnableRaisingEvents = false); + } + + [Test] + public void Dispose_DisposesWatcher() + { + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + container.Dispose(); + + _mockWatcher.Verify(w => w.Dispose(), Times.Once); + } + + [Test] + public void Dispose_ClearsFileList() + { + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + Assert.That(container.GetFileCount(), Is.GreaterThan(0)); + + container.Dispose(); + + Assert.That(container.GetFileCount(), Is.EqualTo(0)); + } + + [Test] + public void Dispose_CalledTwice_NoException() + { + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + container.Dispose(); + Assert.DoesNotThrow(() => container.Dispose()); + } + + [Test] + public void Dispose_WithoutWatcher_NoException() + { + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object); + + Assert.DoesNotThrow(() => container.Dispose()); + Assert.That(container.GetFileCount(), Is.EqualTo(0)); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Integration.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Integration.cs new file mode 100644 index 000000000..e41a6ae54 --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Integration.cs @@ -0,0 +1,496 @@ +using Moq; +using Shared.Core.Events; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; +using Shared.Core.PackFiles.Serialization; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; +using Shared.Core.Settings; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_Integration + { + private string _tempDir = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderInteg_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + } + + [TearDown] + public void TearDown() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + private static IFileSystemAccess CreateRealFileSystemAccess() + { + return new FileSystemAccess(); + } + + [Test] + public void FullWorkflow_CreateFolder_AddFile_DeleteFile_Save() + { + // Arrange: seed a file in the temp directory + var seedPath = Path.Combine(_tempDir, "models", "unit.txt"); + Directory.CreateDirectory(Path.GetDirectoryName(seedPath)!); + File.WriteAllText(seedPath, "original"); + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var watcher = new Mock(); + + // Act: create container from real folder + var container = new SystemFolderContainer(_tempDir, fileSystem, watcher.Object, eventHub.Object); + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + Assert.That(container.FindFile(@"models\unit.txt"), Is.Not.Null); + + // Register with PackFileService + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(container); + + // Add a file via service + var newFileData = "new file content"u8.ToArray(); + var newFile = new PackFile("added.bin", new MemorySource(newFileData)); + pfs.AddFilesToPack(container, [new NewPackFileEntry("scripts", newFile)]); + + Assert.That(container.GetFileCount(), Is.EqualTo(2)); + Assert.That(File.Exists(Path.Combine(_tempDir, "scripts", "added.bin")), Is.True); + Assert.That(File.ReadAllBytes(Path.Combine(_tempDir, "scripts", "added.bin")), Is.EqualTo(newFileData)); + + // Delete the original file via service + var originalFile = container.FindFile(@"models\unit.txt")!; + pfs.DeleteFile(container, originalFile); + + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + Assert.That(File.Exists(seedPath), Is.False); + + // Save as pack + var packPath = Path.Combine(_tempDir, "output.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + pfs.SavePackContainer(container, packPath, false, gameInfo); + + Assert.That(File.Exists(packPath), Is.True); + + // Verify the pack file is valid and contains only the added file + using var fs = File.OpenRead(packPath); + using var reader = new BinaryReader(fs); + var loaded = PackFileSerializerLoader.Load(packPath, fs.Length, reader, new CaPackDuplicateFileResolver()); + Assert.That(loaded.GetFileCount(), Is.EqualTo(1)); + Assert.That(loaded.FindFile(@"scripts\added.bin"), Is.Not.Null); + + // Container should remain active + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + Assert.That(container.SystemFilePath, Is.EqualTo(_tempDir)); + + // Unload disposes cleanly + pfs.UnloadPackContainer(container); + watcher.Verify(w => w.Dispose(), Times.Once); + } + + [Test] + public void FullWorkflow_ExternalAdd_DetectedAndEventsPublished() + { + // Arrange + var seedPath = Path.Combine(_tempDir, "initial.txt"); + File.WriteAllText(seedPath, "init"); + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + + var container = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + + // Simulate an external file being created on disk + var externalPath = Path.Combine(_tempDir, "external.txt"); + File.WriteAllText(externalPath, "external content"); + + // Simulate the watcher firing a Created event + mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "external.txt")); + + // Process the pending events (as the debounce timer would) + container.ProcessPendingEvents(null); + + // Verify file was added to container + Assert.That(container.GetFileCount(), Is.EqualTo(2)); + Assert.That(container.ContainsFile("external.txt"), Is.True); + + // Verify event was published + eventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.Container == container && + e.AddedFiles.Count == 1 && + e.AddedFiles[0].Name == "external.txt" + )), Times.Once); + + // Simulate external deletion + File.Delete(externalPath); + mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "external.txt")); + container.ProcessPendingEvents(null); + + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + Assert.That(container.ContainsFile("external.txt"), Is.False); + eventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.Container == container && + e.RemovedFiles.Count == 1 && + e.RemovedFiles[0].Name == "external.txt" + )), Times.Once); + } + + [Test] + public void FullWorkflow_RenameAndMoveFile_ThenSave() + { + // Seed files + var filePath = Path.Combine(_tempDir, "folder", "original.txt"); + Directory.CreateDirectory(Path.GetDirectoryName(filePath)!); + File.WriteAllText(filePath, "data"); + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + + var container = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(container); + + // Rename file + var file = container.FindFile(@"folder\original.txt")!; + pfs.RenameFile(container, file, "renamed.txt"); + + Assert.That(container.ContainsFile(@"folder\original.txt"), Is.False); + Assert.That(container.ContainsFile(@"folder\renamed.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "folder", "renamed.txt")), Is.True); + + // Move file to root + var renamedFile = container.FindFile(@"folder\renamed.txt")!; + pfs.MoveFile(container, renamedFile, ""); + + Assert.That(container.ContainsFile(@"folder\renamed.txt"), Is.False); + Assert.That(container.ContainsFile("renamed.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "renamed.txt")), Is.True); + + // Save to pack and verify content + var packPath = Path.Combine(_tempDir, "result.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + pfs.SavePackContainer(container, packPath, false, gameInfo); + + using var fs = File.OpenRead(packPath); + using var reader = new BinaryReader(fs); + var loaded = PackFileSerializerLoader.Load(packPath, fs.Length, reader, new CaPackDuplicateFileResolver()); + Assert.That(loaded.GetFileCount(), Is.EqualTo(1)); + Assert.That(loaded.FindFile("renamed.txt"), Is.Not.Null); + } + + [Test] + public void CopyFileFromOtherPackFile_PublishesCorrectReference() + { + // Arrange: create a SystemFolderContainer as target + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + + File.WriteAllText(Path.Combine(_tempDir, "existing.txt"), "existing"); + var target = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + // Create a source PackFileContainer + var source = PackFileContainer.CreatePackFile("source"); + source.AddOrUpdateFile(@"scripts\new_script.lua", new PackFile("new_script.lua", new MemorySource("lua content"u8.ToArray()))); + + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(source); + pfs.AddContainer(target); + + // Act: copy from source to target + pfs.CopyFileFromOtherPackFile(source, @"scripts\new_script.lua", target); + + // Assert: the event should publish the reference that's actually stored in the container + eventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.Container == target && + e.AddedFiles.Count == 1 && + ReferenceEquals(e.AddedFiles[0], target.FindFile(@"scripts\new_script.lua")) + )), Times.Once); + + // The stored file should be a FileSystemSource (written to disk) + var storedFile = target.FindFile(@"scripts\new_script.lua")!; + Assert.That(storedFile.DataSource, Is.InstanceOf()); + Assert.That(storedFile.DataSource.ReadData(), Is.EqualTo("lua content"u8.ToArray())); + } + + [Test] + public void CopyFileFromOtherPackFile_FileIncludedInSavedPack() + { + // Arrange + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + + File.WriteAllText(Path.Combine(_tempDir, "initial.txt"), "initial"); + var target = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + var source = PackFileContainer.CreatePackFile("source"); + source.AddOrUpdateFile(@"data\added_file.bin", new PackFile("added_file.bin", new MemorySource(new byte[] { 1, 2, 3, 4 }))); + + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(source); + pfs.AddContainer(target); + + // Act: copy then save + pfs.CopyFileFromOtherPackFile(source, @"data\added_file.bin", target); + + var packPath = Path.Combine(_tempDir, "output.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + pfs.SavePackContainer(target, packPath, false, gameInfo); + + // Assert: load the saved pack and verify both files are present + using var fs = File.OpenRead(packPath); + using var reader = new BinaryReader(fs); + var loaded = PackFileSerializerLoader.Load(packPath, fs.Length, reader, new CaPackDuplicateFileResolver()); + + Assert.That(loaded.GetFileCount(), Is.EqualTo(2)); + Assert.That(loaded.FindFile(@"initial.txt"), Is.Not.Null); + Assert.That(loaded.FindFile(@"data\added_file.bin"), Is.Not.Null); + + var loadedData = loaded.FindFile(@"data\added_file.bin")!.DataSource.ReadData(); + Assert.That(loadedData, Is.EqualTo(new byte[] { 1, 2, 3, 4 })); + } + + [Test] + public void SaveToDisk_SortOrder_MatchesPackFileContainer() + { + // Arrange: create identical file sets in both container types + var filePaths = new[] + { + @"animations\battle\humanoid01.anim", + @"animations\skeletons\humanoid01.bone", + @"db\units_tables\data", + @"scripts\campaign\main.lua", + @"variantmeshes\wh_main\hum01.variantmeshdefinition", + }; + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + + // Create files on disk for SystemFolderContainer + foreach (var path in filePaths) + { + var absolutePath = Path.Combine(_tempDir, path); + Directory.CreateDirectory(Path.GetDirectoryName(absolutePath)!); + File.WriteAllText(absolutePath, $"content of {path}"); + } + + var sysContainer = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + // Create equivalent PackFileContainer + var packContainer = PackFileContainer.CreatePackFile("test", "test.pack"); + foreach (var path in filePaths) + packContainer.AddOrUpdateFile(path, PackFile.CreateFromASCII(Path.GetFileName(path), $"content of {path}")); + + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + + // Save SystemFolderContainer + var sysPackPath = Path.Combine(_tempDir, "sys_output.pack"); + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(sysContainer); + pfs.SavePackContainer(sysContainer, sysPackPath, false, gameInfo); + + // Save PackFileContainer + var normalPackPath = Path.Combine(_tempDir, "normal_output.pack"); + pfs.AddContainer(packContainer); + pfs.SavePackContainer(packContainer, normalPackPath, false, gameInfo); + + // Load both and compare file order + using var sysFs = File.OpenRead(sysPackPath); + using var sysReader = new BinaryReader(sysFs); + var loadedSys = PackFileSerializerLoader.Load(sysPackPath, sysFs.Length, sysReader, new CaPackDuplicateFileResolver()); + + using var normFs = File.OpenRead(normalPackPath); + using var normReader = new BinaryReader(normFs); + var loadedNorm = PackFileSerializerLoader.Load(normalPackPath, normFs.Length, normReader, new CaPackDuplicateFileResolver()); + + // Both should have the same files in the same order + var sysKeys = loadedSys.GetAllFiles().Keys.ToList(); + var normKeys = loadedNorm.GetAllFiles().Keys.ToList(); + + Assert.That(sysKeys.Count, Is.EqualTo(normKeys.Count), "File count mismatch"); + for (var i = 0; i < sysKeys.Count; i++) + Assert.That(sysKeys[i], Is.EqualTo(normKeys[i]), $"File order mismatch at index {i}"); + } + + // ────────────────────────────────────────────────────────────────────── + // B6 — Sort-order consistency between GetDirectoryContent, SearchFiles and + // the saved .pack. All three must use the same ordering so the tree, + // search results and persisted pack agree. Uses names that sort + // differently under ordinal vs current-culture rules (underscore, + // mixed case, digits). + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B6_GetDirectoryContent_SortOrder_IsOrdinal_AndConsistentWithSearchAndSavedPack() + { + // Arrange: root-level files whose ordinal and culture orderings differ + var rootFiles = new[] + { + "Zebra.txt", + "apple.txt", + "_underscore.txt", + "File1.txt", + "file10.txt", + "file2.txt", + }; + + foreach (var name in rootFiles) + File.WriteAllText(Path.Combine(_tempDir, name), $"content of {name}"); + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + var container = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + // Expected ordering: ordinal, matching PackFileSortHelper.PathComparer / serializer + var expectedOrder = rootFiles + .Select(n => n.ToLowerInvariant()) + .OrderBy(n => n, StringComparer.Ordinal) + .ToList(); + + // Act + var directoryOrder = container.GetDirectoryContent("").Select(x => x.Path).ToList(); + var searchOrder = container.SearchFiles(null, null).Select(x => x.Path).ToList(); + + // Assert: GetDirectoryContent matches the ordinal expectation ... + Assert.That(directoryOrder, Is.EqualTo(expectedOrder), + "GetDirectoryContent should sort using ordinal rules (consistent with the saved pack)."); + + // ... and is consistent with SearchFiles + Assert.That(directoryOrder, Is.EqualTo(searchOrder), + "GetDirectoryContent and SearchFiles must produce the same ordering."); + + // ... and with the persisted pack + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(container); + + var packPath = Path.Combine(_tempDir, "sorted_output.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + pfs.SavePackContainer(container, packPath, false, gameInfo); + + using var fs = File.OpenRead(packPath); + using var reader = new BinaryReader(fs); + var loaded = PackFileSerializerLoader.Load(packPath, fs.Length, reader, new CaPackDuplicateFileResolver()); + var savedOrder = loaded.GetAllFiles().Keys.ToList(); + + Assert.That(directoryOrder, Is.EqualTo(savedOrder), + "GetDirectoryContent ordering must match the order files are written into the .pack."); + } + + // ────────────────────────────────────────────────────────────────────── + // B7 — DeleteFolder with empty / whitespace input must NOT delete the + // entire source folder. Guards against the destructive root-delete. + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B7_DeleteFolder_EmptyOrWhitespace_DoesNotWipeSourceFolder() + { + // Arrange: seed files at root and in a subfolder + File.WriteAllText(Path.Combine(_tempDir, "keep.txt"), "keep"); + var subDir = Path.Combine(_tempDir, "sub"); + Directory.CreateDirectory(subDir); + File.WriteAllText(Path.Combine(subDir, "nested.txt"), "nested"); + + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + var container = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + Assert.That(container.GetFileCount(), Is.EqualTo(2)); + + // Act: attempt to delete an empty / whitespace folder + foreach (var input in new[] { "", " ", "\\" }) + container.DeleteFolder(input); + + // Assert: source folder and all files are intact on disk and in the container + Assert.That(Directory.Exists(_tempDir), Is.True, "Source folder must not be deleted."); + Assert.That(File.Exists(Path.Combine(_tempDir, "keep.txt")), Is.True, "Root file must survive."); + Assert.That(File.Exists(Path.Combine(subDir, "nested.txt")), Is.True, "Nested file must survive."); + Assert.That(container.GetFileCount(), Is.EqualTo(2), "Container must still track both files."); + } + + // ────────────────────────────────────────────────────────────────────── + // B15 — Path edge cases: spaces, mixed case, deep nesting. Add / rename / + // move / delete and save must round-trip; lookups stay case-insensitive. + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B15_PathEdgeCases_SpacesMixedCaseAndDeepNesting_RoundTrip() + { + var eventHub = new Mock(); + var fileSystem = CreateRealFileSystemAccess(); + var mockWatcher = new Mock(); + var container = new SystemFolderContainer(_tempDir, fileSystem, mockWatcher.Object, eventHub.Object); + + var pfs = new PackFileService(eventHub.Object); + pfs.MessageBoxProvider = new Mock().Object; + pfs.EnforceGameFilesMustBeLoaded = false; + pfs.AddContainer(container); + + // Add a file into a folder with spaces and mixed case + var spaced = PackFile.CreateFromASCII("Mixed Case.TXT", "spaced content"); + pfs.AddFilesToPack(container, [new NewPackFileEntry(@"My Folder", spaced)]); + + // Add a deeply nested file + var deep = PackFile.CreateFromASCII("leaf.bin", "deep content"); + pfs.AddFilesToPack(container, [new NewPackFileEntry(@"a\b\c\d\e\f", deep)]); + + // Lookups are case-insensitive (paths normalize to lower case) + Assert.That(container.ContainsFile(@"my folder\mixed case.txt"), Is.True); + Assert.That(container.ContainsFile(@"MY FOLDER\MIXED CASE.TXT"), Is.True); + Assert.That(container.ContainsFile(@"a\b\c\d\e\f\leaf.bin"), Is.True); + + // Disk reflects the paths + Assert.That(File.Exists(Path.Combine(_tempDir, "My Folder", "Mixed Case.TXT")), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "a", "b", "c", "d", "e", "f", "leaf.bin")), Is.True); + + // Rename the spaced file + var toRename = container.FindFile(@"my folder\mixed case.txt")!; + pfs.RenameFile(container, toRename, "Renamed File.txt"); + Assert.That(container.ContainsFile(@"my folder\renamed file.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "My Folder", "Renamed File.txt")), Is.True); + + // Save and reload — structure and content preserved + var packPath = Path.Combine(_tempDir, "edgecases.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + pfs.SavePackContainer(container, packPath, false, gameInfo); + + using var fs = File.OpenRead(packPath); + using var reader = new BinaryReader(fs); + var loaded = PackFileSerializerLoader.Load(packPath, fs.Length, reader, new CaPackDuplicateFileResolver()); + + Assert.That(loaded.FindFile(@"my folder\renamed file.txt"), Is.Not.Null); + Assert.That(loaded.FindFile(@"a\b\c\d\e\f\leaf.bin"), Is.Not.Null); + + var deepData = loaded.FindFile(@"a\b\c\d\e\f\leaf.bin")!.DataSource.ReadData(); + Assert.That(System.Text.Encoding.ASCII.GetString(deepData), Is.EqualTo("deep content")); + + container.Dispose(); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Read.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Read.cs new file mode 100644 index 000000000..d8d00865e --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Read.cs @@ -0,0 +1,288 @@ +using Moq; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.Services; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_Read + { + private string _tempDir = null!; + private Mock _fileSystemAccess = null!; + private SystemFolderContainer _container = null!; + + // Test file structure: + // folder/file.txt + // folder/sub/nested.bin + // root_file.txt + // models/unit.model + + private static readonly string[] TestRelativePaths = + [ + @"folder\file.txt", + @"folder\sub\nested.bin", + "root_file.txt", + @"models\unit.model", + ]; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SystemFolderContainerTest_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + // Create real files on disk so FileSystemSource can read sizes + foreach (var rel in TestRelativePaths) + { + var abs = Path.Combine(_tempDir, rel); + Directory.CreateDirectory(Path.GetDirectoryName(abs)!); + File.WriteAllText(abs, $"content of {rel}"); + } + + // Set up mock to return the file list + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(_tempDir)).Returns(true); + _fileSystemAccess + .Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns(TestRelativePaths.Select(r => Path.Combine(_tempDir, r)).ToArray()); + + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object); + } + + [TearDown] + public void TearDown() + { + _container.Dispose(); + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + [Test] + public void Constructor_ScansFolder_PopulatesFileList() + { + Assert.That(_container.GetFileCount(), Is.EqualTo(TestRelativePaths.Length)); + } + + [Test] + public void Constructor_SetsNameToFolderName() + { + var expectedName = Path.GetFileName(_tempDir); + Assert.That(_container.Name, Is.EqualTo(expectedName)); + } + + [Test] + public void Constructor_SetsSystemFilePath() + { + Assert.That(_container.SystemFilePath, Is.EqualTo(_tempDir)); + } + + [Test] + public void Constructor_IsCaPackFile_IsFalse() + { + Assert.That(_container.IsCaPackFile, Is.False); + } + + [Test] + public void Constructor_EmptyPath_Throws() + { + Assert.Throws(() => new SystemFolderContainer("", _fileSystemAccess.Object)); + } + + [Test] + public void Constructor_NonExistentDirectory_Throws() + { + var fs = new Mock(); + fs.Setup(x => x.DirectoryExists(It.IsAny())).Returns(false); + + Assert.Throws(() => new SystemFolderContainer(@"C:\nonexistent", fs.Object)); + } + + [Test] + public void FindFile_NormalizedPath_ReturnsPackFile() + { + var result = _container.FindFile(@"folder\file.txt"); + Assert.That(result, Is.Not.Null); + Assert.That(result!.Name, Is.EqualTo("file.txt")); + } + + [Test] + public void FindFile_CaseInsensitive_ReturnsPackFile() + { + var result = _container.FindFile(@"FOLDER\FILE.TXT"); + Assert.That(result, Is.Not.Null); + Assert.That(result!.Name, Is.EqualTo("file.txt")); + } + + [Test] + public void FindFile_NonExistent_ReturnsNull() + { + var result = _container.FindFile(@"does\not\exist.txt"); + Assert.That(result, Is.Null); + } + + [Test] + public void ContainsFile_ExistingFile_ReturnsTrue() + { + Assert.That(_container.ContainsFile(@"root_file.txt"), Is.True); + } + + [Test] + public void ContainsFile_MissingFile_ReturnsFalse() + { + Assert.That(_container.ContainsFile(@"missing.txt"), Is.False); + } + + [Test] + public void GetAllFiles_ReturnsAllScannedFiles() + { + var all = _container.GetAllFiles(); + Assert.That(all.Count, Is.EqualTo(TestRelativePaths.Length)); + } + + [Test] + public void GetFullPath_ReturnsCorrectRelativePath() + { + var file = _container.FindFile(@"models\unit.model"); + Assert.That(file, Is.Not.Null); + + var path = _container.GetFullPath(file!); + Assert.That(path, Is.EqualTo(@"models\unit.model")); + } + + [Test] + public void GetFullPath_UnknownFile_ReturnsNull() + { + var unknownFile = new PackFile("unknown.txt", null!); + var path = _container.GetFullPath(unknownFile); + Assert.That(path, Is.Null); + } + + [Test] + public void GetAllFilesByFolder_GroupsCorrectly() + { + var byFolder = _container.GetAllFilesByFolder(); + + Assert.That(byFolder.ContainsKey("folder"), Is.True); + Assert.That(byFolder["folder"], Does.Contain("file.txt")); + + Assert.That(byFolder.ContainsKey(@"folder\sub"), Is.True); + Assert.That(byFolder[@"folder\sub"], Does.Contain("nested.bin")); + + Assert.That(byFolder.ContainsKey(string.Empty), Is.True); + Assert.That(byFolder[string.Empty], Does.Contain("root_file.txt")); + + Assert.That(byFolder.ContainsKey("models"), Is.True); + Assert.That(byFolder["models"], Does.Contain("unit.model")); + } + + [Test] + public void FindAllWithExtention_ReturnsMatchingFiles() + { + var results = _container.FindAllWithExtention(".txt"); + Assert.That(results.Count, Is.EqualTo(2)); // folder\file.txt + root_file.txt + Assert.That(results.All(r => r.FileName.EndsWith(".txt")), Is.True); + } + + [Test] + public void FindAllWithExtention_NoMatch_ReturnsEmpty() + { + var results = _container.FindAllWithExtention(".xyz"); + Assert.That(results, Is.Empty); + } + + [Test] + public void SearchFiles_FilterByName_ReturnsMatch() + { + var results = _container.SearchFiles("nested", null); + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].File.Name, Is.EqualTo("nested.bin")); + } + + [Test] + public void SearchFiles_FilterByExtension_ReturnsMatch() + { + var results = _container.SearchFiles(null, [".model"]); + Assert.That(results.Count, Is.EqualTo(1)); + Assert.That(results[0].File.Name, Is.EqualTo("unit.model")); + } + + [Test] + public void SearchFiles_NoFilter_ReturnsAll() + { + var results = _container.SearchFiles(null, null); + Assert.That(results.Count, Is.EqualTo(TestRelativePaths.Length)); + } + + [Test] + public void GetDirectoryContent_RootLevel_ReturnsRootFiles() + { + var results = _container.GetDirectoryContent(""); + Assert.That(results.Count, Is.EqualTo(1)); // root_file.txt + Assert.That(results[0].File.Name, Is.EqualTo("root_file.txt")); + } + + [Test] + public void GetDirectoryContent_SubFolder_ReturnsDirectChildren() + { + var results = _container.GetDirectoryContent("folder"); + Assert.That(results.Count, Is.EqualTo(1)); // folder\file.txt (not nested) + Assert.That(results[0].File.Name, Is.EqualTo("file.txt")); + } + + [Test] + public void Dispose_ClearsFileList() + { + _container.Dispose(); + Assert.That(_container.GetFileCount(), Is.EqualTo(0)); + } + + [Test] + public void GetAllFilesByFolder_FilesWithinEachFolderAreSortedByOrdinal() + { + // Arrange: create a container with multiple files per folder in non-alphabetical order + var tempDir = Path.Combine(Path.GetTempPath(), "SortTest_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(tempDir); + + var files = new[] + { + @"audio\zombie.wem", + @"audio\attack.wem", + @"audio\music.wem", + @"scripts\main.lua", + @"scripts\ai.lua", + @"scripts\campaign.lua", + }; + + foreach (var rel in files) + { + var abs = Path.Combine(tempDir, rel); + Directory.CreateDirectory(Path.GetDirectoryName(abs)!); + File.WriteAllText(abs, "data"); + } + + var fs = new Mock(); + fs.Setup(x => x.DirectoryExists(tempDir)).Returns(true); + // Return files in reverse order to simulate non-sorted filesystem enumeration + fs.Setup(x => x.DirectoryGetFiles(tempDir, "*.*", SearchOption.AllDirectories)) + .Returns(files.Reverse().Select(r => Path.Combine(tempDir, r)).ToArray()); + + var container = new SystemFolderContainer(tempDir, fs.Object); + + try + { + // Act + var result = container.GetAllFilesByFolder(); + + // Assert: files within each folder are sorted by StringComparer.Ordinal + Assert.That(result["audio"], Is.EqualTo(new[] { "attack.wem", "music.wem", "zombie.wem" })); + Assert.That(result["scripts"], Is.EqualTo(new[] { "ai.lua", "campaign.lua", "main.lua" })); + } + finally + { + container.Dispose(); + Directory.Delete(tempDir, true); + } + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_SaveToDisk.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_SaveToDisk.cs new file mode 100644 index 000000000..b7ba474cf --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_SaveToDisk.cs @@ -0,0 +1,210 @@ +using Moq; +using Shared.Core.Events; +using Shared.Core.PackFiles.Events; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; +using Shared.Core.PackFiles.Serialization; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; +using Shared.Core.Settings; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_SaveToDisk + { + private string _tempDir = null!; + private string _outputDir = null!; + private SystemFolderContainer _container = null!; + private Mock _fileSystemAccess = null!; + private GameInformation _gameInfo = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderSave_" + Guid.NewGuid().ToString("N")); + _outputDir = Path.Combine(Path.GetTempPath(), "SysFolderSaveOutput_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + Directory.CreateDirectory(_outputDir); + + // Create test files + var fileA = Path.Combine(_tempDir, "folder", "fileA.txt"); + var fileB = Path.Combine(_tempDir, "fileB.bin"); + Directory.CreateDirectory(Path.GetDirectoryName(fileA)!); + File.WriteAllText(fileA, "content A"); + File.WriteAllBytes(fileB, new byte[] { 1, 2, 3, 4, 5 }); + + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(It.IsAny())) + .Returns((string p) => Directory.Exists(p)); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([fileA, fileB]); + _fileSystemAccess.Setup(x => x.FileExists(It.IsAny())) + .Returns((string p) => File.Exists(p)); + _fileSystemAccess.Setup(x => x.FileDelete(It.IsAny())) + .Callback((string p) => File.Delete(p)); + _fileSystemAccess.Setup(x => x.FileMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => File.Move(s, d, overwrite: true)); + + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object); + _gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + } + + [TearDown] + public void TearDown() + { + _container.Dispose(); + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + if (Directory.Exists(_outputDir)) + Directory.Delete(_outputDir, true); + } + + [Test] + public void SaveToDisk_GeneratesValidPackFile() + { + var outputPath = Path.Combine(_outputDir, "output.pack"); + + _container.SaveToDisk(outputPath, false, _gameInfo); + + Assert.That(File.Exists(outputPath), Is.True); + + // Verify the .pack can be reloaded + using var fileStream = File.OpenRead(outputPath); + using var reader = new BinaryReader(fileStream); + var loaded = PackFileSerializerLoader.Load(outputPath, fileStream.Length, reader, new CaPackDuplicateFileResolver()); + + Assert.That(loaded.GetFileCount(), Is.EqualTo(2)); + Assert.That(loaded.FindFile(@"folder\filea.txt"), Is.Not.Null); + Assert.That(loaded.FindFile(@"fileb.bin"), Is.Not.Null); + } + + [Test] + public void SaveToDisk_ContainerRemainsActive() + { + var outputPath = Path.Combine(_outputDir, "output.pack"); + + _container.SaveToDisk(outputPath, false, _gameInfo); + + // Container should still work normally + Assert.That(_container.GetFileCount(), Is.EqualTo(2)); + Assert.That(_container.FindFile(@"folder\fileA.txt"), Is.Not.Null); + Assert.That(_container.SystemFilePath, Is.EqualTo(_tempDir)); + } + + [Test] + public void SaveToDisk_CreateBackup_BackupCreated() + { + var outputPath = Path.Combine(_outputDir, "output.pack"); + + // First save - creates the file + _container.SaveToDisk(outputPath, false, _gameInfo); + Assert.That(File.Exists(outputPath), Is.True); + + // Second save with backup + _container.SaveToDisk(outputPath, true, _gameInfo); + + // Backup folder should exist with a file + var backupFolder = Path.Combine(_outputDir, "Backup"); + Assert.That(Directory.Exists(backupFolder), Is.True); + var backupFiles = Directory.GetFiles(backupFolder, "output*"); + Assert.That(backupFiles.Length, Is.GreaterThan(0)); + } + + [Test] + public void SaveToDisk_LockedFile_ThrowsIOException() + { + var outputPath = Path.Combine(_outputDir, "locked.pack"); + + // Create and lock the file + using var lockStream = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None); + + Assert.Throws(() => _container.SaveToDisk(outputPath, false, _gameInfo)); + } + + [Test] + public void SaveToDisk_FileContentPreserved() + { + var outputPath = Path.Combine(_outputDir, "output.pack"); + + _container.SaveToDisk(outputPath, false, _gameInfo); + + using var fileStream = File.OpenRead(outputPath); + using var reader = new BinaryReader(fileStream); + var loaded = PackFileSerializerLoader.Load(outputPath, fileStream.Length, reader, new CaPackDuplicateFileResolver()); + + var fileA = loaded.FindFile(@"folder\filea.txt"); + Assert.That(fileA, Is.Not.Null); + + var dataA = ((PackedFileSource)fileA!.DataSource).ReadData(fileStream); + Assert.That(System.Text.Encoding.UTF8.GetString(dataA), Is.EqualTo("content A")); + + var fileB = loaded.FindFile(@"fileb.bin"); + Assert.That(fileB, Is.Not.Null); + + var dataB = ((PackedFileSource)fileB!.DataSource).ReadData(fileStream); + Assert.That(dataB, Is.EqualTo(new byte[] { 1, 2, 3, 4, 5 })); + } + + // ────────────────────────────────────────────────────────────────────── + // B2 — Saving the pack INTO the watched folder must not ingest the produced + // .pack into the container itself (watcher must be suppressed during save). + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B2_SaveIntoWatchedFolder_DoesNotIngestOwnPack() + { + var mockWatcher = new Mock(); + var eventHub = new Mock(); + var outputPath = Path.Combine(_tempDir, "selfsave.pack"); + + // Simulate the OS watcher firing a Created event for the new pack while the + // save is still in progress (i.e. while suppression should be active). + _fileSystemAccess.Setup(x => x.FileMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => + { + File.Move(s, d, overwrite: true); + mockWatcher.Raise(w => w.Created += null, + new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, Path.GetFileName(d))); + }); + + var container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, mockWatcher.Object, eventHub.Object); + try + { + var initialCount = container.GetFileCount(); + + container.SaveToDisk(outputPath, false, _gameInfo); + container.ProcessPendingEvents(null); + + Assert.That(container.ContainsFile("selfsave.pack"), Is.False, + "The saved .pack must not be ingested into its own container."); + Assert.That(container.GetFileCount(), Is.EqualTo(initialCount)); + eventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + } + finally + { + container.Dispose(); + } + } + + // ────────────────────────────────────────────────────────────────────── + // B14 — The saved pack header version is PFH5 (current intended behaviour). + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B14_SaveToDisk_WritesPfh5Header() + { + var outputPath = Path.Combine(_outputDir, "versioned.pack"); + + _container.SaveToDisk(outputPath, false, _gameInfo); + + using var fileStream = File.OpenRead(outputPath); + using var reader = new BinaryReader(fileStream); + var loaded = PackFileSerializerLoader.Load(outputPath, fileStream.Length, reader, new CaPackDuplicateFileResolver()); + + Assert.That(loaded.Header, Is.Not.Null); + Assert.That(loaded.Header!.Version, Is.EqualTo(PackFileVersion.PFH5)); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Watcher.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Watcher.cs new file mode 100644 index 000000000..7bee26cb3 --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Watcher.cs @@ -0,0 +1,511 @@ +using Moq; +using Shared.Core.Events; +using Shared.Core.PackFiles.Events; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_Watcher + { + private string _tempDir = null!; + private Mock _fileSystemAccess = null!; + private Mock _mockWatcher = null!; + private Mock _mockEventHub = null!; + private SystemFolderContainer _container = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderWatcher_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + // Create a seed file + var seedPath = Path.Combine(_tempDir, "existing.txt"); + File.WriteAllText(seedPath, "seed"); + + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(_tempDir)).Returns(true); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([seedPath]); + _fileSystemAccess.Setup(x => x.DirectoryExists(It.IsAny())) + .Returns((string p) => Directory.Exists(p)); + _fileSystemAccess.Setup(x => x.DirectoryCreateDirectory(It.IsAny())) + .Callback((string p) => Directory.CreateDirectory(p)); + _fileSystemAccess.Setup(x => x.FileWriteAllBytes(It.IsAny(), It.IsAny())) + .Callback((string p, byte[] d) => { Directory.CreateDirectory(Path.GetDirectoryName(p)!); File.WriteAllBytes(p, d); }); + _fileSystemAccess.Setup(x => x.FileExists(It.IsAny())) + .Returns((string p) => File.Exists(p)); + _fileSystemAccess.Setup(x => x.FileDelete(It.IsAny())) + .Callback((string p) => File.Delete(p)); + + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + } + + [TearDown] + public void TearDown() + { + _container.Dispose(); + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + [Test] + public void Constructor_StartsWatcher() + { + _mockWatcher.VerifySet(w => w.Path = _tempDir); + _mockWatcher.VerifySet(w => w.IncludeSubdirectories = true); + _mockWatcher.VerifySet(w => w.EnableRaisingEvents = true); + } + + [Test] + public void ExternalFileCreated_PublishesFilesAddedEvent() + { + // Create the file on disk so FileSystemSource can work + var newFilePath = Path.Combine(_tempDir, "newfile.txt"); + File.WriteAllText(newFilePath, "new content"); + + // Simulate the watcher event + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "newfile.txt")); + + // Process debounced events immediately + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "newfile.txt" + )), Times.Once); + + Assert.That(_container.ContainsFile("newfile.txt"), Is.True); + } + + [Test] + public void ExternalFileDeleted_PublishesFilesRemovedEvent() + { + // Simulate deletion of the existing file + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "existing.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "existing.txt" + )), Times.Once); + + Assert.That(_container.ContainsFile("existing.txt"), Is.False); + } + + [Test] + public void ExternalFileRenamed_PublishesRemovedAndAddedEvents() + { + // Create the renamed file on disk + var renamedPath = Path.Combine(_tempDir, "renamed.txt"); + File.WriteAllText(renamedPath, "seed"); + + // Simulate rename event + var args = new RenamedEventArgs(WatcherChangeTypes.Renamed, _tempDir, "renamed.txt", "existing.txt"); + _mockWatcher.Raise(w => w.Renamed += null, args); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "existing.txt" + )), Times.Once); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "renamed.txt" + )), Times.Once); + + Assert.That(_container.ContainsFile("existing.txt"), Is.False); + Assert.That(_container.ContainsFile("renamed.txt"), Is.True); + } + + [Test] + public void InternalAdd_DoesNotTriggerExternalEvent() + { + // Create file on disk so it could be picked up + var filePath = Path.Combine(_tempDir, "suppressed.txt"); + File.WriteAllText(filePath, "data"); + + // Simulate watcher firing while suppression is active (e.g., during internal write) + _container._suppressWatcher = true; + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "suppressed.txt")); + _container._suppressWatcher = false; + + _container.ProcessPendingEvents(null); + + // The suppressed event should not have been queued + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + // File should NOT be in container since the event was suppressed + Assert.That(_container.ContainsFile("suppressed.txt"), Is.False); + } + + [Test] + public void InternalDelete_DoesNotTriggerExternalEvent() + { + _container._suppressWatcher = true; + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "existing.txt")); + _container._suppressWatcher = false; + + _container.ProcessPendingEvents(null); + + // Event should not be published since it was suppressed + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + // File should still be in the list + Assert.That(_container.ContainsFile("existing.txt"), Is.True); + } + + [Test] + public void MultipleRapidCreates_BatchedIntoSingleEvent() + { + // Create files on disk + var file1 = Path.Combine(_tempDir, "batch1.txt"); + var file2 = Path.Combine(_tempDir, "batch2.txt"); + File.WriteAllText(file1, "1"); + File.WriteAllText(file2, "2"); + + // Simulate multiple rapid creates + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "batch1.txt")); + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "batch2.txt")); + + // Process them all at once + _container.ProcessPendingEvents(null); + + // Should be a single event with both files + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 2 + )), Times.Once); + } + + [Test] + public void Dispose_StopsWatcher() + { + _container.Dispose(); + + _mockWatcher.VerifySet(w => w.EnableRaisingEvents = false); + _mockWatcher.Verify(w => w.Dispose(), Times.Once); + } + + [Test] + public void ExternalFileCreated_DuplicatePath_IgnoredGracefully() + { + // existing.txt is already in the container + // Try to trigger a Created event for the same path + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "existing.txt")); + + _container.ProcessPendingEvents(null); + + // Should not publish event since file is already tracked + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + } + + [Test] + public void ExternalFileDeleted_UnknownFile_IgnoredGracefully() + { + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "unknown.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + } + + [Test] + public void ExternalFileCreated_InSubdirectory_PublishesFilesAddedEvent() + { + var subDir = Path.Combine(_tempDir, "models"); + Directory.CreateDirectory(subDir); + var newFilePath = Path.Combine(subDir, "hero.mesh"); + File.WriteAllText(newFilePath, "mesh data"); + + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, subDir, "hero.mesh")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "hero.mesh" + )), Times.Once); + + Assert.That(_container.ContainsFile(@"models\hero.mesh"), Is.True); + } + + [Test] + public void ExternalFolderDeleted_RemovesAllContainedFiles() + { + // Set up container with files in a subfolder + var subDir = Path.Combine(_tempDir, "scripts"); + Directory.CreateDirectory(subDir); + var file1 = Path.Combine(subDir, "main.lua"); + var file2 = Path.Combine(subDir, "utils.lua"); + File.WriteAllText(file1, "lua1"); + File.WriteAllText(file2, "lua2"); + + // Re-create container with the subfolder files + _container.Dispose(); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([Path.Combine(_tempDir, "existing.txt"), file1, file2]); + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + Assert.That(_container.GetFileCount(), Is.EqualTo(3)); + + // Simulate folder deletion event — watcher fires a single event for the folder + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "scripts")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 2 + )), Times.Once); + + Assert.That(_container.GetFileCount(), Is.EqualTo(1)); + Assert.That(_container.ContainsFile("existing.txt"), Is.True); + Assert.That(_container.ContainsFile(@"scripts\main.lua"), Is.False); + Assert.That(_container.ContainsFile(@"scripts\utils.lua"), Is.False); + } + + [Test] + public void ExternalFolderRenamed_RemovesOldAndAddsNewFiles() + { + // Set up container with files in a subfolder + var oldDir = Path.Combine(_tempDir, "oldfolder"); + var newDir = Path.Combine(_tempDir, "newfolder"); + Directory.CreateDirectory(oldDir); + var oldFile = Path.Combine(oldDir, "data.bin"); + File.WriteAllText(oldFile, "binary"); + + _container.Dispose(); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([Path.Combine(_tempDir, "existing.txt"), oldFile]); + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + Assert.That(_container.GetFileCount(), Is.EqualTo(2)); + + // Simulate the folder rename on disk (move oldfolder to newfolder) + Directory.Move(oldDir, newDir); + + // Watcher raises a single Renamed event for the folder + var args = new RenamedEventArgs(WatcherChangeTypes.Renamed, _tempDir, "newfolder", "oldfolder"); + _mockWatcher.Raise(w => w.Renamed += null, args); + + _container.ProcessPendingEvents(null); + + // Old file should be removed + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "data.bin" + )), Times.Once); + + // New file should be added from the renamed folder + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "data.bin" + )), Times.Once); + + Assert.That(_container.ContainsFile(@"oldfolder\data.bin"), Is.False); + Assert.That(_container.ContainsFile(@"newfolder\data.bin"), Is.True); + } + + [Test] + public void ExternalFileRenamed_InSubdirectory_UpdatesCorrectly() + { + // Set up container with a file in subfolder + var subDir = Path.Combine(_tempDir, "textures"); + Directory.CreateDirectory(subDir); + var oldFile = Path.Combine(subDir, "diffuse.dds"); + File.WriteAllText(oldFile, "texture"); + + _container.Dispose(); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([Path.Combine(_tempDir, "existing.txt"), oldFile]); + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + // Rename on disk + var newFile = Path.Combine(subDir, "normal.dds"); + File.Move(oldFile, newFile); + + // Watcher event + var args = new RenamedEventArgs(WatcherChangeTypes.Renamed, subDir, "normal.dds", "diffuse.dds"); + _mockWatcher.Raise(w => w.Renamed += null, args); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "diffuse.dds" + )), Times.Once); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "normal.dds" + )), Times.Once); + + Assert.That(_container.ContainsFile(@"textures\diffuse.dds"), Is.False); + Assert.That(_container.ContainsFile(@"textures\normal.dds"), Is.True); + } + + [Test] + public void MultipleRapidDeletes_BatchedIntoSingleEvent() + { + // Set up container with multiple files + var file1 = Path.Combine(_tempDir, "a.txt"); + var file2 = Path.Combine(_tempDir, "b.txt"); + File.WriteAllText(file1, "a"); + File.WriteAllText(file2, "b"); + + _container.Dispose(); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([file1, file2]); + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + // Simulate rapid deletes + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "a.txt")); + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "b.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 2 + )), Times.Once); + + Assert.That(_container.GetFileCount(), Is.EqualTo(0)); + } + + [Test] + public void MixedCreateAndDelete_InSameBatch_PublishesBothEvents() + { + // Create a new file on disk + var newFile = Path.Combine(_tempDir, "added.txt"); + File.WriteAllText(newFile, "new"); + + // Simulate: existing file deleted + new file created in same debounce window + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "existing.txt")); + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "added.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "existing.txt" + )), Times.Once); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "added.txt" + )), Times.Once); + + Assert.That(_container.ContainsFile("existing.txt"), Is.False); + Assert.That(_container.ContainsFile("added.txt"), Is.True); + } + + [Test] + public void ExternalDirectoryCreated_AddsAllFilesWithin() + { + // Simulate copying a folder into the watched directory + var newDir = Path.Combine(_tempDir, "imported"); + Directory.CreateDirectory(newDir); + File.WriteAllText(Path.Combine(newDir, "model.rmv2"), "mesh"); + File.WriteAllText(Path.Combine(newDir, "texture.dds"), "tex"); + + // Watcher fires Created for the directory + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "imported")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 2 + )), Times.Once); + + Assert.That(_container.ContainsFile(@"imported\model.rmv2"), Is.True); + Assert.That(_container.ContainsFile(@"imported\texture.dds"), Is.True); + } + + // ────────────────────────────────────────────────────────────────────── + // B10 — Chatty / duplicate watcher events must not produce duplicate + // entries in the published add/remove payloads. + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B10_DuplicateDeleteEvents_RemovedFilePublishedOnce() + { + // Watcher commonly raises the same Deleted event more than once. + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "existing.txt")); + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "existing.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count == 1 && e.RemovedFiles[0].Name == "existing.txt" + )), Times.Once); + + Assert.That(_container.ContainsFile("existing.txt"), Is.False); + } + + [Test] + public void B10_DuplicateCreateEvents_AddedFilePublishedOnce() + { + var newFilePath = Path.Combine(_tempDir, "dup.txt"); + File.WriteAllText(newFilePath, "data"); + + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "dup.txt")); + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "dup.txt")); + + _container.ProcessPendingEvents(null); + + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.AddedFiles.Count == 1 && e.AddedFiles[0].Name == "dup.txt" + )), Times.Once); + } + + [Test] + public void B10_FolderDeletePlusChildDelete_FilePublishedOnce() + { + // Re-create container with a subfolder file + var subDir = Path.Combine(_tempDir, "folder"); + Directory.CreateDirectory(subDir); + var child = Path.Combine(subDir, "child.txt"); + File.WriteAllText(child, "child"); + + _container.Dispose(); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([Path.Combine(_tempDir, "existing.txt"), child]); + _mockWatcher = new Mock(); + _mockEventHub = new Mock(); + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _mockEventHub.Object); + + // Both a folder delete and the child file delete arrive in the same batch. + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, _tempDir, "folder")); + _mockWatcher.Raise(w => w.Deleted += null, new FileSystemEventArgs(WatcherChangeTypes.Deleted, subDir, "child.txt")); + + _container.ProcessPendingEvents(null); + + // child.txt must be reported exactly once, not twice. + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => + e.RemovedFiles.Count(f => f.Name == "child.txt") == 1 + )), Times.Once); + + Assert.That(_container.ContainsFile(@"folder\child.txt"), Is.False); + } + + // ────────────────────────────────────────────────────────────────────── + // B12 — Watcher activity around dispose must not throw or publish. + // ────────────────────────────────────────────────────────────────────── + + [Test] + public void B12_ProcessPendingEvents_AfterDispose_DoesNotThrowOrPublish() + { + // Queue an event, then dispose before processing. + var newFilePath = Path.Combine(_tempDir, "late.txt"); + File.WriteAllText(newFilePath, "late"); + _mockWatcher.Raise(w => w.Created += null, new FileSystemEventArgs(WatcherChangeTypes.Created, _tempDir, "late.txt")); + + _container.Dispose(); + + Assert.DoesNotThrow(() => _container.ProcessPendingEvents(null)); + _mockEventHub.Verify(x => x.PublishGlobalEvent(It.IsAny()), Times.Never); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Write.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Write.cs new file mode 100644 index 000000000..3fe52f330 --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Models/Containers/SystemFolderContainerTests_Write.cs @@ -0,0 +1,225 @@ +using Moq; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; +using Shared.Core.Services; + +namespace Shared.CoreTest.PackFiles.Models.Containers +{ + [TestFixture] + internal class SystemFolderContainerTests_Write + { + private string _tempDir = null!; + private SystemFolderContainer _container = null!; + private Mock _fileSystemAccess = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderWrite_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + // Seed with one file + var seedPath = Path.Combine(_tempDir, "existing", "seed.txt"); + Directory.CreateDirectory(Path.GetDirectoryName(seedPath)!); + File.WriteAllText(seedPath, "seed content"); + + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(_tempDir)).Returns(true); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([seedPath]); + + // Default pass-through for directory/file operations + _fileSystemAccess.Setup(x => x.DirectoryExists(It.IsAny())) + .Returns((string p) => Directory.Exists(p)); + _fileSystemAccess.Setup(x => x.DirectoryCreateDirectory(It.IsAny())) + .Callback((string p) => Directory.CreateDirectory(p)); + _fileSystemAccess.Setup(x => x.FileWriteAllBytes(It.IsAny(), It.IsAny())) + .Callback((string p, byte[] d) => { Directory.CreateDirectory(Path.GetDirectoryName(p)!); File.WriteAllBytes(p, d); }); + _fileSystemAccess.Setup(x => x.FileExists(It.IsAny())) + .Returns((string p) => File.Exists(p)); + _fileSystemAccess.Setup(x => x.FileDelete(It.IsAny())) + .Callback((string p) => File.Delete(p)); + _fileSystemAccess.Setup(x => x.FileMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => { Directory.CreateDirectory(Path.GetDirectoryName(d)!); File.Move(s, d); }); + _fileSystemAccess.Setup(x => x.DirectoryMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => Directory.Move(s, d)); + _fileSystemAccess.Setup(x => x.DirectoryDelete(It.IsAny(), It.IsAny())) + .Callback((string p, bool r) => Directory.Delete(p, r)); + + _container = new SystemFolderContainer(_tempDir, _fileSystemAccess.Object); + } + + [TearDown] + public void TearDown() + { + _container.Dispose(); + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + [Test] + public void AddOrUpdateFile_WritesFileToDisk() + { + var data = "hello world"u8.ToArray(); + var file = new PackFile("new.txt", new MemorySource(data)); + + _container.AddOrUpdateFile(@"folder\new.txt", file); + + var absolutePath = Path.Combine(_tempDir, "folder", "new.txt"); + Assert.That(File.Exists(absolutePath), Is.True); + Assert.That(File.ReadAllBytes(absolutePath), Is.EqualTo(data)); + } + + [Test] + public void AddOrUpdateFile_UpdatesFileList() + { + var data = "test"u8.ToArray(); + var file = new PackFile("new.txt", new MemorySource(data)); + + _container.AddOrUpdateFile(@"folder\new.txt", file); + + Assert.That(_container.ContainsFile(@"folder\new.txt"), Is.True); + } + + [Test] + public void AddOrUpdateFile_EmptyName_Throws() + { + var file = new PackFile("", new MemorySource([1, 2, 3])); + Assert.Throws(() => _container.AddOrUpdateFile(@"folder\", file)); + } + + [Test] + public void AddOrUpdateFile_SuppressesWatcher() + { + var data = "test"u8.ToArray(); + var file = new PackFile("new.txt", new MemorySource(data)); + + bool wasSuppressed = false; + _fileSystemAccess.Setup(x => x.FileWriteAllBytes(It.IsAny(), It.IsAny())) + .Callback((string p, byte[] d) => { wasSuppressed = _container._suppressWatcher; File.WriteAllBytes(p, d); }); + + _container.AddOrUpdateFile(@"test\new.txt", file); + + Assert.That(wasSuppressed, Is.True); + Assert.That(_container._suppressWatcher, Is.False); // restored after + } + + [Test] + public void AddFiles_MultipleFiles_AllWrittenToDisk() + { + var newFiles = new List + { + new("dir", new PackFile("a.txt", new MemorySource("aaa"u8.ToArray()))), + new("", new PackFile("root.txt", new MemorySource("bbb"u8.ToArray()))), + }; + + var added = _container.AddFiles(newFiles); + + Assert.That(added.Count, Is.EqualTo(2)); + Assert.That(_container.ContainsFile(@"dir\a.txt"), Is.True); + Assert.That(_container.ContainsFile("root.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "dir", "a.txt")), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "root.txt")), Is.True); + } + + [Test] + public void AddFiles_EmptyFileName_Throws() + { + var newFiles = new List + { + new("dir", new PackFile("", new MemorySource([1]))), + }; + + Assert.Throws(() => _container.AddFiles(newFiles)); + } + + [Test] + public void DeleteFile_RemovesFromDiskAndFileList() + { + var seedFile = _container.FindFile(@"existing\seed.txt"); + Assert.That(seedFile, Is.Not.Null); + + var result = _container.DeleteFile(seedFile!); + + Assert.That(result, Is.Not.Null); + Assert.That(_container.ContainsFile(@"existing\seed.txt"), Is.False); + Assert.That(File.Exists(Path.Combine(_tempDir, "existing", "seed.txt")), Is.False); + } + + [Test] + public void DeleteFile_NonExistentFile_ReturnsNull() + { + var unknownFile = new PackFile("nope.txt", new MemorySource([1])); + var result = _container.DeleteFile(unknownFile); + Assert.That(result, Is.Null); + } + + [Test] + public void DeleteFolder_RemovesRecursively() + { + // Add more files in a folder + var data = "x"u8.ToArray(); + _container.AddOrUpdateFile(@"todelete\a.txt", new PackFile("a.txt", new MemorySource(data))); + _container.AddOrUpdateFile(@"todelete\sub\b.txt", new PackFile("b.txt", new MemorySource(data))); + + _container.DeleteFolder("todelete"); + + Assert.That(_container.ContainsFile(@"todelete\a.txt"), Is.False); + Assert.That(_container.ContainsFile(@"todelete\sub\b.txt"), Is.False); + Assert.That(Directory.Exists(Path.Combine(_tempDir, "todelete")), Is.False); + } + + [Test] + public void MoveFile_UpdatesDiskAndFileList() + { + var file = _container.FindFile(@"existing\seed.txt")!; + _container.MoveFile(file, "newfolder"); + + Assert.That(_container.ContainsFile(@"existing\seed.txt"), Is.False); + Assert.That(_container.ContainsFile(@"newfolder\seed.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "newfolder", "seed.txt")), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "existing", "seed.txt")), Is.False); + } + + [Test] + public void RenameFile_UpdatesDiskAndFileList() + { + var file = _container.FindFile(@"existing\seed.txt")!; + _container.RenameFile(file, "renamed.txt"); + + Assert.That(_container.ContainsFile(@"existing\seed.txt"), Is.False); + Assert.That(_container.ContainsFile(@"existing\renamed.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "existing", "renamed.txt")), Is.True); + } + + [Test] + public void RenameDirectory_UpdatesAllChildPaths() + { + // Add files in a directory + var data = "x"u8.ToArray(); + _container.AddOrUpdateFile(@"olddir\a.txt", new PackFile("a.txt", new MemorySource(data))); + _container.AddOrUpdateFile(@"olddir\sub\b.txt", new PackFile("b.txt", new MemorySource(data))); + + _container.RenameDirectory("olddir", "newdir"); + + Assert.That(_container.ContainsFile(@"olddir\a.txt"), Is.False); + Assert.That(_container.ContainsFile(@"olddir\sub\b.txt"), Is.False); + Assert.That(_container.ContainsFile(@"newdir\a.txt"), Is.True); + Assert.That(_container.ContainsFile(@"newdir\sub\b.txt"), Is.True); + } + + [Test] + public void SaveFileData_WritesNewContent() + { + var file = _container.FindFile(@"existing\seed.txt")!; + var newData = "updated content"u8.ToArray(); + + _container.SaveFileData(file, newData); + + var absolutePath = Path.Combine(_tempDir, "existing", "seed.txt"); + Assert.That(File.ReadAllBytes(absolutePath), Is.EqualTo(newData)); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileServiceTest.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileServiceTest.cs index 5c822cf5e..0e8bc0eb9 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileServiceTest.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileServiceTest.cs @@ -1,7 +1,7 @@ using Moq; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; using Shared.Core.PackFiles.Models.FileSources; @@ -22,7 +22,7 @@ private static PackFileService CreateService(Mock? eventHub = n private static PackFileService CreateServiceWithCaPack(Mock? eventHub = null) { var pfs = CreateService(eventHub); - var ca = new PackFileContainer("CaPack") { IsCaPackFile = true, SystemFilePath = "ca_path" }; + var ca = PackFileContainer.CreateCaPackFile("CaPack", "ca_path"); pfs.AddContainer(ca); return pfs; } @@ -32,10 +32,7 @@ public void AddContainer_CaPackFile() { var eventHub = new Mock(); var pfs = new PackFileService(eventHub.Object); - var container = new PackFileContainer("MyTest"); - container.SystemFilePath = "SystemPath"; - container.IsCaPackFile = true; - + var container = PackFileContainer.CreateCaPackFile("MyTest", "SystemPath"); pfs.AddContainer(container); var containers = pfs.GetAllPackfileContainers(); @@ -51,10 +48,7 @@ public void AddContainer_CaPackFileNotSet() var dialogProvider = new Mock(); var pfs = new PackFileService(eventHub.Object); pfs.MessageBoxProvider = dialogProvider.Object; - var container = new PackFileContainer("MyTest"); - container.SystemFilePath = "SystemPath"; - container.IsCaPackFile = false; - + var container = PackFileContainer.CreatePackFile("MyTest", "SystemPath"); pfs.AddContainer(container); var containers = pfs.GetAllPackfileContainers(); @@ -69,13 +63,8 @@ public void AddContainer_CaPackFileSet() { var eventHub = new Mock(); var pfs = new PackFileService(eventHub.Object); - var caContainer = new PackFileContainer("MyTest"); - caContainer.SystemFilePath = "SystemPath"; - caContainer.IsCaPackFile = true; - - var customContainer = new PackFileContainer("MyTest"); - customContainer.SystemFilePath = "SystemPath2"; - customContainer.IsCaPackFile = false; + var caContainer = PackFileContainer.CreateCaPackFile("MyTest", "SystemPath"); + var customContainer = PackFileContainer.CreatePackFile("MyTest", "SystemPath2"); pfs.AddContainer(caContainer); pfs.AddContainer(customContainer, true); @@ -95,13 +84,8 @@ public void AddContainers_Duplicate() var dialogProvider = new Mock(); var pfs = new PackFileService(eventHub.Object); pfs.MessageBoxProvider = dialogProvider.Object; - var caContainer = new PackFileContainer("MyTest"); - caContainer.SystemFilePath = "SystemPath"; - caContainer.IsCaPackFile = true; - - var customContainer = new PackFileContainer("MyTest"); - customContainer.SystemFilePath = "SystemPath2"; - customContainer.IsCaPackFile = false; + var caContainer = PackFileContainer.CreateCaPackFile("MyTest", "Systempath"); + var customContainer = PackFileContainer.CreatePackFile("MyTest", "SystemPath2"); pfs.AddContainer(caContainer); pfs.AddContainer(customContainer, true); @@ -118,11 +102,9 @@ public void CreateNewPackFileContainer() { var eventHub = new Mock(); var pfs = new PackFileService(eventHub.Object); - var container = new PackFileContainer("MyTest"); - container.SystemFilePath = "SystemPath"; - container.IsCaPackFile = true; - + var container = PackFileContainer.CreateCaPackFile("MyTest", "SystemPath"); pfs.AddContainer(container); + var emptyPackFileContainer = pfs.CreateNewPackFileContainer("Custom", PackFileVersion.PFH5, PackFileCAType.MOD, true); var containers = pfs.GetAllPackfileContainers(); @@ -144,7 +126,7 @@ public void CreateNewPackFileContainer_EmptyName_Throws() public void SaveFile_NoEditablePack_ThrowsDescriptiveException() { var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("Ca") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("Ca", "SystemPath")); var file = new PackFile("file.txt", new MemorySource([1, 2, 3])); var ex = Assert.Throws(() => pfs.SaveFile(file, [4, 5, 6])); @@ -157,11 +139,11 @@ public void AddContainer_TwoContainersWithNullSystemFilePath_BothAdded() var dialogProvider = new Mock(); var pfs = new PackFileService(null); pfs.MessageBoxProvider = dialogProvider.Object; - var ca = new PackFileContainer("Ca") { IsCaPackFile = true }; + var ca = PackFileContainer.CreateCaPackFile("Ca", "SystemPath"); pfs.AddContainer(ca); - var container1 = new PackFileContainer("Pack1") { SystemFilePath = null }; - var container2 = new PackFileContainer("Pack2") { SystemFilePath = null }; + var container1 = PackFileContainer.CreatePackFile("Pack1"); + var container2 = PackFileContainer.CreatePackFile("Pack2"); pfs.AddContainer(container1); var result = pfs.AddContainer(container2); @@ -176,7 +158,7 @@ public void AddContainer_EnforceGameFilesDisabled_AllowsNonCaFirst() var pfs = CreateService(); pfs.EnforceGameFilesMustBeLoaded = false; - var container = new PackFileContainer("Custom") { IsCaPackFile = false, SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); var result = pfs.AddContainer(container); Assert.That(result, Is.Not.Null); @@ -197,7 +179,7 @@ public void SetEditablePack_Null_ClearsSelection() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var custom = new PackFileContainer("Custom") { SystemFilePath = "custom_path" }; + var custom = PackFileContainer.CreatePackFile("Custom", "SystemPath"); pfs.AddContainer(custom, true); Assert.That(pfs.GetEditablePack(), Is.EqualTo(custom)); @@ -210,7 +192,7 @@ public void UnloadPackContainer_RemovesContainer() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var custom = new PackFileContainer("Custom") { SystemFilePath = "custom_path" }; + var custom = PackFileContainer.CreatePackFile("Custom", "SystemPath"); pfs.AddContainer(custom, true); pfs.UnloadPackContainer(custom); @@ -224,8 +206,8 @@ public void UnloadPackContainer_RemovesContainer() public void UnloadPackContainer_NonEditablePack_DoesNotClearEditable() { var pfs = CreateServiceWithCaPack(); - var pack1 = new PackFileContainer("Pack1") { SystemFilePath = "path1" }; - var pack2 = new PackFileContainer("Pack2") { SystemFilePath = "path2" }; + var pack1 = PackFileContainer.CreatePackFile("Pack1", "path1"); + var pack2 = PackFileContainer.CreatePackFile("Pack2", "path2"); pfs.AddContainer(pack1, true); pfs.AddContainer(pack2); @@ -240,7 +222,7 @@ public void AddFilesToPack_AddsFiles() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "SystemPath"); pfs.AddContainer(container, true); var newFiles = new List @@ -273,11 +255,11 @@ public void CopyFileFromOtherPackFile_CopiesData() var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var source = new PackFileContainer("Source") { SystemFilePath = "source_path" }; + var source = PackFileContainer.CreatePackFile("Source", "source_path"); source.AddOrUpdateFile("data\\file.bin", new PackFile("file.bin", new MemorySource([10, 20, 30]))); pfs.AddContainer(source); - var target = new PackFileContainer("Target") { SystemFilePath = "target_path" }; + var target = PackFileContainer.CreatePackFile("Target", "target_path"); pfs.AddContainer(target, true); pfs.CopyFileFromOtherPackFile(source, "data\\file.bin", target); @@ -292,9 +274,9 @@ public void CopyFileFromOtherPackFile_CopiesData() public void CopyFileFromOtherPackFile_MissingFile_DoesNothing() { var pfs = CreateServiceWithCaPack(); - var source = new PackFileContainer("Source") { SystemFilePath = "source_path" }; + var source = PackFileContainer.CreatePackFile("Source", "source_path"); pfs.AddContainer(source); - var target = new PackFileContainer("Target") { SystemFilePath = "target_path" }; + var target = PackFileContainer.CreatePackFile("Target", "target_path"); pfs.AddContainer(target, true); pfs.CopyFileFromOtherPackFile(source, "nonexistent\\file.bin", target); @@ -307,7 +289,7 @@ public void CopyFileFromOtherPackFile_TargetIsCaPack_Throws() { var pfs = CreateServiceWithCaPack(); var caPack = pfs.GetAllPackfileContainers().First(); - var source = new PackFileContainer("Source") { SystemFilePath = "source_path" }; + var source = PackFileContainer.CreatePackFile("Source", "source_path"); source.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(source); @@ -319,7 +301,7 @@ public void DeleteFile_RemovesFile() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("folder\\test.txt", new PackFile("test.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -345,7 +327,7 @@ public void DeleteFolder_RemovesFiles() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("folder\\a.txt", new PackFile("a.txt", new MemorySource([1]))); container.AddOrUpdateFile("folder\\b.txt", new PackFile("b.txt", new MemorySource([2]))); container.AddOrUpdateFile("other\\c.txt", new PackFile("c.txt", new MemorySource([3]))); @@ -372,7 +354,7 @@ public void MoveFile_ChangesPath() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("old\\test.txt", new PackFile("test.txt", new MemorySource([1, 2]))); pfs.AddContainer(container, true); @@ -398,7 +380,7 @@ public void RenameFile_ChangesName() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("folder\\old.txt", new PackFile("old.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -413,7 +395,7 @@ public void RenameFile_ChangesName() public void RenameFile_EmptyName_Throws() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -437,7 +419,7 @@ public void RenameDirectory_ChangesPath() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("oldname\\file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -452,7 +434,7 @@ public void RenameDirectory_ChangesPath() public void RenameDirectory_EmptyName_Throws() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); pfs.AddContainer(container, true); Assert.Throws(() => pfs.RenameDirectory(container, "folder", "")); @@ -471,7 +453,7 @@ public void RenameDirectory_CaPack_Throws() public void FindFile_FindsAcrossContainers() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("data\\file.txt", new PackFile("file.txt", new MemorySource([1, 2]))); pfs.AddContainer(container, true); @@ -492,11 +474,11 @@ public void FindFile_ReturnsNullForMissing() public void FindFile_SpecificContainer_OnlySearchesThat() { var pfs = CreateServiceWithCaPack(); - var container1 = new PackFileContainer("Pack1") { SystemFilePath = "path1" }; + var container1 = PackFileContainer.CreatePackFile("Pack1", "path1"); container1.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container1); - var container2 = new PackFileContainer("Pack2") { SystemFilePath = "path2" }; + var container2 = PackFileContainer.CreatePackFile("Pack2", "path2"); pfs.AddContainer(container2); var found = pfs.FindFile("file.txt", container2); @@ -510,11 +492,11 @@ public void FindFile_SpecificContainer_OnlySearchesThat() public void FindFile_LaterContainerTakesPriority() { var pfs = CreateServiceWithCaPack(); - var container1 = new PackFileContainer("Pack1") { SystemFilePath = "path1" }; + var container1 = PackFileContainer.CreatePackFile("Pack1", "path1"); container1.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container1); - var container2 = new PackFileContainer("Pack2") { SystemFilePath = "path2" }; + var container2 = PackFileContainer.CreatePackFile("Pack2", "path2"); container2.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([2]))); pfs.AddContainer(container2); @@ -526,7 +508,7 @@ public void FindFile_LaterContainerTakesPriority() public void FindAllWithExtention_SearchesAllContainers() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("a.txt", new PackFile("a.txt", new MemorySource([1]))); container.AddOrUpdateFile("b.txt", new PackFile("b.txt", new MemorySource([2]))); container.AddOrUpdateFile("c.bin", new PackFile("c.bin", new MemorySource([3]))); @@ -540,11 +522,11 @@ public void FindAllWithExtention_SearchesAllContainers() public void FindAllWithExtention_SpecificContainer() { var pfs = CreateServiceWithCaPack(); - var container1 = new PackFileContainer("Pack1") { SystemFilePath = "path1" }; + var container1 = PackFileContainer.CreatePackFile("Pack1", "path1"); container1.AddOrUpdateFile("a.txt", new PackFile("a.txt", new MemorySource([1]))); pfs.AddContainer(container1); - var container2 = new PackFileContainer("Pack2") { SystemFilePath = "path2" }; + var container2 = PackFileContainer.CreatePackFile("Pack2", "path2"); container2.AddOrUpdateFile("b.txt", new PackFile("b.txt", new MemorySource([2]))); pfs.AddContainer(container2); @@ -556,7 +538,7 @@ public void FindAllWithExtention_SpecificContainer() public void GetFullPath_ReturnsPath() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("folder\\file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -578,7 +560,7 @@ public void GetFullPath_UnknownFile_Throws() public void GetPackFileContainer_ReturnsCorrectContainer() { var pfs = CreateServiceWithCaPack(); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([1]))); pfs.AddContainer(container, true); @@ -602,7 +584,7 @@ public void SaveFile_UpdatesFileData() { var eventHub = new Mock(); var pfs = CreateServiceWithCaPack(eventHub); - var container = new PackFileContainer("Custom") { SystemFilePath = "path" }; + var container = PackFileContainer.CreatePackFile("Custom", "path"); container.AddOrUpdateFile("file.txt", new PackFile("file.txt", new MemorySource([1, 2, 3]))); pfs.AddContainer(container, true); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_AddFilesToPack.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_AddFilesToPack.cs index 69fb12531..622556b42 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_AddFilesToPack.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_AddFilesToPack.cs @@ -11,9 +11,9 @@ public void AddFilesToPack_MultipleFiles() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -36,9 +36,9 @@ public void AddFilesToPack_AddToRoot() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -57,9 +57,9 @@ public void AddFilesToPack_AddToFolder() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath"}, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -83,9 +83,9 @@ public void AddFilesToPack_FileNameConflict_override() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -108,9 +108,9 @@ public void AddFilesToPack_WhiteSpaceInName() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -131,9 +131,9 @@ public void AddFilesToPack_NoFileName() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); - var container = pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { @@ -150,7 +150,25 @@ public void AddFilesToPack_CAPackFile() { // Arrange var pfs = new PackFileService(null); - var caPack = pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + var caPack = pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); + + var newFiles = new List + { + new("Directory_0", new PackFile("file0", null)), + }; + + // Act + Assert.Throws(() => pfs.AddFilesToPack(caPack, newFiles)); + } + + + [Test] + public void AddFilesToPack_ReadonlyPackFile() + { + // Arrange + var pfs = new PackFileService(null); + pfs.EnforceGameFilesMustBeLoaded = false; + var caPack = pfs.AddContainer(PackFileContainer.CreateReadOnlyPackFile("CaPackFile")); var newFiles = new List { diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_DeleteFolder.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_DeleteFolder.cs index dfc4aaaa7..6bd93bfdf 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_DeleteFolder.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/PackFileService_DeleteFolder.cs @@ -11,7 +11,7 @@ public void DeleteFolder() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); var container = CreateTestPack(pfs); // Act @@ -26,7 +26,7 @@ public void DeleteMissingFolder() // THere might be nodes in the tree view that { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); var container = CreateTestPack(pfs); // Act @@ -41,7 +41,7 @@ public void DeleteFolderWithSubFolder() { // Arrange var pfs = new PackFileService(null); - pfs.AddContainer(new PackFileContainer("CaPackFile") { IsCaPackFile = true }); + pfs.AddContainer(PackFileContainer.CreateCaPackFile("CaPackFile")); var container = CreateTestPack(pfs); // Act @@ -54,7 +54,7 @@ public void DeleteFolderWithSubFolder() static PackFileContainer CreateTestPack(IPackFileService pfs) { - var container = (PackFileContainer)pfs.AddContainer(new PackFileContainer("Custom") { SystemFilePath = "SystemPath" }, true)!; + var container = (PackFileContainer)pfs.AddContainer(PackFileContainer.CreatePackFile("Custom", "SystemPath"), true)!; var newFiles = new List { new("Directory_0", new PackFile("file0.txt", null)), diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileContainerCacheHelperTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileContainerCacheHelperTests.cs index 9947d4eb4..4d452473f 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileContainerCacheHelperTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileContainerCacheHelperTests.cs @@ -1,4 +1,4 @@ -using Microsoft.Data.Sqlite; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; @@ -69,12 +69,7 @@ private static void SaveCache(string fingerprint, PackFileContainer container, D public void RoundTrip_PreservesMetadata() { // Arrange - var container = new PackFileContainer("All Game Packs - TestGame") - { - IsCaPackFile = true, - SystemFilePath = @"c:\game\data" - }; - + var container = PackFileContainer.CreateCaPackFile("All Game Packs - TestGame", @"c:\game\data"); container.SourcePackFilePaths.Add(@"c:\game\data\pack1.pack"); container.SourcePackFilePaths.Add(@"c:\game\data\pack2.pack"); @@ -104,12 +99,7 @@ public void RoundTrip_PreservesMetadata() public void LoadCache_ReturnsCorrectFileData() { // Arrange - var container = new PackFileContainer("Test Container") - { - IsCaPackFile = true, - SystemFilePath = @"c:\game\data" - }; - + var container = PackFileContainer.CreateCaPackFile("Test Container", @"c:\game\data"); container.SourcePackFilePaths.Add(@"c:\game\data\pack1.pack"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\data\pack1.pack" }; @@ -151,11 +141,7 @@ public void LoadCache_ReturnsCorrectFileData() [Test] public void LoadCache_PreservesSourcePackFilePath() { - var container = new PackFileContainer("Test") - { - SystemFilePath = @"c:\game" - }; - + var container = PackFileContainer.CreatePackFile("Test", @"c:\game"); var parent = new PackedFileSourceParent { FilePath = @"c:\pack.pack" }; container.AddOrUpdateFile("a.txt", new PackFile("a.txt", new PackedFileSource(parent, 0, 10, false, false, CompressionFormat.None, 0))); @@ -183,10 +169,7 @@ public void LoadCache_ReturnsNullForMissingFile() [Test] public void LoadCache_ReturnsNullForWrongFingerprint() { - var container = new PackFileContainer("Test") - { - SystemFilePath = @"c:\game" - }; + var container = PackFileContainer.CreatePackFile("Test", @"c:\game"); var dbOptions = CreateTestDbOptions(); SaveCache("correct_fp", container, dbOptions); @@ -229,11 +212,7 @@ public void ComputeFingerprint_ChangesWhenFileChanges() public void RoundTrip_FullCycle() { // Arrange - var container = new PackFileContainer("Full Cycle Test") - { - IsCaPackFile = true, - SystemFilePath = @"c:\game\data" - }; + var container = PackFileContainer.CreateCaPackFile("Full Cycle Test", @"c:\game\data"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\data\main.pack" }; container.SourcePackFilePaths.Add(parent.FilePath); @@ -251,7 +230,7 @@ public void RoundTrip_FullCycle() // Assert Assert.That(restored, Is.Not.Null); Assert.That(restored.Name, Is.EqualTo("Full Cycle Test")); - Assert.That(restored.IsCaPackFile, Is.True); + Assert.That(restored.IsReadOnly, Is.True); Assert.That(restored.SystemFilePath, Is.EqualTo(@"c:\game\data")); Assert.That(restored.GetFileCount(), Is.EqualTo(2)); @@ -269,10 +248,7 @@ public void RoundTrip_FullCycle() [Test] public void TryLoadFromCache_ReturnsContainerWhenValid() { - var container = new PackFileContainer("TryLoad Test") - { - SystemFilePath = @"c:\game\data" - }; + var container = PackFileContainer.CreatePackFile("TryLoad Test", @"c:\game\data"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\data\pack.pack" }; container.AddOrUpdateFile("test\\file.txt", new PackFile("file.txt", @@ -305,10 +281,7 @@ public void TryLoadFromCache_ReturnsNullForCorruptFile() [Test] public void SaveCache_PreservesEncryptedFlag() { - var container = new PackFileContainer("Encrypted Test") - { - SystemFilePath = @"c:\game" - }; + var container = PackFileContainer.CreatePackFile("Encrypted Test", @"c:\game"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\encrypted.pack" }; container.AddOrUpdateFile("secret\\data.bin", new PackFile("data.bin", @@ -324,10 +297,7 @@ public void SaveCache_PreservesEncryptedFlag() [Test] public void SaveCache_EmptyContainer_RoundTrips() { - var container = new PackFileContainer("Empty Pack") - { - SystemFilePath = @"c:\game\empty" - }; + var container = PackFileContainer.CreatePackFile("Empty Pack", @"c:\game\empty"); var dbOptions = CreateTestDbOptions(); SaveCache("fp_empty", container, dbOptions); var loaded = CachedPackFileContainer.CreateFromFingerPrint(dbOptions, "fp_empty"); @@ -343,13 +313,13 @@ public void SaveCache_OverwritesExistingCache() var parent = new PackedFileSourceParent { FilePath = @"c:\game\pack.pack" }; var dbOptions = CreateFileDbOptions(); // Save first version - var container1 = new PackFileContainer("Version1") { SystemFilePath = @"c:\game" }; + var container1 = PackFileContainer.CreatePackFile("Version1", @"c:\game"); container1.AddOrUpdateFile("old.txt", new PackFile("old.txt", new PackedFileSource(parent, 0, 10, false, false, CompressionFormat.None, 0))); SaveCache("fp1", container1, dbOptions); // Save second version (same db path) - var container2 = new PackFileContainer("Version2") { SystemFilePath = @"c:\game" }; + var container2 = PackFileContainer.CreatePackFile("Version2", @"c:\game"); container2.AddOrUpdateFile("new.txt", new PackFile("new.txt", new PackedFileSource(parent, 0, 20, false, false, CompressionFormat.None, 0))); SaveCache("fp2", container2, dbOptions); @@ -369,10 +339,7 @@ public void SaveCache_OverwritesExistingCache() [Test] public void SaveCache_StoresFolderPathCorrectly() { - var container = new PackFileContainer("FolderPath Test") - { - SystemFilePath = @"c:\game" - }; + var container = PackFileContainer.CreatePackFile("FolderPath Test", @"c:\game"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\pack.pack" }; container.AddOrUpdateFile("a\\folder_marker.txt", new PackFile("folder_marker.txt", @@ -399,10 +366,7 @@ public void SaveCache_StoresFolderPathCorrectly() [Test] public void SaveCache_SkipsNonPackedFileSources() { - var container = new PackFileContainer("Mixed Sources") - { - SystemFilePath = @"c:\game" - }; + var container = PackFileContainer.CreatePackFile("Mixed Sources", @"c:\game"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\pack.pack" }; container.AddOrUpdateFile("packed.txt", new PackFile("packed.txt", diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileSerializerWriterTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileSerializerWriterTests.cs index 6522b9439..a09c193d5 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileSerializerWriterTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Serialization/PackFileSerializerWriterTests.cs @@ -55,12 +55,7 @@ public void PackFileSerializerWriterTests_GameWithoutCompression( // Create packfile with the above files var outputContainerName = @"c:\fullpath\to\packfile.pack"; - var packFileHeader = PackFileVersionConverter.ToString(outputPackFileVersion); - var container = new PackFileContainer("test") - { - Header = new PFHeader(packFileHeader, PackFileCAType.MOD), - SystemFilePath = "test.pack" - }; + var container = PackFileContainer.CreatePackFile("test", "test.pack", outputPackFileVersion); foreach (var fileInfo in expectedFileInfo) container.AddOrUpdateFile(fileInfo.FilePath, PackFile.CreateFromASCII(fileInfo.FileName, new string(fileInfo.Content, fileInfo.Length))); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/SystemFolderContainer_PackFileServiceTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/SystemFolderContainer_PackFileServiceTests.cs new file mode 100644 index 000000000..55137eb9c --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/SystemFolderContainer_PackFileServiceTests.cs @@ -0,0 +1,180 @@ +using Moq; +using Shared.Core.Events; +using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; +using Shared.Core.PackFiles.Models; +using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Models.FileSources; +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; +using Shared.Core.Settings; + +namespace Shared.CoreTest.PackFiles +{ + [TestFixture] + internal class SystemFolderContainer_PackFileServiceTests + { + private string _tempDir = null!; + private Mock _eventHub = null!; + private Mock _fileSystemAccess = null!; + private Mock _mockWatcher = null!; + private PackFileService _pfs = null!; + + [SetUp] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "SysFolderPFS_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + + // Seed a file + var seedPath = Path.Combine(_tempDir, "data", "file.txt"); + Directory.CreateDirectory(Path.GetDirectoryName(seedPath)!); + File.WriteAllText(seedPath, "hello"); + + _fileSystemAccess = new Mock(); + _fileSystemAccess.Setup(x => x.DirectoryExists(It.IsAny())) + .Returns((string p) => Directory.Exists(p)); + _fileSystemAccess.Setup(x => x.DirectoryGetFiles(_tempDir, "*.*", SearchOption.AllDirectories)) + .Returns([seedPath]); + _fileSystemAccess.Setup(x => x.DirectoryCreateDirectory(It.IsAny())) + .Callback((string p) => Directory.CreateDirectory(p)); + _fileSystemAccess.Setup(x => x.FileWriteAllBytes(It.IsAny(), It.IsAny())) + .Callback((string p, byte[] d) => { Directory.CreateDirectory(Path.GetDirectoryName(p)!); File.WriteAllBytes(p, d); }); + _fileSystemAccess.Setup(x => x.FileExists(It.IsAny())) + .Returns((string p) => File.Exists(p)); + _fileSystemAccess.Setup(x => x.FileDelete(It.IsAny())) + .Callback((string p) => File.Delete(p)); + _fileSystemAccess.Setup(x => x.FileMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => { Directory.CreateDirectory(Path.GetDirectoryName(d)!); File.Move(s, d); }); + _fileSystemAccess.Setup(x => x.DirectoryMove(It.IsAny(), It.IsAny())) + .Callback((string s, string d) => Directory.Move(s, d)); + _fileSystemAccess.Setup(x => x.DirectoryDelete(It.IsAny(), It.IsAny())) + .Callback((string p, bool r) => Directory.Delete(p, r)); + + _mockWatcher = new Mock(); + _eventHub = new Mock(); + + _pfs = new PackFileService(_eventHub.Object); + _pfs.MessageBoxProvider = new Mock().Object; + _pfs.EnforceGameFilesMustBeLoaded = false; + } + + [TearDown] + public void TearDown() + { + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, true); + } + + private SystemFolderContainer CreateContainer() + { + return new SystemFolderContainer(_tempDir, _fileSystemAccess.Object, _mockWatcher.Object, _eventHub.Object); + } + + [Test] + public void AddContainer_SystemFolder_RegistersSuccessfully() + { + var container = CreateContainer(); + + var result = _pfs.AddContainer(container); + + Assert.That(result, Is.Not.Null); + Assert.That(_pfs.GetAllPackfileContainers(), Does.Contain(container)); + _eventHub.Verify(x => x.PublishGlobalEvent(It.Is(e => e.Container == container)), Times.Once); + } + + [Test] + public void AddContainer_DuplicateFolderPath_Rejected() + { + var container1 = CreateContainer(); + _pfs.AddContainer(container1); + + var container2 = CreateContainer(); + var result = _pfs.AddContainer(container2); + + Assert.That(result, Is.Null); + Assert.That(_pfs.GetAllPackfileContainers().Count, Is.EqualTo(1)); + } + + [Test] + public void UnloadContainer_DisposesWatcher() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + + _pfs.UnloadPackContainer(container); + + _mockWatcher.VerifySet(w => w.EnableRaisingEvents = false); + _mockWatcher.Verify(w => w.Dispose(), Times.Once); + Assert.That(_pfs.GetAllPackfileContainers(), Does.Not.Contain(container)); + } + + [Test] + public void SetEditablePack_SystemFolder_Works() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + + _pfs.SetEditablePack(container); + + Assert.That(_pfs.GetEditablePack(), Is.EqualTo(container)); + } + + [Test] + public void AddFilesToPack_SystemFolder_WritesThrough() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + + var newFile = new PackFile("added.txt", new MemorySource("new content"u8.ToArray())); + var entries = new List { new("newdir", newFile) }; + + _pfs.AddFilesToPack(container, entries); + + Assert.That(container.ContainsFile(@"newdir\added.txt"), Is.True); + Assert.That(File.Exists(Path.Combine(_tempDir, "newdir", "added.txt")), Is.True); + } + + [Test] + public void DeleteFile_SystemFolder_DeletesFromDisk() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + var file = container.FindFile(@"data\file.txt"); + Assert.That(file, Is.Not.Null); + + _pfs.DeleteFile(container, file!); + + Assert.That(container.ContainsFile(@"data\file.txt"), Is.False); + Assert.That(File.Exists(Path.Combine(_tempDir, "data", "file.txt")), Is.False); + } + + [Test] + public void SavePackContainer_SystemFolder_GeneratesPackFile() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + + var outputPath = Path.Combine(_tempDir, "output.pack"); + var gameInfo = GameInformationDatabase.GetGameById(GameTypeEnum.Warhammer3); + + _pfs.SavePackContainer(container, outputPath, false, gameInfo); + + Assert.That(File.Exists(outputPath), Is.True); + // Container should remain active + Assert.That(container.GetFileCount(), Is.EqualTo(1)); + } + + [Test] + public void FindFile_AcrossContainers_FindsInSystemFolder() + { + var container = CreateContainer(); + _pfs.AddContainer(container); + + var result = _pfs.FindFile(@"data\file.txt"); + + Assert.That(result, Is.Not.Null); + Assert.That(result!.Name, Is.EqualTo("file.txt")); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/Services/FileSaveServiceTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSaveServiceTests.cs similarity index 97% rename from Shared/SharedCore/Shared.CoreTest/Services/FileSaveServiceTests.cs rename to Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSaveServiceTests.cs index 2b1b439db..462f15b77 100644 --- a/Shared/SharedCore/Shared.CoreTest/Services/FileSaveServiceTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSaveServiceTests.cs @@ -2,15 +2,16 @@ using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.Containers; +using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.Core.Settings; using Test.TestingUtility.TestUtility; -namespace Shared.CoreTest.Services +namespace Shared.CoreTest.PackFiles.Utility { internal class FileSaveServiceTests { @@ -29,10 +30,8 @@ public void Setup() var dialogProvider = new Mock(); _pfs = new PackFileService(_eventHub.Object); (_pfs as PackFileService).MessageBoxProvider = dialogProvider.Object; - var container = new PackFileContainer("MyTest"); - container.SystemFilePath = "SystemPath"; - container.IsCaPackFile = true; - + var container = PackFileContainer.CreateCaPackFile("MyTest", "SystemPath"); + _pfs.AddContainer(container); _container = _pfs.CreateNewPackFileContainer("Output", PackFileVersion.PFH5, PackFileCAType.MOD, true); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSystemWatcherWrapperTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSystemWatcherWrapperTests.cs new file mode 100644 index 000000000..4d927eb90 --- /dev/null +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/FileSystemWatcherWrapperTests.cs @@ -0,0 +1,56 @@ +using Shared.Core.PackFiles.Utility; +using Shared.Core.Services; + +namespace Shared.CoreTest.PackFiles.Utility +{ + [TestFixture] + internal class FileSystemWatcherWrapperTests + { + [Test] + public void Dispose_DoesNotThrow() + { + var wrapper = new FileSystemWatcherWrapper(); + Assert.DoesNotThrow(() => wrapper.Dispose()); + } + + [Test] + public void SetPath_PropagatesCorrectly() + { + using var wrapper = new FileSystemWatcherWrapper(); + var tempDir = Path.GetTempPath(); + + wrapper.Path = tempDir; + + Assert.That(wrapper.Path, Is.EqualTo(tempDir)); + } + + [Test] + public void EnableRaisingEvents_DefaultFalse() + { + using var wrapper = new FileSystemWatcherWrapper(); + wrapper.Path = Path.GetTempPath(); + + Assert.That(wrapper.EnableRaisingEvents, Is.False); + } + + [Test] + public void IncludeSubdirectories_DefaultFalse() + { + using var wrapper = new FileSystemWatcherWrapper(); + wrapper.Path = Path.GetTempPath(); + + Assert.That(wrapper.IncludeSubdirectories, Is.False); + } + + [Test] + public void IncludeSubdirectories_SetTrue_PropagatesCorrectly() + { + using var wrapper = new FileSystemWatcherWrapper(); + wrapper.Path = Path.GetTempPath(); + + wrapper.IncludeSubdirectories = true; + + Assert.That(wrapper.IncludeSubdirectories, Is.True); + } + } +} diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileContainerLoaderTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileContainerLoaderTests.cs index a0c86d2b5..cf458677c 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileContainerLoaderTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileContainerLoaderTests.cs @@ -1,5 +1,6 @@ using Moq; using Shared.Core.Misc; +using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Utility; using Shared.Core.Services; using Shared.Core.Settings; @@ -49,7 +50,7 @@ public void TearDown() Directory.Delete(_tempGameDir, true); } - private PackFileContainerLoader CreateLoader() => new PackFileContainerLoader(_settingsService, _dialogs.Object, _localizationManager, _cacheHelper); + private PackFileContainerLoader CreateLoader() => new PackFileContainerLoader(_settingsService, _dialogs.Object, _localizationManager, _cacheHelper, new SimpleSystemFolderContainerFactory()); [Test] public void LoadAllCaFiles_MissingGameDirectory_ShowsErrorAndSkipsBuild() @@ -57,7 +58,7 @@ public void LoadAllCaFiles_MissingGameDirectory_ShowsErrorAndSkipsBuild() _settingsService.CurrentSettings.GameDirectories.Clear(); var loader = CreateLoader(); - var result = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var result = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(result, Is.Null); } @@ -67,7 +68,7 @@ public void LoadAllCaFiles_NoCacheExists_ShowsNotFoundDialogAndWaitCursor() { var loader = CreateLoader(); - var result = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var result = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(result, Is.Not.Null); @@ -85,13 +86,13 @@ public void LoadAllCaFiles_CacheExists_NoDialogShown() var loader = CreateLoader(); // First call builds the cache - var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(firstResult, Is.Not.Null); _dialogs.Invocations.Clear(); // Second call should use cache - no dialogs - var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(secondResult, Is.Not.Null); _dialogs.Verify(d => d.ShowDialogBox(It.IsAny(), It.IsAny()), Times.Never); @@ -104,7 +105,7 @@ public void LoadAllCaFiles_CacheCorrupted_ShowsCorruptedDialog() var loader = CreateLoader(); // First call builds the cache - var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(firstResult, Is.Not.Null); // Corrupt the cache via the in-memory helper @@ -116,7 +117,7 @@ public void LoadAllCaFiles_CacheCorrupted_ShowsCorruptedDialog() _dialogs.Invocations.Clear(); // Load again — should detect corruption - var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(secondResult, Is.Not.Null); // Single combined dialog: reason + building message @@ -130,7 +131,7 @@ public void LoadAllCaFiles_DataChanged_ShowsDialogsAndRebuildsCache() var loader = CreateLoader(); // First call builds the cache - var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var firstResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(firstResult, Is.Not.Null); // Add a new pack file to the game dir to change the fingerprint @@ -140,7 +141,7 @@ public void LoadAllCaFiles_DataChanged_ShowsDialogsAndRebuildsCache() _dialogs.Invocations.Clear(); // Load again — fingerprint changed, new fingerprint has no cache file - var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + var secondResult = loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(secondResult, Is.Not.Null); // Single combined dialog: reason + building message @@ -174,7 +175,7 @@ public void LoadAllCaFiles_WaitCursorActive_DuringBuild() .Callback(() => waitCursorDisposed = true); var loader = CreateLoader(); - loader.CreateFromGameEnum(PackFileContainerType.Cached, GameTypeEnum.Warhammer3); + loader.CreateFromGameEnum(PackFileContainerType.Database, GameTypeEnum.Warhammer3); Assert.That(dialogShownBeforeWaitCursor, Is.True, "Dialog should be shown before wait cursor starts"); Assert.That(waitCursorCreated, Is.True, "Wait cursor should have been created"); diff --git a/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileServiceUtilityTests.cs b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileServiceUtilityTests.cs index 054f64e3e..5faefda48 100644 --- a/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileServiceUtilityTests.cs +++ b/Shared/SharedCore/Shared.CoreTest/PackFiles/Utility/PackFileServiceUtilityTests.cs @@ -48,7 +48,7 @@ public void GetDirectoryContent_MissingDirectory_ReturnsEmpty() private static PackFileContainer CreateContainer() { - var container = new PackFileContainer("Test"); + var container = PackFileContainer.CreatePackFile("Test"); var parent = new PackedFileSourceParent { FilePath = @"c:\game\p.pack" }; container.AddOrUpdateFile(@"root.txt", new PackFile("root.txt", new PackedFileSource(parent, 0, 1, false, false, CompressionFormat.None, 0))); diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ClosePackContainerFileCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ClosePackContainerFileCommand.cs index 77aa2a088..cf26a169a 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ClosePackContainerFileCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ClosePackContainerFileCommand.cs @@ -1,8 +1,6 @@ +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CollapseNodeCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CollapseNodeCommand.cs index 9f4710bc8..548a3c237 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CollapseNodeCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CollapseNodeCommand.cs @@ -1,6 +1,4 @@ -using Serilog; -using Shared.Core.ErrorHandling; -using Shared.Core.PackFiles.Models; +using Shared.Core.ErrorHandling; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands { diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyNodePathCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyNodePathCommand.cs index eaa0e99e2..6d6158849 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyNodePathCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyNodePathCommand.cs @@ -1,6 +1,4 @@ -using System.Windows; -using Shared.Core.PackFiles.Models; -using Serilog; +using System.Windows; using Shared.Core.ErrorHandling; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyToEditablePackCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyToEditablePackCommand.cs index 3d1353195..8a4938960 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyToEditablePackCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CopyToEditablePackCommand.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.Services; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CreateFolderCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CreateFolderCommand.cs index 1669d631a..782c6e9d7 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CreateFolderCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/CreateFolderCommand.cs @@ -1,9 +1,6 @@ -using System.Linq; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -16,7 +13,7 @@ public class CreateFolderCommand(IPackFileService packFileService, IStandardDial public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType != NodeType.File && container is { IsCaPackFile: false }; + return node.NodeType != NodeType.File && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; @@ -38,10 +35,10 @@ public void Execute() return; } - if (container.IsCaPackFile) + if (container.IsReadOnly) { - _logger.Here().Warning($"Create folder blocked for CA pack '{CommandLoggingHelper.DescribePack(container)}'"); - standardDialogs.ShowDialogBox("Unable to edit CA packfile"); + _logger.Here().Warning($"Create folder blocked for readonly pack '{CommandLoggingHelper.DescribePack(container)}'"); + standardDialogs.ShowDialogBox("Unable to edit readonly packfile"); return; } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DeleteNodeCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DeleteNodeCommand.cs index 7078b829f..8720c1a9d 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DeleteNodeCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DeleteNodeCommand.cs @@ -1,8 +1,7 @@ +using Serilog; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -16,7 +15,7 @@ public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); var packFile = TreeNodeHelper.GetPackFile(node); - return container is { IsCaPackFile: false } && ((node.NodeType == NodeType.File && packFile != null) || node.NodeType == NodeType.Directory); + return container is { IsReadOnly: false } && ((node.NodeType == NodeType.File && packFile != null) || node.NodeType == NodeType.Directory); } public bool IsEnabled(TreeNode node) => true; @@ -38,10 +37,10 @@ public void Execute() return; } - if (container.IsCaPackFile) + if (container.IsReadOnly) { - _logger.Here().Warning($"Delete blocked for CA pack node '{CommandLoggingHelper.DescribeNode(_node)}'"); - standardDialogs.ShowDialogBox("Unable to edit CA packfile", "Error"); + _logger.Here().Warning($"Delete blocked for readonly pack node '{CommandLoggingHelper.DescribeNode(_node)}'"); + standardDialogs.ShowDialogBox("Unable to edit readonly packfile", "Error"); return; } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DuplicateFileCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DuplicateFileCommand.cs index c5e0dec80..6925daa47 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DuplicateFileCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/DuplicateFileCommand.cs @@ -1,10 +1,9 @@ -using System.IO; +using System.IO; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -18,7 +17,7 @@ public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); var packFile = TreeNodeHelper.GetPackFile(node); - return node.NodeType == NodeType.File && packFile != null && container is { IsCaPackFile: false }; + return node.NodeType == NodeType.File && packFile != null && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExpandNodeCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExpandNodeCommand.cs index 73f6d8077..441bb9b6a 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExpandNodeCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExpandNodeCommand.cs @@ -1,6 +1,5 @@ -using Serilog; +using Serilog; using Shared.Core.ErrorHandling; -using Shared.Core.PackFiles.Models; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands { diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExportToDirectoryCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExportToDirectoryCommand.cs index 9175d4831..af62178b4 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExportToDirectoryCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ExportToDirectoryCommand.cs @@ -1,11 +1,8 @@ -using System; -using System.IO; +using Shared.Core.ErrorHandling; using Shared.Core.Misc; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportDirectoryCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportDirectoryCommand.cs index ae5e2f027..43b930500 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportDirectoryCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportDirectoryCommand.cs @@ -1,12 +1,9 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -19,7 +16,7 @@ public class ImportDirectoryCommand(IPackFileService packFileService, IStandardD public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType != NodeType.File && container is { IsCaPackFile: false }; + return node.NodeType != NodeType.File && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; @@ -41,10 +38,10 @@ public void Execute() return; } - if (container.IsCaPackFile) + if (container.IsReadOnly) { - _logger.Here().Warning($"Import directory blocked for CA pack '{CommandLoggingHelper.DescribePack(container)}'"); - standardDialogs.ShowDialogBox("Unable to edit CA packfile"); + _logger.Here().Warning($"Import directory blocked for readonly pack '{CommandLoggingHelper.DescribePack(container)}'"); + standardDialogs.ShowDialogBox("Unable to edit readonly packfile"); return; } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportFilesCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportFilesCommand.cs index f09168a4b..43ec8428d 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportFilesCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/ImportFilesCommand.cs @@ -1,10 +1,9 @@ -using System.IO; +using System.IO; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.PackFiles.Models; using Shared.Core.PackFiles.Models.FileSources; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -17,7 +16,7 @@ public class ImportFileCommand(IPackFileService packFileService, IStandardDialog public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType != NodeType.File && container is { IsCaPackFile: false }; + return node.NodeType != NodeType.File && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; @@ -39,10 +38,10 @@ public void Execute() return; } - if (container.IsCaPackFile) + if (container.IsReadOnly) { - _logger.Here().Warning($"Import file blocked for CA pack '{CommandLoggingHelper.DescribePack(container)}'"); - standardDialogs.ShowDialogBox("Unable to edit CA packfile"); + _logger.Here().Warning($"Import file blocked for readonly pack '{CommandLoggingHelper.DescribePack(container)}'"); + standardDialogs.ShowDialogBox("Unable to edit readonly packfile"); return; } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenNodeInCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenNodeInCommand.cs index 8587e0c05..ff9bb43db 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenNodeInCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenNodeInCommand.cs @@ -1,10 +1,7 @@ -using System; -using System.Diagnostics; -using System.IO; +using System.IO; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenPackInFileExplorerCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenPackInFileExplorerCommand.cs index 2c30200f8..bb54f2760 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenPackInFileExplorerCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/OpenPackInFileExplorerCommand.cs @@ -1,9 +1,7 @@ -using System.Diagnostics; +using System.Diagnostics; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; using Shared.Core.Services; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -16,7 +14,7 @@ public class OpenPackInFileExplorerCommand(IPackFileService packFileService, ISt public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType != NodeType.File && container is { IsCaPackFile: false }; + return node.NodeType != NodeType.File && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/RenameNodeCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/RenameNodeCommand.cs index 6b6c972f1..f8dcf9870 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/RenameNodeCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/RenameNodeCommand.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Serilog; +using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; using Shared.Core.Services; @@ -17,7 +16,7 @@ public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); var packFile = TreeNodeHelper.GetPackFile(node); - return container is { IsCaPackFile: false } && ((node.NodeType == NodeType.File && packFile != null) || node.NodeType == NodeType.Directory); + return container is { IsReadOnly: false } && ((node.NodeType == NodeType.File && packFile != null) || node.NodeType == NodeType.Directory); } public bool IsEnabled(TreeNode node) => true; @@ -39,10 +38,10 @@ public void Execute() return; } - if (container.IsCaPackFile) + if (container.IsReadOnly) { - _logger.Here().Warning($"Rename blocked for CA pack node '{CommandLoggingHelper.DescribeNode(_node)}'"); - standardDialogs.ShowDialogBox("Unable to edit CA packfile", "Error"); + _logger.Here().Warning($"Rename blocked for readonly pack node '{CommandLoggingHelper.DescribeNode(_node)}'"); + standardDialogs.ShowDialogBox("Unable to edit readonly packfile", "Error"); return; } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SaveAsPackFileContainerCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SaveAsPackFileContainerCommand.cs index 9ad07c898..5c9f53801 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SaveAsPackFileContainerCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SaveAsPackFileContainerCommand.cs @@ -1,8 +1,5 @@ -using System; -using Serilog; -using Shared.Core.ErrorHandling; +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; using Shared.Core.Services; using Shared.Core.Settings; using Shared.Ui.BaseDialogs.PackFileTree.Utility; @@ -16,7 +13,7 @@ public class SaveAsPackFileContainerCommand(IPackFileService packFileService, Ap public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType == NodeType.Root && container is { IsCaPackFile: false }; + return node.NodeType == NodeType.Root && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SavePackFileContainerCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SavePackFileContainerCommand.cs index 9bd52d8e7..2d1d94415 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SavePackFileContainerCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SavePackFileContainerCommand.cs @@ -1,7 +1,8 @@ -using System; +using System.IO; using Serilog; using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Models.Containers; using Shared.Core.Services; using Shared.Core.Settings; using Shared.Ui.BaseDialogs.PackFileTree.Utility; @@ -18,7 +19,7 @@ public class SavePackFileContainerCommand( public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType == NodeType.Root && container is { IsCaPackFile: false }; + return node.NodeType == NodeType.Root && container is { IsReadOnly: false }; } public bool IsEnabled(TreeNode node) => true; @@ -59,6 +60,8 @@ public void Execute() { var gameInformation = GameInformationDatabase.GetGameById(applicationSettingsService.CurrentSettings.CurrentGame); _logger.Here().Information($"Saving pack file container '{packDescription}' to '{systemPath}'"); + if (container is SystemFolderContainer) + systemPath = Path.ChangeExtension(systemPath, ".pack"); packFileService.SavePackContainer(container, systemPath, false, gameInformation); _logger.Here().Information($"Saved pack file container '{packDescription}' to '{systemPath}'"); } diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SetAsEditablePackCommand.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SetAsEditablePackCommand.cs index 058b36c7f..76eac1590 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SetAsEditablePackCommand.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ContextMenu/Commands/SetAsEditablePackCommand.cs @@ -1,7 +1,5 @@ +using Shared.Core.ErrorHandling; using Shared.Core.PackFiles; -using Shared.Core.PackFiles.Models; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Ui.BaseDialogs.PackFileTree.Utility; namespace Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands @@ -14,7 +12,7 @@ public class SetAsEditablePackCommand(IPackFileService packFileService, IScopedL public bool ShouldAdd(TreeNode node) { var container = TreeNodeHelper.GetPackFileContainer(node); - return node.NodeType == NodeType.Root && container is { IsCaPackFile: false } && packFileService.GetEditablePack() != container; + return node.NodeType == NodeType.Root && container is { IsReadOnly: false } && packFileService.GetEditablePack() != container; } public bool IsEnabled(TreeNode node) => true; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserView.xaml b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserView.xaml index 6aeb2efdf..b1ca5fdfa 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserView.xaml +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserView.xaml @@ -137,6 +137,8 @@ + + @@ -166,6 +168,31 @@ + + + + + + + + + + + + + diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserViewModel.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserViewModel.cs index dc44b79c8..86c55ade3 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserViewModel.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/PackFileBrowserViewModel.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.IO; -using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Shared.Core.Events; -using Shared.Core.Events.Global; using Shared.Core.PackFiles; +using Shared.Core.PackFiles.Events; using Shared.Core.PackFiles.Models; using Shared.Core.Services; using Shared.Core.Settings; @@ -26,7 +23,6 @@ public partial class PackFileBrowserViewModel : ObservableObject, IDisposable, I { protected IPackFileService _packFileService; private readonly IEventHub? _eventHub; - private readonly IWindowsKeyboard _windowKeyboard; private readonly ApplicationSettingsService _applicationSettingsService; private readonly PackFileContextMenuComposer _contextMenuComposer; private readonly ContextMenuType _contextMenuType; @@ -55,7 +51,6 @@ public PackFileBrowserViewModel( { _packFileService = packFileService; _eventHub = eventHub; - _windowKeyboard = windowKeyboard; _applicationSettingsService = applicationSettingsService; _contextMenuComposer = contextMenuComposer; _contextMenuType = contextMenuType; diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/TreeNode.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/TreeNode.cs index 7e3f42dbb..b15b2bf77 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/TreeNode.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/TreeNode.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; +using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; -using Serilog; -using Shared.Core.ErrorHandling; using Shared.Core.PackFiles.Models; namespace Shared.Ui.BaseDialogs.PackFileTree diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/Utility/DropHandler.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/Utility/DropHandler.cs index e1a710f65..14db7a7c3 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/Utility/DropHandler.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/Utility/DropHandler.cs @@ -18,7 +18,7 @@ public static bool AllowDrop(TreeNode node, TreeNode? targetNode, IPackFileServi if (sourceContainer == null || sourceContainer != targetContainer) return false; - if (sourceContainer.IsCaPackFile) + if (sourceContainer.IsReadOnly) return false; if (targetNode.NodeType == NodeType.File) diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ValueConverters/PackFileToImageValueConverter.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ValueConverters/PackFileToImageValueConverter.cs index c15db2e78..6169c381b 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ValueConverters/PackFileToImageValueConverter.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/PackFileTree/ValueConverters/PackFileToImageValueConverter.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Data; using System.Windows.Media.Imaging; +using Shared.Core.PackFiles.Models; using Shared.EmbeddedResources; namespace Shared.Ui.BaseDialogs.PackFileTree.ValueConverters @@ -13,7 +14,7 @@ public object Convert(object value, Type targetType, object parameter, System.Gl if (value is TreeNode node) { if (node.NodeType == NodeType.Root) - return IconLibrary.CollectionIcon; + return GetRootIcon(node as RootTreeNode); else if (node.NodeType == NodeType.Directory) return IconLibrary.FolderIcon; if (node.NodeType == NodeType.File) @@ -23,6 +24,23 @@ public object Convert(object value, Type targetType, object parameter, System.Gl throw new Exception("Unknown type " + value.GetType().FullName); } + private static BitmapImage GetRootIcon(RootTreeNode? root) + { + var container = root?.Owner; + + switch(container!.ContainerType) + { + case PackFileContainerType.Normal: + return IconLibrary.NormalModPackIcon; + case PackFileContainerType.Database: + return IconLibrary.DatabaseModPackIcon; + case PackFileContainerType.SystemFolder: + return IconLibrary.SystemFolderModPackIcon; + default: + return IconLibrary.MissingIcon; + } + } + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/ExceptionHandling/CustomExceptionWindow.xaml.cs b/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/ExceptionHandling/CustomExceptionWindow.xaml.cs index 544601984..e3687ea55 100644 --- a/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/ExceptionHandling/CustomExceptionWindow.xaml.cs +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/ExceptionHandling/CustomExceptionWindow.xaml.cs @@ -1,13 +1,11 @@ -using System; -using System.Linq; -using System.Text; +using System.Text; using System.Text.Json; using System.Windows; -using Shared.Core.DependencyInjection; using Shared.Core.ErrorHandling.Exceptions; using Shared.Core.Events; using Shared.Core.Events.Global; using Shared.Core.Services; +using Shared.Core.ToolCreation; namespace Shared.Ui.Common.Exceptions { diff --git a/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/NewPackFile/NewPackFileWindow.xaml b/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/NewPackFile/NewPackFileWindow.xaml new file mode 100644 index 000000000..519e13338 --- /dev/null +++ b/Shared/SharedUI/Shared.Ui/BaseDialogs/StandardDialog/NewPackFile/NewPackFileWindow.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + +