mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Some more work on mod merging/splitting, still WIP.
This commit is contained in:
parent
b50564f741
commit
5d96f789fe
4 changed files with 126 additions and 49 deletions
|
|
@ -15,7 +15,7 @@ public class DuplicateManager
|
||||||
{
|
{
|
||||||
private readonly SaveService _saveService;
|
private readonly SaveService _saveService;
|
||||||
private readonly ModManager _modManager;
|
private readonly ModManager _modManager;
|
||||||
private readonly SHA256 _hasher = SHA256.Create();
|
private readonly SHA256 _hasher = SHA256.Create();
|
||||||
private readonly List<(FullPath[] Paths, long Size, byte[] Hash)> _duplicates = new();
|
private readonly List<(FullPath[] Paths, long Size, byte[] Hash)> _duplicates = new();
|
||||||
|
|
||||||
public DuplicateManager(ModManager modManager, SaveService saveService)
|
public DuplicateManager(ModManager modManager, SaveService saveService)
|
||||||
|
|
@ -175,7 +175,8 @@ public class DuplicateManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe bool CompareFilesDirectly(FullPath f1, FullPath f2)
|
/// <summary> Check if two files are identical on a binary level. Returns true if they are identical. </summary>
|
||||||
|
public static unsafe bool CompareFilesDirectly(FullPath f1, FullPath f2)
|
||||||
{
|
{
|
||||||
if (!f1.Exists || !f2.Exists)
|
if (!f1.Exists || !f2.Exists)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
using Penumbra.Communication;
|
using Penumbra.Communication;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
|
@ -24,7 +24,9 @@ public class ModMerger : IDisposable
|
||||||
private readonly ModManager _mods;
|
private readonly ModManager _mods;
|
||||||
private readonly ModCreator _creator;
|
private readonly ModCreator _creator;
|
||||||
|
|
||||||
public Mod? MergeFromMod { get; private set; }
|
public Mod? MergeFromMod
|
||||||
|
=> _selector.Selected;
|
||||||
|
|
||||||
public Mod? MergeToMod;
|
public Mod? MergeToMod;
|
||||||
public string OptionGroupName = "Merges";
|
public string OptionGroupName = "Merges";
|
||||||
public string OptionName = string.Empty;
|
public string OptionName = string.Empty;
|
||||||
|
|
@ -32,11 +34,13 @@ public class ModMerger : IDisposable
|
||||||
|
|
||||||
private readonly Dictionary<string, string> _fileToFile = new();
|
private readonly Dictionary<string, string> _fileToFile = new();
|
||||||
private readonly HashSet<string> _createdDirectories = new();
|
private readonly HashSet<string> _createdDirectories = new();
|
||||||
public readonly HashSet<SubMod> SelectedOptions = new();
|
private readonly HashSet<int> _createdGroups = new();
|
||||||
|
private readonly HashSet<SubMod> _createdOptions = new();
|
||||||
|
|
||||||
private int _createdGroup = -1;
|
public readonly HashSet<SubMod> SelectedOptions = new();
|
||||||
private SubMod? _createdOption;
|
|
||||||
public Exception? Error { get; private set; }
|
public readonly IReadOnlyList<string> Warnings = new List<string>();
|
||||||
|
public Exception? Error { get; private set; }
|
||||||
|
|
||||||
public ModMerger(ModManager mods, ModOptionEditor editor, ModFileSystemSelector selector, DuplicateManager duplicates,
|
public ModMerger(ModManager mods, ModOptionEditor editor, ModFileSystemSelector selector, DuplicateManager duplicates,
|
||||||
CommunicatorService communicator, ModCreator creator)
|
CommunicatorService communicator, ModCreator creator)
|
||||||
|
|
@ -48,7 +52,7 @@ public class ModMerger : IDisposable
|
||||||
_creator = creator;
|
_creator = creator;
|
||||||
_mods = mods;
|
_mods = mods;
|
||||||
_selector.SelectionChanged += OnSelectionChange;
|
_selector.SelectionChanged += OnSelectionChange;
|
||||||
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.Api);
|
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModMerger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -61,7 +65,7 @@ public class ModMerger : IDisposable
|
||||||
=> _mods.Where(m => m != MergeFromMod);
|
=> _mods.Where(m => m != MergeFromMod);
|
||||||
|
|
||||||
public bool CanMerge
|
public bool CanMerge
|
||||||
=> MergeToMod != null && MergeToMod != MergeFromMod && !MergeFromMod!.HasOptions;
|
=> MergeToMod != null && MergeToMod != MergeFromMod;
|
||||||
|
|
||||||
public void Merge()
|
public void Merge()
|
||||||
{
|
{
|
||||||
|
|
@ -91,7 +95,34 @@ public class ModMerger : IDisposable
|
||||||
|
|
||||||
private void MergeWithOptions()
|
private void MergeWithOptions()
|
||||||
{
|
{
|
||||||
// Not supported
|
MergeIntoOption(Enumerable.Repeat(MergeFromMod!.Default, 1), MergeToMod!.Default, false);
|
||||||
|
|
||||||
|
foreach (var originalGroup in MergeFromMod!.Groups)
|
||||||
|
{
|
||||||
|
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, originalGroup.Type, originalGroup.Name);
|
||||||
|
if (groupCreated)
|
||||||
|
_createdGroups.Add(groupIdx);
|
||||||
|
if (group.Type != originalGroup.Type)
|
||||||
|
((List<string>)Warnings).Add(
|
||||||
|
$"The merged group {group.Name} already existed, but has a different type {group.Type} than the original group of type {originalGroup.Type}.");
|
||||||
|
|
||||||
|
foreach (var originalOption in originalGroup)
|
||||||
|
{
|
||||||
|
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, originalOption.Name);
|
||||||
|
if (optionCreated)
|
||||||
|
{
|
||||||
|
_createdOptions.Add(option);
|
||||||
|
MergeIntoOption(Enumerable.Repeat(originalOption, 1), option, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception(
|
||||||
|
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: The option {option.FullName} already existed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyFiles(MergeToMod!.ModPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MergeIntoOption(string groupName, string optionName)
|
private void MergeIntoOption(string groupName, string optionName)
|
||||||
|
|
@ -99,7 +130,7 @@ public class ModMerger : IDisposable
|
||||||
if (groupName.Length == 0 && optionName.Length == 0)
|
if (groupName.Length == 0 && optionName.Length == 0)
|
||||||
{
|
{
|
||||||
CopyFiles(MergeToMod!.ModPath);
|
CopyFiles(MergeToMod!.ModPath);
|
||||||
MergeIntoOption(MergeFromMod!.AllSubMods.Reverse(), MergeToMod!.Default);
|
MergeIntoOption(MergeFromMod!.AllSubMods.Reverse(), MergeToMod!.Default, true);
|
||||||
}
|
}
|
||||||
else if (groupName.Length * optionName.Length == 0)
|
else if (groupName.Length * optionName.Length == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -108,10 +139,10 @@ public class ModMerger : IDisposable
|
||||||
|
|
||||||
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, GroupType.Multi, groupName);
|
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, GroupType.Multi, groupName);
|
||||||
if (groupCreated)
|
if (groupCreated)
|
||||||
_createdGroup = groupIdx;
|
_createdGroups.Add(groupIdx);
|
||||||
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, optionName);
|
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, optionName);
|
||||||
if (optionCreated)
|
if (optionCreated)
|
||||||
_createdOption = option;
|
_createdOptions.Add(option);
|
||||||
var dir = ModCreator.NewOptionDirectory(MergeToMod!.ModPath, groupName);
|
var dir = ModCreator.NewOptionDirectory(MergeToMod!.ModPath, groupName);
|
||||||
if (!dir.Exists)
|
if (!dir.Exists)
|
||||||
_createdDirectories.Add(dir.FullName);
|
_createdDirectories.Add(dir.FullName);
|
||||||
|
|
@ -119,14 +150,36 @@ public class ModMerger : IDisposable
|
||||||
if (!dir.Exists)
|
if (!dir.Exists)
|
||||||
_createdDirectories.Add(dir.FullName);
|
_createdDirectories.Add(dir.FullName);
|
||||||
CopyFiles(dir);
|
CopyFiles(dir);
|
||||||
MergeIntoOption(MergeFromMod!.AllSubMods.Reverse(), option);
|
MergeIntoOption(MergeFromMod!.AllSubMods.Reverse(), option, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MergeIntoOption(IEnumerable<SubMod> mergeOptions, SubMod option)
|
private void MergeIntoOption(IEnumerable<ISubMod> mergeOptions, SubMod option, bool fromFileToFile)
|
||||||
{
|
{
|
||||||
var redirections = option.FileData.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
var redirections = option.FileData.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
var swaps = option.FileSwapData.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
var swaps = option.FileSwapData.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
var manips = option.ManipulationData.ToHashSet();
|
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 originalOption in mergeOptions)
|
||||||
{
|
{
|
||||||
foreach (var manip in originalOption.Manipulations)
|
foreach (var manip in originalOption.Manipulations)
|
||||||
|
|
@ -143,14 +196,14 @@ public class ModMerger : IDisposable
|
||||||
$"Could not add file swap {swapB} -> {swapA} from {originalOption.FullName} to {option.FullName} because another swap of the key already exists.");
|
$"Could not add file swap {swapB} -> {swapA} from {originalOption.FullName} to {option.FullName} because another swap of the key already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (gamePath, relPath) in originalOption.Files)
|
foreach (var (gamePath, path) in originalOption.Files)
|
||||||
{
|
{
|
||||||
if (!_fileToFile.TryGetValue(relPath.FullName, out var newFile))
|
if (!GetFullPath(path, out var newFile))
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"Could not add file redirection {relPath} -> {gamePath} from {originalOption.FullName} to {option.FullName} because the file does not exist in the new mod.");
|
$"Could not add file redirection {path} -> {gamePath} from {originalOption.FullName} to {option.FullName} because the file does not exist in the new mod.");
|
||||||
if (!redirections.TryAdd(gamePath, new FullPath(newFile)))
|
if (!redirections.TryAdd(gamePath, newFile))
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"Could not add file redirection {relPath} -> {gamePath} from {originalOption.FullName} to {option.FullName} because a redirection for the game path already exists.");
|
$"Could not add file redirection {path} -> {gamePath} from {originalOption.FullName} to {option.FullName} because a redirection for the game path already exists.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,6 +246,7 @@ public class ModMerger : IDisposable
|
||||||
if (mods.Count == 0)
|
if (mods.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
((List<string>)Warnings).Clear();
|
||||||
Error = null;
|
Error = null;
|
||||||
DirectoryInfo? dir = null;
|
DirectoryInfo? dir = null;
|
||||||
Mod? result = null;
|
Mod? result = null;
|
||||||
|
|
@ -261,10 +315,14 @@ public class ModMerger : IDisposable
|
||||||
{
|
{
|
||||||
var target = Path.GetRelativePath(parentPath, file.FullName);
|
var target = Path.GetRelativePath(parentPath, file.FullName);
|
||||||
target = Path.Combine(newMod.FullName, target);
|
target = Path.Combine(newMod.FullName, target);
|
||||||
|
var targetPath = new FullPath(target);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
|
||||||
File.Copy(file.FullName, target);
|
// Copy throws if the file exists, which we want.
|
||||||
|
// This copies if the target does not exist, throws if it exists and is different, or does nothing if it exists and is identical.
|
||||||
|
if (!File.Exists(target) || !DuplicateManager.CompareFilesDirectly(targetPath, file))
|
||||||
|
File.Copy(file.FullName, target);
|
||||||
Penumbra.Log.Verbose($"[Splitter] Copied file {file.FullName} to {target}.");
|
Penumbra.Log.Verbose($"[Splitter] Copied file {file.FullName} to {target}.");
|
||||||
ret.Add(path, new FullPath(target));
|
ret.Add(path, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -274,16 +332,24 @@ public class ModMerger : IDisposable
|
||||||
{
|
{
|
||||||
_fileToFile.Clear();
|
_fileToFile.Clear();
|
||||||
_createdDirectories.Clear();
|
_createdDirectories.Clear();
|
||||||
_createdOption = null;
|
_createdGroups.Clear();
|
||||||
_createdGroup = -1;
|
_createdOptions.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FailureCleanup()
|
private void FailureCleanup()
|
||||||
{
|
{
|
||||||
if (_createdGroup >= 0 && _createdGroup < MergeToMod!.Groups.Count)
|
foreach (var option in _createdOptions)
|
||||||
_editor.DeleteModGroup(MergeToMod!, _createdGroup);
|
{
|
||||||
else if (_createdOption != null)
|
_editor.DeleteOption(MergeToMod!, option.GroupIdx, option.OptionIdx);
|
||||||
_editor.DeleteOption(MergeToMod!, _createdOption.GroupIdx, _createdOption.OptionIdx);
|
Penumbra.Log.Verbose($"[Merger] Removed option {option.FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var group in _createdGroups)
|
||||||
|
{
|
||||||
|
var groupName = MergeToMod!.Groups[group];
|
||||||
|
_editor.DeleteModGroup(MergeToMod!, group);
|
||||||
|
Penumbra.Log.Verbose($"[Merger] Removed option group {groupName}.");
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var dir in _createdDirectories)
|
foreach (var dir in _createdDirectories)
|
||||||
{
|
{
|
||||||
|
|
@ -329,7 +395,6 @@ public class ModMerger : IDisposable
|
||||||
MergeToMod = null;
|
MergeToMod = null;
|
||||||
|
|
||||||
SelectedOptions.Clear();
|
SelectedOptions.Clear();
|
||||||
MergeFromMod = newSelection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
|
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
|
||||||
|
|
@ -339,10 +404,7 @@ public class ModMerger : IDisposable
|
||||||
case ModPathChangeType.Deleted:
|
case ModPathChangeType.Deleted:
|
||||||
{
|
{
|
||||||
if (mod == MergeFromMod)
|
if (mod == MergeFromMod)
|
||||||
{
|
|
||||||
SelectedOptions.Clear();
|
SelectedOptions.Clear();
|
||||||
MergeFromMod = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mod == MergeToMod)
|
if (mod == MergeToMod)
|
||||||
MergeToMod = null;
|
MergeToMod = null;
|
||||||
|
|
@ -350,11 +412,7 @@ public class ModMerger : IDisposable
|
||||||
}
|
}
|
||||||
case ModPathChangeType.StartingReload:
|
case ModPathChangeType.StartingReload:
|
||||||
SelectedOptions.Clear();
|
SelectedOptions.Clear();
|
||||||
MergeFromMod = null;
|
MergeToMod = null;
|
||||||
MergeToMod = null;
|
|
||||||
break;
|
|
||||||
case ModPathChangeType.Reloaded:
|
|
||||||
MergeFromMod = _selector.Selected;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ public class ModOptionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
|
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
|
||||||
public void OptionSetFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> replacements)
|
public void OptionSetFiles(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> replacements)
|
||||||
{
|
{
|
||||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||||
if (subMod.FileData.SetEquals(replacements))
|
if (subMod.FileData.SetEquals(replacements))
|
||||||
|
|
@ -362,7 +362,7 @@ public class ModOptionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Add additional file redirections to a given option, keeping already existing ones. Only fires an event if anything is actually added.</summary>
|
/// <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 OptionAddFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> additions)
|
public void OptionAddFiles(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> additions)
|
||||||
{
|
{
|
||||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||||
var oldCount = subMod.FileData.Count;
|
var oldCount = subMod.FileData.Count;
|
||||||
|
|
@ -375,7 +375,7 @@ public class ModOptionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set the file swaps for a given option. Replaces existing swaps. </summary>
|
/// <summary> Set the file swaps for a given option. Replaces existing swaps. </summary>
|
||||||
public void OptionSetFileSwaps(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> swaps)
|
public void OptionSetFileSwaps(Mod mod, int groupIdx, int optionIdx, IReadOnlyDictionary<Utf8GamePath, FullPath> swaps)
|
||||||
{
|
{
|
||||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||||
if (subMod.FileSwapData.SetEquals(swaps))
|
if (subMod.FileSwapData.SetEquals(swaps))
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,14 @@ public class ModMergeTab
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawCombo();
|
DrawCombo();
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
using (var disabled = ImRaii.Disabled(_modMerger.MergeFromMod.HasOptions))
|
||||||
ImGui.InputTextWithHint("##optionGroupInput", "Target Option Group", ref _modMerger.OptionGroupName, 64);
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
ImGui.InputTextWithHint("##optionGroupInput", "Target Option Group", ref _modMerger.OptionGroupName, 64);
|
||||||
ImGui.InputTextWithHint("##optionInput", "Target Option Name", ref _modMerger.OptionName, 64);
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||||
|
ImGui.InputTextWithHint("##optionInput", "Target Option Name", ref _modMerger.OptionName, 64);
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGuiUtil.DrawDisabledButton("Merge", Vector2.Zero, string.Empty, !_modMerger.CanMerge))
|
if (ImGuiUtil.DrawDisabledButton("Merge", Vector2.Zero, string.Empty, !_modMerger.CanMerge))
|
||||||
_modMerger.Merge();
|
_modMerger.Merge();
|
||||||
|
|
@ -50,7 +53,8 @@ public class ModMergeTab
|
||||||
ImGui.Dummy(Vector2.One);
|
ImGui.Dummy(Vector2.One);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Dummy(Vector2.One);
|
ImGui.Dummy(Vector2.One);
|
||||||
using (var table = ImRaii.Table("##options", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollY, ImGui.GetContentRegionAvail() with { Y = 6 * ImGui.GetFrameHeightWithSpacing()}))
|
using (var table = ImRaii.Table("##options", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollY,
|
||||||
|
ImGui.GetContentRegionAvail() with { Y = 6 * ImGui.GetFrameHeightWithSpacing() }))
|
||||||
{
|
{
|
||||||
foreach (var (option, idx) in _modMerger.MergeFromMod.AllSubMods.WithIndex())
|
foreach (var (option, idx) in _modMerger.MergeFromMod.AllSubMods.WithIndex())
|
||||||
{
|
{
|
||||||
|
|
@ -79,13 +83,27 @@ public class ModMergeTab
|
||||||
ImGuiUtil.DrawTableColumn(option.FileData.Count.ToString());
|
ImGuiUtil.DrawTableColumn(option.FileData.Count.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(option.FileSwapData.Count.ToString());
|
ImGuiUtil.DrawTableColumn(option.FileSwapData.Count.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(option.Manipulations.Count.ToString());
|
ImGuiUtil.DrawTableColumn(option.Manipulations.Count.ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.InputTextWithHint("##newModInput", "New Mod Name...", ref _newModName, 64);
|
ImGui.InputTextWithHint("##newModInput", "New Mod Name...", ref _newModName, 64);
|
||||||
if (ImGuiUtil.DrawDisabledButton("Split Off", Vector2.Zero, string.Empty, _newModName.Length == 0 || _modMerger.SelectedOptions.Count == 0))
|
if (ImGuiUtil.DrawDisabledButton("Split Off", Vector2.Zero, string.Empty,
|
||||||
|
_newModName.Length == 0 || _modMerger.SelectedOptions.Count == 0))
|
||||||
_modMerger.SplitIntoMod(_newModName);
|
_modMerger.SplitIntoMod(_newModName);
|
||||||
|
|
||||||
|
if (_modMerger.Warnings.Count > 0)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.Dummy(Vector2.One);
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.TutorialBorder);
|
||||||
|
foreach (var warning in _modMerger.Warnings.SkipLast(1))
|
||||||
|
{
|
||||||
|
ImGuiUtil.TextWrapped(warning);
|
||||||
|
ImGui.Separator();
|
||||||
|
}
|
||||||
|
ImGuiUtil.TextWrapped(_modMerger.Warnings[^1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (_modMerger.Error != null)
|
if (_modMerger.Error != null)
|
||||||
{
|
{
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue