From a8000fbf14491a50d0e52c164ece1213cffe559a Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 28 Mar 2023 16:58:20 +0200 Subject: [PATCH] Add ExportManager. --- Penumbra/Interop/PathResolving/MetaState.cs | 2 +- Penumbra/Mods/Editor/ModBackup.cs | 22 +++-- Penumbra/Mods/Manager/ExportManager.cs | 89 +++++++++++++++++++ Penumbra/Mods/Manager/Mod.Manager.BasePath.cs | 1 - Penumbra/Mods/Manager/Mod.Manager.Root.cs | 46 ---------- Penumbra/Mods/Manager/Mod.Manager.cs | 1 - Penumbra/Mods/Subclasses/IModGroup.cs | 4 +- Penumbra/PenumbraNew.cs | 1 + Penumbra/UI/FileDialogService.cs | 20 +++-- Penumbra/UI/ModsTab/ModPanelEditTab.cs | 29 +++--- Penumbra/UI/Tabs/SettingsTab.cs | 15 ++-- 11 files changed, 141 insertions(+), 89 deletions(-) create mode 100644 Penumbra/Mods/Manager/ExportManager.cs diff --git a/Penumbra/Interop/PathResolving/MetaState.cs b/Penumbra/Interop/PathResolving/MetaState.cs index febd7414..82dc3914 100644 --- a/Penumbra/Interop/PathResolving/MetaState.cs +++ b/Penumbra/Interop/PathResolving/MetaState.cs @@ -256,7 +256,7 @@ public unsafe class MetaState : IDisposable using var decals = new DecalReverter(_characterUtility, _resources, resolveData.ModCollection, UsesDecal(0, data)); var ret = _changeCustomize.Original(human, data, skipEquipment); - _inChangeCustomize = false; + _inChangeCustomize = false; return ret; } diff --git a/Penumbra/Mods/Editor/ModBackup.cs b/Penumbra/Mods/Editor/ModBackup.cs index e2ab4994..083fb803 100644 --- a/Penumbra/Mods/Editor/ModBackup.cs +++ b/Penumbra/Mods/Editor/ModBackup.cs @@ -10,17 +10,15 @@ public class ModBackup { public static bool CreatingBackup { get; private set; } - private readonly ModManager _modManager; - private readonly Mod _mod; - public readonly string Name; - public readonly bool Exists; + private readonly Mod _mod; + public readonly string Name; + public readonly bool Exists; - public ModBackup(ModManager modManager, Mod mod) - { - _modManager = modManager; - _mod = mod; - Name = Path.Combine(_modManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp"; - Exists = File.Exists(Name); + public ModBackup(ExportManager exportManager, Mod mod) + { + _mod = mod; + Name = Path.Combine(exportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp"; + Exists = File.Exists(Name); } /// Migrate file extensions. @@ -118,7 +116,7 @@ public class ModBackup /// Restore a mod from a pre-existing backup. Does not check if the mod contained in the backup is even similar. /// Does an automatic reload after extraction. /// - public void Restore() + public void Restore(ModManager modManager) { try { @@ -130,7 +128,7 @@ public class ModBackup ZipFile.ExtractToDirectory(Name, _mod.ModPath.FullName); Penumbra.Log.Debug($"Extracted exported file {Name} to {_mod.ModPath.FullName}."); - _modManager.ReloadMod(_mod.Index); + modManager.ReloadMod(_mod.Index); } catch (Exception e) { diff --git a/Penumbra/Mods/Manager/ExportManager.cs b/Penumbra/Mods/Manager/ExportManager.cs new file mode 100644 index 00000000..d0ecb6a3 --- /dev/null +++ b/Penumbra/Mods/Manager/ExportManager.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; + +namespace Penumbra.Mods; + +public class ExportManager : IDisposable +{ + private readonly Configuration _config; + private readonly ModManager _modManager; + + private DirectoryInfo? _exportDirectory; + + public DirectoryInfo ExportDirectory + => _exportDirectory ?? _modManager.BasePath; + + public ExportManager(Configuration config, ModManager modManager) + { + _config = config; + _modManager = modManager; + UpdateExportDirectory(_config.ExportDirectory, false); + _modManager.ModPathChanged += OnModPathChange; + } + + /// + public void UpdateExportDirectory(string newDirectory) + => UpdateExportDirectory(newDirectory, true); + + /// + /// Update the export directory to a new directory. Can also reset it to null with empty input. + /// If the directory is changed, all existing backups will be moved to the new one. + /// + /// The new directory name. + /// Can be used to stop saving for the initial setting + private void UpdateExportDirectory(string newDirectory, bool change) + { + if (newDirectory.Length == 0) + { + if (_exportDirectory == null) + return; + + _exportDirectory = null; + _config.ExportDirectory = string.Empty; + _config.Save(); + return; + } + + var dir = new DirectoryInfo(newDirectory); + if (dir.FullName.Equals(_exportDirectory?.FullName, StringComparison.OrdinalIgnoreCase)) + return; + + if (!dir.Exists) + try + { + Directory.CreateDirectory(dir.FullName); + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not create Export Directory:\n{e}"); + return; + } + + if (change) + foreach (var mod in _modManager) + new ModBackup(this, mod).Move(dir.FullName); + + _exportDirectory = dir; + + if (!change) + return; + + _config.ExportDirectory = dir.FullName; + _config.Save(); + } + + public void Dispose() + => _modManager.ModPathChanged -= OnModPathChange; + + /// Automatically migrate the backup file to the new name if any exists. + private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, + DirectoryInfo? newDirectory) + { + if (type is not ModPathChangeType.Moved || oldDirectory == null || newDirectory == null) + return; + + mod.ModPath = oldDirectory; + new ModBackup(this, mod).Move(null, newDirectory.Name); + mod.ModPath = newDirectory; + } +} diff --git a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs index 96e206fa..25340e84 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs @@ -57,7 +57,6 @@ public partial class ModManager } DataEditor.MoveDataFile(oldDirectory, dir); - new ModBackup(this, mod).Move(null, dir.Name); dir.Refresh(); mod.ModPath = dir; diff --git a/Penumbra/Mods/Manager/Mod.Manager.Root.cs b/Penumbra/Mods/Manager/Mod.Manager.Root.cs index 0377e09b..35f8d900 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Root.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Root.cs @@ -9,11 +9,6 @@ namespace Penumbra.Mods; public sealed partial class ModManager { public DirectoryInfo BasePath { get; private set; } = null!; - private DirectoryInfo? _exportDirectory; - - public DirectoryInfo ExportDirectory - => _exportDirectory ?? BasePath; - public bool Valid { get; private set; } public event Action? ModDiscoveryStarted; @@ -105,45 +100,4 @@ public sealed partial class ModManager if (MigrateModBackups) ModBackup.MigrateZipToPmp(this); } - - public void UpdateExportDirectory(string newDirectory, bool change) - { - if (newDirectory.Length == 0) - { - if (_exportDirectory == null) - return; - - _exportDirectory = null; - _config.ExportDirectory = string.Empty; - _config.Save(); - return; - } - - var dir = new DirectoryInfo(newDirectory); - if (dir.FullName.Equals(_exportDirectory?.FullName, StringComparison.OrdinalIgnoreCase)) - return; - - if (!dir.Exists) - try - { - Directory.CreateDirectory(dir.FullName); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not create Export Directory:\n{e}"); - return; - } - - if (change) - foreach (var mod in _mods) - new ModBackup(this, mod).Move(dir.FullName); - - _exportDirectory = dir; - - if (change) - { - _config.ExportDirectory = dir.FullName; - _config.Save(); - } - } } diff --git a/Penumbra/Mods/Manager/Mod.Manager.cs b/Penumbra/Mods/Manager/Mod.Manager.cs index 7e3310e4..95fbe2ac 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.cs @@ -114,7 +114,6 @@ public sealed partial class ModManager : IReadOnlyList, IDisposable OptionEditor = optionEditor; ModDirectoryChanged += OnModDirectoryChange; SetBaseDirectory(config.ModDirectory, true); - UpdateExportDirectory(_config.ExportDirectory, false); _communicator.ModOptionChanged.Event += OnModOptionChange; ModPathChanged += OnModPathChange; DiscoverMods(); diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 45db2b59..c5087711 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -47,11 +47,11 @@ public readonly struct ModSaveGroup : ISavable public ModSaveGroup(Mod mod, int groupIdx) { _basePath = mod.ModPath; + _groupIdx = groupIdx; if (_groupIdx < 0) _defaultMod = mod.Default; else - _group = mod.Groups[groupIdx]; - _groupIdx = groupIdx; + _group = mod.Groups[_groupIdx]; } public ModSaveGroup(DirectoryInfo basePath, IModGroup group, int groupIdx) diff --git a/Penumbra/PenumbraNew.cs b/Penumbra/PenumbraNew.cs index 6766d3e8..afba2adf 100644 --- a/Penumbra/PenumbraNew.cs +++ b/Penumbra/PenumbraNew.cs @@ -93,6 +93,7 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(); // Add Resource services diff --git a/Penumbra/UI/FileDialogService.cs b/Penumbra/UI/FileDialogService.cs index 650014c8..061f53ad 100644 --- a/Penumbra/UI/FileDialogService.cs +++ b/Penumbra/UI/FileDialogService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using Dalamud.Interface; using Dalamud.Interface.ImGuiFileDialog; @@ -87,8 +88,9 @@ public class FileDialogService : IDisposable return (valid, list) => { _isOpen = false; - _startPaths[title] = GetCurrentLocation(); - callback(valid, list); + var loc = HandleRoot(GetCurrentLocation()); + _startPaths[title] = loc; + callback(valid, list.Select(HandleRoot).ToList()); }; } @@ -97,14 +99,20 @@ public class FileDialogService : IDisposable return (valid, list) => { _isOpen = false; - var loc = GetCurrentLocation(); - if (loc.Length == 2) - loc += '\\'; + var loc = HandleRoot(GetCurrentLocation()); _startPaths[title] = loc; - callback(valid, list); + callback(valid, HandleRoot(list)); }; } + private static string HandleRoot(string path) + { + if (path.Length == 2 && path[1] == ':') + return path + '\\'; + + return path; + } + // TODO: maybe change this from reflection when its public. private string GetCurrentLocation() => (_manager.GetType().GetField("dialog", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_manager) as FileDialog) diff --git a/Penumbra/UI/ModsTab/ModPanelEditTab.cs b/Penumbra/UI/ModsTab/ModPanelEditTab.cs index bb1b6d75..83d09f9d 100644 --- a/Penumbra/UI/ModsTab/ModPanelEditTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelEditTab.cs @@ -23,6 +23,7 @@ public class ModPanelEditTab : ITab private readonly ChatService _chat; private readonly FilenameService _filenames; private readonly ModManager _modManager; + private readonly ExportManager _exportManager; private readonly ModFileSystem _fileSystem; private readonly ModFileSystemSelector _selector; private readonly ModEditWindow _editWindow; @@ -36,15 +37,16 @@ public class ModPanelEditTab : ITab private Mod _mod = null!; public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, ChatService chat, - ModEditWindow editWindow, ModEditor editor, FilenameService filenames) + ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ExportManager exportManager) { - _modManager = modManager; - _selector = selector; - _fileSystem = fileSystem; - _chat = chat; - _editWindow = editWindow; - _editor = editor; - _filenames = filenames; + _modManager = modManager; + _selector = selector; + _fileSystem = fileSystem; + _chat = chat; + _editWindow = editWindow; + _editor = editor; + _filenames = filenames; + _exportManager = exportManager; } public ReadOnlySpan Label @@ -147,7 +149,7 @@ public class ModPanelEditTab : ITab private void BackupButtons(Vector2 buttonSize) { - var backup = new ModBackup(_modManager, _mod); + var backup = new ModBackup(_exportManager, _mod); var tt = ModBackup.CreatingBackup ? "Already exporting a mod." : backup.Exists @@ -168,7 +170,7 @@ public class ModPanelEditTab : ITab : $"Exported mod \"{backup.Name}\" does not exist."; ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton("Restore From Export", buttonSize, tt, !backup.Exists)) - backup.Restore(); + backup.Restore(_modManager); } /// Anything about editing the regular meta information about the mod. @@ -306,11 +308,11 @@ public class ModPanelEditTab : ITab private static int _newDescriptionIdx = -1; private static int _newDescriptionOptionIdx = -1; private static Mod? _mod; - private static FilenameService? _fileNames; + private static FilenameService? _fileNames; public static void OpenPopup(FilenameService filenames, Mod mod, int groupIdx, int optionIdx = -1) { - _fileNames = filenames; + _fileNames = filenames; _newDescriptionIdx = groupIdx; _newDescriptionOptionIdx = optionIdx; _newDescription = groupIdx < 0 @@ -598,7 +600,8 @@ public class ModPanelEditTab : ITab if (_dragDropGroupIdx == groupIdx) { var sourceOption = _dragDropOptionIdx; - panel._delayedActions.Enqueue(() => panel._modManager.OptionEditor.MoveOption(panel._mod, groupIdx, sourceOption, optionIdx)); + panel._delayedActions.Enqueue( + () => panel._modManager.OptionEditor.MoveOption(panel._mod, groupIdx, sourceOption, optionIdx)); } else { diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index 039a648f..a920e723 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -9,7 +9,6 @@ using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Widgets; -using Penumbra.Interop; using Penumbra.Interop.Services; using Penumbra.Mods; using Penumbra.Services; @@ -31,7 +30,8 @@ public class SettingsTab : ITab private readonly TutorialService _tutorial; private readonly Penumbra _penumbra; private readonly FileDialogService _fileDialog; - private readonly ModManager _modManager; + private readonly ModManager _modManager; + private readonly ExportManager _exportManager; private readonly ModFileSystemSelector _selector; private readonly CharacterUtility _characterUtility; private readonly ResidentResourceManager _residentResources; @@ -39,7 +39,7 @@ public class SettingsTab : ITab public SettingsTab(Configuration config, FontReloader fontReloader, TutorialService tutorial, Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, CharacterUtility characterUtility, - ResidentResourceManager residentResources, DalamudServices dalamud) + ResidentResourceManager residentResources, DalamudServices dalamud, ExportManager exportManager) { _config = config; _fontReloader = fontReloader; @@ -51,6 +51,7 @@ public class SettingsTab : ITab _characterUtility = characterUtility; _residentResources = residentResources; _dalamud = dalamud; + _exportManager = exportManager; } public void DrawHeader() @@ -114,7 +115,7 @@ public class SettingsTab : ITab /// Check a potential new root directory for validity and return the button text and whether it is valid. private static (string Text, bool Valid) CheckRootDirectoryPath(string newName, string old, bool selected) - { + { static bool IsSubPathOf(string basePath, string subPath) { if (basePath.Length == 0) @@ -127,7 +128,7 @@ public class SettingsTab : ITab if (newName.Length > RootDirectoryMaxLength) return ($"Path is too long. The maximum length is {RootDirectoryMaxLength}.", false); - if (Path.GetDirectoryName(newName) == null) + if (Path.GetDirectoryName(newName).IsNullOrEmpty()) return ("Path is not allowed to be a drive root. Please add a directory.", false); var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); @@ -553,7 +554,7 @@ public class SettingsTab : ITab _tempExportDirectory = tmp; if (ImGui.IsItemDeactivatedAfterEdit()) - _modManager.UpdateExportDirectory(_tempExportDirectory, true); + _exportManager.UpdateExportDirectory(_tempExportDirectory); ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Folder.ToIconString()}##export", UiHelpers.IconButtonSize, @@ -567,7 +568,7 @@ public class SettingsTab : ITab _fileDialog.OpenFolderPicker("Choose Default Export Directory", (b, s) => { if (b) - Penumbra.ModManager.UpdateExportDirectory(s, true); + _exportManager.UpdateExportDirectory(s); }, startDir, false); }