Fix issue with merging and deduplicating.

This commit is contained in:
Ottermandias 2024-04-01 13:59:09 +02:00
parent 5cebddb0ab
commit a65009dfb0
5 changed files with 59 additions and 50 deletions

@ -1 +1 @@
Subproject commit f641a34ffa80e89bd61701f60f15d15c4c5b361e
Subproject commit f48c6886cbc163c5a292fa8b9fd919cb01c11d7b

View file

@ -1,3 +1,4 @@
using OtterGui.Classes;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
using Penumbra.Services;
@ -81,7 +82,7 @@ public class DuplicateManager(ModManager modManager, SaveService saveService, Co
if (useModManager)
{
modManager.OptionEditor.OptionSetFiles(mod, groupIdx, optionIdx, dict);
modManager.OptionEditor.OptionSetFiles(mod, groupIdx, optionIdx, dict, SaveType.ImmediateSync);
}
else
{
@ -216,18 +217,21 @@ public class DuplicateManager(ModManager modManager, SaveService saveService, Co
}
/// <summary> Deduplicate a mod simply by its directory without any confirmation or waiting time. </summary>
internal void DeduplicateMod(DirectoryInfo modDirectory)
internal void DeduplicateMod(DirectoryInfo modDirectory, bool useModManager = false)
{
try
{
var mod = new Mod(modDirectory);
modManager.Creator.ReloadMod(mod, true, out _);
if (!useModManager || !modManager.TryGetMod(modDirectory.Name, string.Empty, out var mod))
{
mod = new Mod(modDirectory);
modManager.Creator.ReloadMod(mod, true, out _);
}
Clear();
var files = new ModFileCollection();
files.UpdateAll(mod, mod.Default);
CheckDuplicates(files.Available.OrderByDescending(f => f.FileSize).ToArray(), CancellationToken.None);
DeleteDuplicates(files, mod, mod.Default, false);
CheckDuplicates([.. files.Available.OrderByDescending(f => f.FileSize)], CancellationToken.None);
DeleteDuplicates(files, mod, mod.Default, useModManager);
}
catch (Exception e)
{

View file

@ -36,7 +36,7 @@ public class ModMerger : IDisposable
public readonly HashSet<SubMod> SelectedOptions = [];
public readonly IReadOnlyList<string> Warnings = new List<string>();
public readonly IReadOnlyList<string> Warnings = [];
public Exception? Error { get; private set; }
public ModMerger(ModManager mods, ModOptionEditor editor, ModFileSystemSelector selector, DuplicateManager duplicates,
@ -78,7 +78,8 @@ public class ModMerger : IDisposable
MergeWithOptions();
else
MergeIntoOption(OptionGroupName, OptionName);
_duplicates.DeduplicateMod(MergeToMod.ModPath);
_duplicates.DeduplicateMod(MergeToMod.ModPath, true);
}
catch (Exception ex)
{
@ -134,10 +135,10 @@ public class ModMerger : IDisposable
return;
}
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, GroupType.Multi, groupName);
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, GroupType.Multi, groupName, SaveType.None);
if (groupCreated)
_createdGroups.Add(groupIdx);
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, optionName);
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, optionName, SaveType.None);
if (optionCreated)
_createdOptions.Add(option);
var dir = ModCreator.NewOptionDirectory(MergeToMod!.ModPath, groupName, _config.ReplaceNonAsciiOnImport);
@ -156,27 +157,6 @@ public class ModMerger : IDisposable
var swaps = option.FileSwapData.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
var manips = option.ManipulationData.ToHashSet();
bool GetFullPath(FullPath input, out FullPath ret)
{
if (fromFileToFile)
{
if (!_fileToFile.TryGetValue(input.FullName, out var s))
{
ret = input;
return false;
}
ret = new FullPath(s);
return true;
}
if (!Utf8RelPath.FromFile(input, MergeFromMod!.ModPath, out var relPath))
throw new Exception($"Could not create relative path from {input} and {MergeFromMod!.ModPath}.");
ret = new FullPath(MergeToMod!.ModPath, relPath);
return true;
}
foreach (var originalOption in mergeOptions)
{
foreach (var manip in originalOption.Manipulations)
@ -204,9 +184,31 @@ public class ModMerger : IDisposable
}
}
_editor.OptionSetFiles(MergeToMod!, option.GroupIdx, option.OptionIdx, redirections);
_editor.OptionSetFileSwaps(MergeToMod!, option.GroupIdx, option.OptionIdx, swaps);
_editor.OptionSetManipulations(MergeToMod!, option.GroupIdx, option.OptionIdx, manips);
_editor.OptionSetFiles(MergeToMod!, option.GroupIdx, option.OptionIdx, redirections, SaveType.None);
_editor.OptionSetFileSwaps(MergeToMod!, option.GroupIdx, option.OptionIdx, swaps, SaveType.None);
_editor.OptionSetManipulations(MergeToMod!, option.GroupIdx, option.OptionIdx, manips, SaveType.ImmediateSync);
return;
bool GetFullPath(FullPath input, out FullPath ret)
{
if (fromFileToFile)
{
if (!_fileToFile.TryGetValue(input.FullName, out var s))
{
ret = input;
return false;
}
ret = new FullPath(s);
return true;
}
if (!Utf8RelPath.FromFile(input, MergeFromMod!.ModPath, out var relPath))
throw new Exception($"Could not create relative path from {input} and {MergeFromMod!.ModPath}.");
ret = new FullPath(MergeToMod!.ModPath, relPath);
return true;
}
}
private void CopyFiles(DirectoryInfo directory)

View file

@ -78,7 +78,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
}
/// <summary> Add a new, empty option group of the given type and name. </summary>
public void AddModGroup(Mod mod, GroupType type, string newName)
public void AddModGroup(Mod mod, GroupType type, string newName, SaveType saveType = SaveType.ImmediateSync)
{
if (!VerifyFileName(mod, null, newName, true))
return;
@ -96,18 +96,18 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
Name = newName,
Priority = maxPriority,
});
saveService.ImmediateSave(new ModSaveGroup(mod, mod.Groups.Count - 1, config.ReplaceNonAsciiOnImport));
saveService.Save(saveType, new ModSaveGroup(mod, mod.Groups.Count - 1, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, mod.Groups.Count - 1, -1, -1);
}
/// <summary> Add a new mod, empty option group of the given type and name if it does not exist already. </summary>
public (IModGroup, int, bool) FindOrAddModGroup(Mod mod, GroupType type, string newName)
public (IModGroup, int, bool) FindOrAddModGroup(Mod mod, GroupType type, string newName, SaveType saveType = SaveType.ImmediateSync)
{
var idx = mod.Groups.IndexOf(g => g.Name == newName);
if (idx >= 0)
return (mod.Groups[idx], idx, false);
AddModGroup(mod, type, newName);
AddModGroup(mod, type, newName, saveType);
if (mod.Groups[^1].Name != newName)
throw new Exception($"Could not create new mod group with name {newName}.");
@ -226,7 +226,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
}
/// <summary> Add a new empty option of the given name for the given group. </summary>
public void AddOption(Mod mod, int groupIdx, string newName)
public void AddOption(Mod mod, int groupIdx, string newName, SaveType saveType = SaveType.Queue)
{
var group = mod.Groups[groupIdx];
var subMod = new SubMod(mod) { Name = newName };
@ -241,19 +241,19 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
break;
}
saveService.QueueSave(new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
saveService.Save(saveType, new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1);
}
/// <summary> Add a new empty option of the given name for the given group if it does not exist already. </summary>
public (SubMod, bool) FindOrAddOption(Mod mod, int groupIdx, string newName)
public (SubMod, bool) FindOrAddOption(Mod mod, int groupIdx, string newName, SaveType saveType = SaveType.Queue)
{
var group = mod.Groups[groupIdx];
var idx = group.IndexOf(o => o.Name == newName);
if (idx >= 0)
return ((SubMod)group[idx], false);
AddOption(mod, groupIdx, newName);
AddOption(mod, groupIdx, newName, saveType);
if (group[^1].Name != newName)
throw new Exception($"Could not create new option with name {newName} in {group.Name}.");
@ -324,7 +324,8 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
}
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
public void OptionSetManipulations(Mod mod, int groupIdx, int optionIdx, HashSet<MetaManipulation> manipulations)
public void OptionSetManipulations(Mod mod, int groupIdx, int optionIdx, HashSet<MetaManipulation> manipulations,
SaveType saveType = SaveType.Queue)
{
var subMod = GetSubMod(mod, groupIdx, optionIdx);
if (subMod.Manipulations.Count == manipulations.Count
@ -333,12 +334,13 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
subMod.ManipulationData.SetTo(manipulations);
saveService.QueueSave(new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
saveService.Save(saveType, new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, mod, groupIdx, optionIdx, -1);
}
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
public void OptionSetFiles(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> replacements)
public void OptionSetFiles(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> replacements,
SaveType saveType = SaveType.Queue)
{
var subMod = GetSubMod(mod, groupIdx, optionIdx);
if (subMod.FileData.SetEquals(replacements))
@ -346,7 +348,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
subMod.FileData.SetTo(replacements);
saveService.QueueSave(new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
saveService.Save(saveType, new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, mod, groupIdx, optionIdx, -1);
}
@ -364,7 +366,8 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
}
/// <summary> Set the file swaps for a given option. Replaces existing swaps. </summary>
public void OptionSetFileSwaps(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> swaps)
public void OptionSetFileSwaps(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> swaps,
SaveType saveType = SaveType.Queue)
{
var subMod = GetSubMod(mod, groupIdx, optionIdx);
if (subMod.FileSwapData.SetEquals(swaps))
@ -372,7 +375,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
subMod.FileSwapData.SetTo(swaps);
saveService.QueueSave(new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
saveService.Save(saveType, new ModSaveGroup(mod, groupIdx, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, mod, groupIdx, optionIdx, -1);
}

View file

@ -213,7 +213,7 @@ public sealed class CrashHandlerService : IDisposable, IService
}
catch (Exception ex)
{
Penumbra.Log.Debug($"Could not delete {dir}:\n{ex}");
Penumbra.Log.Verbose($"Could not delete {dir}. This is generally not an error:\n{ex}");
}
}
}