Add ExportManager.

This commit is contained in:
Ottermandias 2023-03-28 16:58:20 +02:00
parent 2b7292adb8
commit a8000fbf14
11 changed files with 141 additions and 89 deletions

View file

@ -256,7 +256,7 @@ public unsafe class MetaState : IDisposable
using var decals = using var decals =
new DecalReverter(_characterUtility, _resources, resolveData.ModCollection, UsesDecal(0, data)); new DecalReverter(_characterUtility, _resources, resolveData.ModCollection, UsesDecal(0, data));
var ret = _changeCustomize.Original(human, data, skipEquipment); var ret = _changeCustomize.Original(human, data, skipEquipment);
_inChangeCustomize = false; _inChangeCustomize = false;
return ret; return ret;
} }

View file

@ -10,17 +10,15 @@ public class ModBackup
{ {
public static bool CreatingBackup { get; private set; } public static bool CreatingBackup { get; private set; }
private readonly ModManager _modManager; private readonly Mod _mod;
private readonly Mod _mod; public readonly string Name;
public readonly string Name; public readonly bool Exists;
public readonly bool Exists;
public ModBackup(ModManager modManager, Mod mod) public ModBackup(ExportManager exportManager, Mod mod)
{ {
_modManager = modManager; _mod = mod;
_mod = mod; Name = Path.Combine(exportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp";
Name = Path.Combine(_modManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp"; Exists = File.Exists(Name);
Exists = File.Exists(Name);
} }
/// <summary> Migrate file extensions. </summary> /// <summary> Migrate file extensions. </summary>
@ -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. /// 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. /// Does an automatic reload after extraction.
/// </summary> /// </summary>
public void Restore() public void Restore(ModManager modManager)
{ {
try try
{ {
@ -130,7 +128,7 @@ public class ModBackup
ZipFile.ExtractToDirectory(Name, _mod.ModPath.FullName); ZipFile.ExtractToDirectory(Name, _mod.ModPath.FullName);
Penumbra.Log.Debug($"Extracted exported file {Name} to {_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) catch (Exception e)
{ {

View file

@ -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;
}
/// <inheritdoc cref="UpdateExportDirectory(string, bool)"/>
public void UpdateExportDirectory(string newDirectory)
=> UpdateExportDirectory(newDirectory, true);
/// <summary>
/// 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.
/// </summary>
/// <param name="newDirectory">The new directory name.</param>
/// <param name="change">Can be used to stop saving for the initial setting</param>
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;
/// <summary> Automatically migrate the backup file to the new name if any exists. </summary>
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;
}
}

View file

@ -57,7 +57,6 @@ public partial class ModManager
} }
DataEditor.MoveDataFile(oldDirectory, dir); DataEditor.MoveDataFile(oldDirectory, dir);
new ModBackup(this, mod).Move(null, dir.Name);
dir.Refresh(); dir.Refresh();
mod.ModPath = dir; mod.ModPath = dir;

View file

@ -9,11 +9,6 @@ namespace Penumbra.Mods;
public sealed partial class ModManager public sealed partial class ModManager
{ {
public DirectoryInfo BasePath { get; private set; } = null!; public DirectoryInfo BasePath { get; private set; } = null!;
private DirectoryInfo? _exportDirectory;
public DirectoryInfo ExportDirectory
=> _exportDirectory ?? BasePath;
public bool Valid { get; private set; } public bool Valid { get; private set; }
public event Action? ModDiscoveryStarted; public event Action? ModDiscoveryStarted;
@ -105,45 +100,4 @@ public sealed partial class ModManager
if (MigrateModBackups) if (MigrateModBackups)
ModBackup.MigrateZipToPmp(this); 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();
}
}
} }

View file

@ -114,7 +114,6 @@ public sealed partial class ModManager : IReadOnlyList<Mod>, IDisposable
OptionEditor = optionEditor; OptionEditor = optionEditor;
ModDirectoryChanged += OnModDirectoryChange; ModDirectoryChanged += OnModDirectoryChange;
SetBaseDirectory(config.ModDirectory, true); SetBaseDirectory(config.ModDirectory, true);
UpdateExportDirectory(_config.ExportDirectory, false);
_communicator.ModOptionChanged.Event += OnModOptionChange; _communicator.ModOptionChanged.Event += OnModOptionChange;
ModPathChanged += OnModPathChange; ModPathChanged += OnModPathChange;
DiscoverMods(); DiscoverMods();

View file

@ -47,11 +47,11 @@ public readonly struct ModSaveGroup : ISavable
public ModSaveGroup(Mod mod, int groupIdx) public ModSaveGroup(Mod mod, int groupIdx)
{ {
_basePath = mod.ModPath; _basePath = mod.ModPath;
_groupIdx = groupIdx;
if (_groupIdx < 0) if (_groupIdx < 0)
_defaultMod = mod.Default; _defaultMod = mod.Default;
else else
_group = mod.Groups[groupIdx]; _group = mod.Groups[_groupIdx];
_groupIdx = groupIdx;
} }
public ModSaveGroup(DirectoryInfo basePath, IModGroup group, int groupIdx) public ModSaveGroup(DirectoryInfo basePath, IModGroup group, int groupIdx)

View file

@ -93,6 +93,7 @@ public class PenumbraNew
.AddSingleton<ModDataEditor>() .AddSingleton<ModDataEditor>()
.AddSingleton<ModOptionEditor>() .AddSingleton<ModOptionEditor>()
.AddSingleton<ModManager>() .AddSingleton<ModManager>()
.AddSingleton<ExportManager>()
.AddSingleton<ModFileSystem>(); .AddSingleton<ModFileSystem>();
// Add Resource services // Add Resource services

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.ImGuiFileDialog;
@ -87,8 +88,9 @@ public class FileDialogService : IDisposable
return (valid, list) => return (valid, list) =>
{ {
_isOpen = false; _isOpen = false;
_startPaths[title] = GetCurrentLocation(); var loc = HandleRoot(GetCurrentLocation());
callback(valid, list); _startPaths[title] = loc;
callback(valid, list.Select(HandleRoot).ToList());
}; };
} }
@ -97,14 +99,20 @@ public class FileDialogService : IDisposable
return (valid, list) => return (valid, list) =>
{ {
_isOpen = false; _isOpen = false;
var loc = GetCurrentLocation(); var loc = HandleRoot(GetCurrentLocation());
if (loc.Length == 2)
loc += '\\';
_startPaths[title] = loc; _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. // TODO: maybe change this from reflection when its public.
private string GetCurrentLocation() private string GetCurrentLocation()
=> (_manager.GetType().GetField("dialog", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_manager) as FileDialog) => (_manager.GetType().GetField("dialog", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_manager) as FileDialog)

View file

@ -23,6 +23,7 @@ public class ModPanelEditTab : ITab
private readonly ChatService _chat; private readonly ChatService _chat;
private readonly FilenameService _filenames; private readonly FilenameService _filenames;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly ExportManager _exportManager;
private readonly ModFileSystem _fileSystem; private readonly ModFileSystem _fileSystem;
private readonly ModFileSystemSelector _selector; private readonly ModFileSystemSelector _selector;
private readonly ModEditWindow _editWindow; private readonly ModEditWindow _editWindow;
@ -36,15 +37,16 @@ public class ModPanelEditTab : ITab
private Mod _mod = null!; private Mod _mod = null!;
public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, ChatService chat, 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; _modManager = modManager;
_selector = selector; _selector = selector;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_chat = chat; _chat = chat;
_editWindow = editWindow; _editWindow = editWindow;
_editor = editor; _editor = editor;
_filenames = filenames; _filenames = filenames;
_exportManager = exportManager;
} }
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
@ -147,7 +149,7 @@ public class ModPanelEditTab : ITab
private void BackupButtons(Vector2 buttonSize) private void BackupButtons(Vector2 buttonSize)
{ {
var backup = new ModBackup(_modManager, _mod); var backup = new ModBackup(_exportManager, _mod);
var tt = ModBackup.CreatingBackup var tt = ModBackup.CreatingBackup
? "Already exporting a mod." ? "Already exporting a mod."
: backup.Exists : backup.Exists
@ -168,7 +170,7 @@ public class ModPanelEditTab : ITab
: $"Exported mod \"{backup.Name}\" does not exist."; : $"Exported mod \"{backup.Name}\" does not exist.";
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Restore From Export", buttonSize, tt, !backup.Exists)) if (ImGuiUtil.DrawDisabledButton("Restore From Export", buttonSize, tt, !backup.Exists))
backup.Restore(); backup.Restore(_modManager);
} }
/// <summary> Anything about editing the regular meta information about the mod. </summary> /// <summary> Anything about editing the regular meta information about the mod. </summary>
@ -306,11 +308,11 @@ public class ModPanelEditTab : ITab
private static int _newDescriptionIdx = -1; private static int _newDescriptionIdx = -1;
private static int _newDescriptionOptionIdx = -1; private static int _newDescriptionOptionIdx = -1;
private static Mod? _mod; 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) public static void OpenPopup(FilenameService filenames, Mod mod, int groupIdx, int optionIdx = -1)
{ {
_fileNames = filenames; _fileNames = filenames;
_newDescriptionIdx = groupIdx; _newDescriptionIdx = groupIdx;
_newDescriptionOptionIdx = optionIdx; _newDescriptionOptionIdx = optionIdx;
_newDescription = groupIdx < 0 _newDescription = groupIdx < 0
@ -598,7 +600,8 @@ public class ModPanelEditTab : ITab
if (_dragDropGroupIdx == groupIdx) if (_dragDropGroupIdx == groupIdx)
{ {
var sourceOption = _dragDropOptionIdx; 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 else
{ {

View file

@ -9,7 +9,6 @@ using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Interop;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Services; using Penumbra.Services;
@ -31,7 +30,8 @@ public class SettingsTab : ITab
private readonly TutorialService _tutorial; private readonly TutorialService _tutorial;
private readonly Penumbra _penumbra; private readonly Penumbra _penumbra;
private readonly FileDialogService _fileDialog; private readonly FileDialogService _fileDialog;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly ExportManager _exportManager;
private readonly ModFileSystemSelector _selector; private readonly ModFileSystemSelector _selector;
private readonly CharacterUtility _characterUtility; private readonly CharacterUtility _characterUtility;
private readonly ResidentResourceManager _residentResources; private readonly ResidentResourceManager _residentResources;
@ -39,7 +39,7 @@ public class SettingsTab : ITab
public SettingsTab(Configuration config, FontReloader fontReloader, TutorialService tutorial, Penumbra penumbra, public SettingsTab(Configuration config, FontReloader fontReloader, TutorialService tutorial, Penumbra penumbra,
FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, CharacterUtility characterUtility, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, CharacterUtility characterUtility,
ResidentResourceManager residentResources, DalamudServices dalamud) ResidentResourceManager residentResources, DalamudServices dalamud, ExportManager exportManager)
{ {
_config = config; _config = config;
_fontReloader = fontReloader; _fontReloader = fontReloader;
@ -51,6 +51,7 @@ public class SettingsTab : ITab
_characterUtility = characterUtility; _characterUtility = characterUtility;
_residentResources = residentResources; _residentResources = residentResources;
_dalamud = dalamud; _dalamud = dalamud;
_exportManager = exportManager;
} }
public void DrawHeader() public void DrawHeader()
@ -114,7 +115,7 @@ public class SettingsTab : ITab
/// <summary> Check a potential new root directory for validity and return the button text and whether it is valid. </summary> /// <summary> Check a potential new root directory for validity and return the button text and whether it is valid. </summary>
private static (string Text, bool Valid) CheckRootDirectoryPath(string newName, string old, bool selected) private static (string Text, bool Valid) CheckRootDirectoryPath(string newName, string old, bool selected)
{ {
static bool IsSubPathOf(string basePath, string subPath) static bool IsSubPathOf(string basePath, string subPath)
{ {
if (basePath.Length == 0) if (basePath.Length == 0)
@ -127,7 +128,7 @@ public class SettingsTab : ITab
if (newName.Length > RootDirectoryMaxLength) if (newName.Length > RootDirectoryMaxLength)
return ($"Path is too long. The maximum length is {RootDirectoryMaxLength}.", false); 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); return ("Path is not allowed to be a drive root. Please add a directory.", false);
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
@ -553,7 +554,7 @@ public class SettingsTab : ITab
_tempExportDirectory = tmp; _tempExportDirectory = tmp;
if (ImGui.IsItemDeactivatedAfterEdit()) if (ImGui.IsItemDeactivatedAfterEdit())
_modManager.UpdateExportDirectory(_tempExportDirectory, true); _exportManager.UpdateExportDirectory(_tempExportDirectory);
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Folder.ToIconString()}##export", UiHelpers.IconButtonSize, 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) => _fileDialog.OpenFolderPicker("Choose Default Export Directory", (b, s) =>
{ {
if (b) if (b)
Penumbra.ModManager.UpdateExportDirectory(s, true); _exportManager.UpdateExportDirectory(s);
}, startDir, false); }, startDir, false);
} }