mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
304 lines
13 KiB
C#
304 lines
13 KiB
C#
using Dalamud.Interface.ImGuiNotification;
|
|
using OtterGui.Classes;
|
|
using OtterGui.Filesystem;
|
|
using OtterGui.Services;
|
|
using Penumbra.Api.Enums;
|
|
using Penumbra.Meta.Manipulations;
|
|
using Penumbra.Mods.Groups;
|
|
using Penumbra.Mods.Settings;
|
|
using Penumbra.Mods.SubMods;
|
|
using Penumbra.Services;
|
|
using Penumbra.String.Classes;
|
|
using Penumbra.Util;
|
|
|
|
namespace Penumbra.Mods.Manager.OptionEditor;
|
|
|
|
public enum ModOptionChangeType
|
|
{
|
|
GroupRenamed,
|
|
GroupAdded,
|
|
GroupDeleted,
|
|
GroupMoved,
|
|
GroupTypeChanged,
|
|
PriorityChanged,
|
|
OptionAdded,
|
|
OptionDeleted,
|
|
OptionMoved,
|
|
OptionFilesChanged,
|
|
OptionFilesAdded,
|
|
OptionSwapsChanged,
|
|
OptionMetaChanged,
|
|
DisplayChange,
|
|
PrepareChange,
|
|
DefaultOptionChanged,
|
|
}
|
|
|
|
public class ModGroupEditor(
|
|
SingleModGroupEditor singleEditor,
|
|
MultiModGroupEditor multiEditor,
|
|
ImcModGroupEditor imcEditor,
|
|
CombiningModGroupEditor combiningEditor,
|
|
CommunicatorService communicator,
|
|
SaveService saveService,
|
|
Configuration config) : IService
|
|
{
|
|
public SingleModGroupEditor SingleEditor
|
|
=> singleEditor;
|
|
|
|
public MultiModGroupEditor MultiEditor
|
|
=> multiEditor;
|
|
|
|
public ImcModGroupEditor ImcEditor
|
|
=> imcEditor;
|
|
|
|
public CombiningModGroupEditor CombiningEditor
|
|
=> combiningEditor;
|
|
|
|
/// <summary> Change the settings stored as default options in a mod.</summary>
|
|
public void ChangeModGroupDefaultOption(IModGroup group, Setting defaultOption)
|
|
{
|
|
if (group.DefaultSettings == defaultOption)
|
|
return;
|
|
|
|
group.DefaultSettings = defaultOption;
|
|
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, group.Mod, group, null, null, -1);
|
|
}
|
|
|
|
/// <summary> Rename an option group if possible. </summary>
|
|
public void RenameModGroup(IModGroup group, string newName)
|
|
{
|
|
var oldName = group.Name;
|
|
if (oldName == newName || !VerifyFileName(group.Mod, group, newName, true))
|
|
return;
|
|
|
|
saveService.ImmediateDelete(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
|
|
group.Name = newName;
|
|
saveService.ImmediateSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, group.Mod, group, null, null, -1);
|
|
}
|
|
|
|
/// <summary> Delete a given option group. Fires an event to prepare before actually deleting. </summary>
|
|
public void DeleteModGroup(IModGroup group)
|
|
{
|
|
var mod = group.Mod;
|
|
var idx = group.GetIndex();
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, null, null, -1);
|
|
mod.Groups.RemoveAt(idx);
|
|
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, null, null, null, idx);
|
|
}
|
|
|
|
/// <summary> Move the index of a given option group. </summary>
|
|
public void MoveModGroup(IModGroup group, int groupIdxTo)
|
|
{
|
|
var mod = group.Mod;
|
|
var idxFrom = group.GetIndex();
|
|
if (!mod.Groups.Move(ref idxFrom, ref groupIdxTo))
|
|
return;
|
|
|
|
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, group, null, null, idxFrom);
|
|
}
|
|
|
|
/// <summary> Change the internal priority of the given option group. </summary>
|
|
public void ChangeGroupPriority(IModGroup group, ModPriority newPriority)
|
|
{
|
|
if (group.Priority == newPriority)
|
|
return;
|
|
|
|
group.Priority = newPriority;
|
|
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, group.Mod, group, null, null, -1);
|
|
}
|
|
|
|
/// <summary> Change the description of the given option group. </summary>
|
|
public void ChangeGroupDescription(IModGroup group, string newDescription)
|
|
{
|
|
if (group.Description == newDescription)
|
|
return;
|
|
|
|
group.Description = newDescription;
|
|
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, group.Mod, group, null, null, -1);
|
|
}
|
|
|
|
/// <summary> Rename the given option. </summary>
|
|
public void RenameOption(IModOption option, string newName)
|
|
{
|
|
if (option.Name == newName)
|
|
return;
|
|
|
|
option.Name = newName;
|
|
saveService.QueueSave(new ModSaveGroup(option.Group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
|
}
|
|
|
|
/// <summary> Change the description of the given option. </summary>
|
|
public void ChangeOptionDescription(IModOption option, string newDescription)
|
|
{
|
|
if (option.Description == newDescription)
|
|
return;
|
|
|
|
option.Description = newDescription;
|
|
saveService.QueueSave(new ModSaveGroup(option.Group, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
|
|
}
|
|
|
|
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
|
|
public void SetManipulations(IModDataContainer subMod, MetaDictionary manipulations, SaveType saveType = SaveType.Queue)
|
|
{
|
|
if (subMod.Manipulations.Equals(manipulations))
|
|
return;
|
|
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
subMod.Manipulations.SetTo(manipulations);
|
|
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
}
|
|
|
|
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
|
|
public void SetFiles(IModDataContainer subMod, IReadOnlyDictionary<Utf8GamePath, FullPath> replacements, SaveType saveType = SaveType.Queue)
|
|
{
|
|
if (subMod.Files.SetEquals(replacements))
|
|
return;
|
|
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
subMod.Files.SetTo(replacements);
|
|
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
}
|
|
|
|
/// <summary> Forces a file save of the given container's group. </summary>
|
|
public void ForceSave(IModDataContainer subMod, SaveType saveType = SaveType.Queue)
|
|
=> saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
|
|
|
|
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
|
public void AddFiles(IModDataContainer subMod, IReadOnlyDictionary<Utf8GamePath, FullPath> additions)
|
|
{
|
|
var oldCount = subMod.Files.Count;
|
|
subMod.Files.AddFrom(additions);
|
|
if (oldCount != subMod.Files.Count)
|
|
{
|
|
saveService.QueueSave(new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
}
|
|
}
|
|
|
|
/// <summary> Set the file swaps for a given option. Replaces existing swaps. </summary>
|
|
public void SetFileSwaps(IModDataContainer subMod, IReadOnlyDictionary<Utf8GamePath, FullPath> swaps, SaveType saveType = SaveType.Queue)
|
|
{
|
|
if (subMod.FileSwaps.SetEquals(swaps))
|
|
return;
|
|
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
subMod.FileSwaps.SetTo(swaps);
|
|
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
|
|
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
|
|
}
|
|
|
|
/// <summary> Verify that a new option group name is unique in this mod. </summary>
|
|
public static bool VerifyFileName(Mod mod, IModGroup? group, string newName, bool message)
|
|
{
|
|
var path = newName.RemoveInvalidPathSymbols();
|
|
if (path.Length != 0
|
|
&& !mod.Groups.Any(o => !ReferenceEquals(o, group)
|
|
&& string.Equals(o.Name.RemoveInvalidPathSymbols(), path, StringComparison.OrdinalIgnoreCase)))
|
|
return true;
|
|
|
|
if (message)
|
|
Penumbra.Messager.NotificationMessage(
|
|
$"Could not name option {newName} because option with same filename {path} already exists.",
|
|
NotificationType.Warning, false);
|
|
|
|
return false;
|
|
}
|
|
|
|
public void DeleteOption(IModOption option)
|
|
{
|
|
switch (option)
|
|
{
|
|
case SingleSubMod s:
|
|
SingleEditor.DeleteOption(s);
|
|
return;
|
|
case MultiSubMod m:
|
|
MultiEditor.DeleteOption(m);
|
|
return;
|
|
case ImcSubMod i:
|
|
ImcEditor.DeleteOption(i);
|
|
return;
|
|
case CombiningSubMod c:
|
|
CombiningEditor.DeleteOption(c);
|
|
return;
|
|
}
|
|
}
|
|
|
|
public IModOption? AddOption(IModGroup group, IModOption option)
|
|
=> group switch
|
|
{
|
|
SingleModGroup s => SingleEditor.AddOption(s, option),
|
|
MultiModGroup m => MultiEditor.AddOption(m, option),
|
|
ImcModGroup i => ImcEditor.AddOption(i, option),
|
|
CombiningModGroup c => CombiningEditor.AddOption(c, option),
|
|
_ => null,
|
|
};
|
|
|
|
public IModOption? AddOption(IModGroup group, string newName)
|
|
=> group switch
|
|
{
|
|
SingleModGroup s => SingleEditor.AddOption(s, newName),
|
|
MultiModGroup m => MultiEditor.AddOption(m, newName),
|
|
ImcModGroup i => ImcEditor.AddOption(i, newName),
|
|
CombiningModGroup c => CombiningEditor.AddOption(c, newName),
|
|
_ => null,
|
|
};
|
|
|
|
public IModGroup? AddModGroup(Mod mod, GroupType type, string newName, SaveType saveType = SaveType.ImmediateSync)
|
|
=> type switch
|
|
{
|
|
GroupType.Single => SingleEditor.AddModGroup(mod, newName, saveType),
|
|
GroupType.Multi => MultiEditor.AddModGroup(mod, newName, saveType),
|
|
GroupType.Imc => ImcEditor.AddModGroup(mod, newName, default, default, saveType),
|
|
GroupType.Combining => CombiningEditor.AddModGroup(mod, newName, saveType),
|
|
_ => null,
|
|
};
|
|
|
|
public (IModGroup?, int, bool) FindOrAddModGroup(Mod mod, GroupType type, string name, SaveType saveType = SaveType.ImmediateSync)
|
|
=> type switch
|
|
{
|
|
GroupType.Single => SingleEditor.FindOrAddModGroup(mod, name, saveType),
|
|
GroupType.Multi => MultiEditor.FindOrAddModGroup(mod, name, saveType),
|
|
GroupType.Imc => ImcEditor.FindOrAddModGroup(mod, name, saveType),
|
|
GroupType.Combining => CombiningEditor.FindOrAddModGroup(mod, name, saveType),
|
|
_ => (null, -1, false),
|
|
};
|
|
|
|
public (IModOption?, int, bool) FindOrAddOption(IModGroup group, string name, SaveType saveType = SaveType.ImmediateSync)
|
|
=> group switch
|
|
{
|
|
SingleModGroup s => SingleEditor.FindOrAddOption(s, name, saveType),
|
|
MultiModGroup m => MultiEditor.FindOrAddOption(m, name, saveType),
|
|
ImcModGroup i => ImcEditor.FindOrAddOption(i, name, saveType),
|
|
CombiningModGroup c => CombiningEditor.FindOrAddOption(c, name, saveType),
|
|
_ => (null, -1, false),
|
|
};
|
|
|
|
public void MoveOption(IModOption option, int toIdx)
|
|
{
|
|
switch (option)
|
|
{
|
|
case SingleSubMod s:
|
|
SingleEditor.MoveOption(s, toIdx);
|
|
return;
|
|
case MultiSubMod m:
|
|
MultiEditor.MoveOption(m, toIdx);
|
|
return;
|
|
case ImcSubMod i:
|
|
ImcEditor.MoveOption(i, toIdx);
|
|
return;
|
|
case CombiningSubMod c:
|
|
CombiningEditor.MoveOption(c, toIdx);
|
|
return;
|
|
}
|
|
}
|
|
}
|