Everything's a service.

This commit is contained in:
Ottermandias 2023-03-17 17:51:05 +01:00
parent 2670ba52c1
commit dd8c910597
45 changed files with 2155 additions and 2212 deletions

View file

@ -290,7 +290,7 @@ public class ItemSwapWindow : IDisposable
}
catch (Exception e)
{
ChatUtil.NotificationMessage($"Could not create new Swap Option:\n{e}", "Error", NotificationType.Error);
Penumbra.ChatService.NotificationMessage($"Could not create new Swap Option:\n{e}", "Error", NotificationType.Error);
try
{
if (optionCreated && _selectedGroup != null)

View file

@ -90,7 +90,7 @@ public partial class ModEditWindow
LoadedShpkPath = FullPath.Empty;
LoadedShpkPathName = string.Empty;
AssociatedShpk = null;
ChatUtil.NotificationMessage( $"Could not load {LoadedShpkPath.ToPath()}:\n{e}", "Penumbra Advanced Editing", NotificationType.Error );
Penumbra.ChatService.NotificationMessage( $"Could not load {LoadedShpkPath.ToPath()}:\n{e}", "Penumbra Advanced Editing", NotificationType.Error );
}
Update();

View file

@ -80,12 +80,12 @@ public partial class ModEditWindow
}
catch( Exception e )
{
ChatUtil.NotificationMessage( $"Could not export {defaultName}{tab.Extension} to {name}:\n{e.Message}", "Penumbra Advanced Editing",
Penumbra.ChatService.NotificationMessage( $"Could not export {defaultName}{tab.Extension} to {name}:\n{e.Message}", "Penumbra Advanced Editing",
NotificationType.Error );
return;
}
ChatUtil.NotificationMessage( $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName( name )}",
Penumbra.ChatService.NotificationMessage( $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName( name )}",
"Penumbra Advanced Editing", NotificationType.Success );
} );
}
@ -110,7 +110,7 @@ public partial class ModEditWindow
}
catch( Exception e )
{
ChatUtil.NotificationMessage( $"Could not import {name}:\n{e.Message}", "Penumbra Advanced Editing", NotificationType.Error );
Penumbra.ChatService.NotificationMessage( $"Could not import {name}:\n{e.Message}", "Penumbra Advanced Editing", NotificationType.Error );
return;
}
@ -122,7 +122,7 @@ public partial class ModEditWindow
catch( Exception e )
{
tab.Shpk.SetInvalid();
ChatUtil.NotificationMessage( $"Failed to update resources after importing {name}:\n{e.Message}", "Penumbra Advanced Editing",
Penumbra.ChatService.NotificationMessage( $"Failed to update resources after importing {name}:\n{e.Message}", "Penumbra Advanced Editing",
NotificationType.Error );
return;
}

View file

@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
@ -30,31 +29,29 @@ public partial class ModEditWindow : Window, IDisposable
private Vector2 _iconSize = Vector2.Zero;
private bool _allowReduplicate = false;
public void ChangeMod( Mod mod )
public void ChangeMod(Mod mod)
{
if( mod == _mod )
{
if (mod == _mod)
return;
}
_editor?.Dispose();
_editor = new Editor( mod, mod.Default );
_editor = new Editor(mod, mod.Default);
_mod = mod;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2( 1240, 600 ),
MinimumSize = new Vector2(1240, 600),
MaximumSize = 4000 * Vector2.One,
};
_selectedFiles.Clear();
_modelTab.Reset();
_materialTab.Reset();
_shaderPackageTab.Reset();
_swapWindow.UpdateMod( mod, Penumbra.CollectionManager.Current[ mod.Index ].Settings );
_swapWindow.UpdateMod(mod, Penumbra.CollectionManager.Current[mod.Index].Settings);
}
public void ChangeOption( ISubMod? subMod )
=> _editor?.SetSubMod( subMod );
public void ChangeOption(ISubMod? subMod)
=> _editor?.SetSubMod(subMod);
public void UpdateModels()
=> _editor?.ScanModels();
@ -64,71 +61,53 @@ public partial class ModEditWindow : Window, IDisposable
public override void PreDraw()
{
using var performance = Penumbra.Performance.Measure( PerformanceType.UiAdvancedWindow );
using var performance = Penumbra.Performance.Measure(PerformanceType.UiAdvancedWindow);
var sb = new StringBuilder( 256 );
var sb = new StringBuilder(256);
var redirections = 0;
var unused = 0;
var size = _editor!.AvailableFiles.Sum( f =>
var size = _editor!.AvailableFiles.Sum(f =>
{
if( f.SubModUsage.Count > 0 )
{
if (f.SubModUsage.Count > 0)
redirections += f.SubModUsage.Count;
}
else
{
++unused;
}
return f.FileSize;
} );
});
var manipulations = 0;
var subMods = 0;
var swaps = _mod!.AllSubMods.Sum( m =>
var swaps = _mod!.AllSubMods.Sum(m =>
{
++subMods;
manipulations += m.Manipulations.Count;
return m.FileSwaps.Count;
} );
sb.Append( _mod!.Name );
if( subMods > 1 )
{
sb.Append( $" | {subMods} Options" );
}
});
sb.Append(_mod!.Name);
if (subMods > 1)
sb.Append($" | {subMods} Options");
if( size > 0 )
{
sb.Append( $" | {_editor.AvailableFiles.Count} Files ({Functions.HumanReadableSize( size )})" );
}
if (size > 0)
sb.Append($" | {_editor.AvailableFiles.Count} Files ({Functions.HumanReadableSize(size)})");
if( unused > 0 )
{
sb.Append( $" | {unused} Unused Files" );
}
if (unused > 0)
sb.Append($" | {unused} Unused Files");
if( _editor.MissingFiles.Count > 0 )
{
sb.Append( $" | {_editor.MissingFiles.Count} Missing Files" );
}
if (_editor.MissingFiles.Count > 0)
sb.Append($" | {_editor.MissingFiles.Count} Missing Files");
if( redirections > 0 )
{
sb.Append( $" | {redirections} Redirections" );
}
if (redirections > 0)
sb.Append($" | {redirections} Redirections");
if( manipulations > 0 )
{
sb.Append( $" | {manipulations} Manipulations" );
}
if (manipulations > 0)
sb.Append($" | {manipulations} Manipulations");
if( swaps > 0 )
{
sb.Append( $" | {swaps} Swaps" );
}
if (swaps > 0)
sb.Append($" | {swaps} Swaps");
_allowReduplicate = redirections != _editor.AvailableFiles.Count || _editor.MissingFiles.Count > 0;
sb.Append( WindowBaseLabel );
sb.Append(WindowBaseLabel);
WindowName = sb.ToString();
}
@ -140,15 +119,13 @@ public partial class ModEditWindow : Window, IDisposable
public override void Draw()
{
using var performance = Penumbra.Performance.Measure( PerformanceType.UiAdvancedWindow );
using var performance = Penumbra.Performance.Measure(PerformanceType.UiAdvancedWindow);
using var tabBar = ImRaii.TabBar( "##tabs" );
if( !tabBar )
{
using var tabBar = ImRaii.TabBar("##tabs");
if (!tabBar)
return;
}
_iconSize = new Vector2( ImGui.GetFrameHeight() );
_iconSize = new Vector2(ImGui.GetFrameHeight());
DrawFileTab();
DrawMetaTab();
DrawSwapTab();
@ -169,46 +146,40 @@ public partial class ModEditWindow : Window, IDisposable
private static string _materialSuffixTo = string.Empty;
private static GenderRace _raceCode = GenderRace.Unknown;
private static string RaceCodeName( GenderRace raceCode )
private static string RaceCodeName(GenderRace raceCode)
{
if( raceCode == GenderRace.Unknown )
{
if (raceCode == GenderRace.Unknown)
return "All Races and Genders";
}
var (gender, race) = raceCode.Split();
return $"({raceCode.ToRaceCode()}) {race.ToName()} {gender.ToName()} ";
}
private static void DrawRaceCodeCombo( Vector2 buttonSize )
private static void DrawRaceCodeCombo(Vector2 buttonSize)
{
ImGui.SetNextItemWidth( buttonSize.X );
using var combo = ImRaii.Combo( "##RaceCode", RaceCodeName( _raceCode ) );
if( !combo )
{
ImGui.SetNextItemWidth(buttonSize.X);
using var combo = ImRaii.Combo("##RaceCode", RaceCodeName(_raceCode));
if (!combo)
return;
}
foreach( var raceCode in Enum.GetValues< GenderRace >() )
foreach (var raceCode in Enum.GetValues<GenderRace>())
{
if( ImGui.Selectable( RaceCodeName( raceCode ), _raceCode == raceCode ) )
{
if (ImGui.Selectable(RaceCodeName(raceCode), _raceCode == raceCode))
_raceCode = raceCode;
}
}
}
public static void Draw( Editor editor, Vector2 buttonSize )
public static void Draw(Editor editor, Vector2 buttonSize)
{
DrawRaceCodeCombo( buttonSize );
DrawRaceCodeCombo(buttonSize);
ImGui.SameLine();
ImGui.SetNextItemWidth( buttonSize.X );
ImGui.InputTextWithHint( "##suffixFrom", "From...", ref _materialSuffixFrom, 32 );
ImGui.SetNextItemWidth(buttonSize.X);
ImGui.InputTextWithHint("##suffixFrom", "From...", ref _materialSuffixFrom, 32);
ImGui.SameLine();
ImGui.SetNextItemWidth( buttonSize.X );
ImGui.InputTextWithHint( "##suffixTo", "To...", ref _materialSuffixTo, 32 );
ImGui.SetNextItemWidth(buttonSize.X);
ImGui.InputTextWithHint("##suffixTo", "To...", ref _materialSuffixTo, 32);
ImGui.SameLine();
var disabled = !Editor.ValidString( _materialSuffixTo );
var disabled = !Editor.ValidString(_materialSuffixTo);
var tt = _materialSuffixTo.Length == 0
? "Please enter a target suffix."
: _materialSuffixFrom == _materialSuffixTo
@ -222,181 +193,149 @@ public partial class ModEditWindow : Window, IDisposable
: _raceCode == GenderRace.Unknown
? $"Convert all skin material suffices that are currently '{_materialSuffixFrom}' to '{_materialSuffixTo}'."
: $"Convert all skin material suffices for the given race code that are currently '{_materialSuffixFrom}' to '{_materialSuffixTo}'.";
if( ImGuiUtil.DrawDisabledButton( "Change Material Suffix", buttonSize, tt, disabled ) )
{
editor.ReplaceAllMaterials( _materialSuffixTo, _materialSuffixFrom, _raceCode );
}
if (ImGuiUtil.DrawDisabledButton("Change Material Suffix", buttonSize, tt, disabled))
editor.ReplaceAllMaterials(_materialSuffixTo, _materialSuffixFrom, _raceCode);
var anyChanges = editor.ModelFiles.Any( m => m.Changed );
if( ImGuiUtil.DrawDisabledButton( "Save All Changes", buttonSize,
anyChanges ? "Irreversibly rewrites all currently applied changes to model files." : "No changes made yet.", !anyChanges ) )
{
var anyChanges = editor.ModelFiles.Any(m => m.Changed);
if (ImGuiUtil.DrawDisabledButton("Save All Changes", buttonSize,
anyChanges ? "Irreversibly rewrites all currently applied changes to model files." : "No changes made yet.", !anyChanges))
editor.SaveAllModels();
}
ImGui.SameLine();
if( ImGuiUtil.DrawDisabledButton( "Revert All Changes", buttonSize,
anyChanges ? "Revert all currently made and unsaved changes." : "No changes made yet.", !anyChanges ) )
{
if (ImGuiUtil.DrawDisabledButton("Revert All Changes", buttonSize,
anyChanges ? "Revert all currently made and unsaved changes." : "No changes made yet.", !anyChanges))
editor.RestoreAllModels();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker(
"Model files refer to the skin material they should use. This skin material is always the same, but modders have started using different suffices to differentiate between body types.\n"
+ "This option allows you to switch the suffix of all model files to another. This changes the files, so you do this on your own risk.\n"
+ "If you do not know what the currently used suffix of this mod is, you can leave 'From' blank and it will replace all suffices with 'To', instead of only the matching ones." );
+ "If you do not know what the currently used suffix of this mod is, you can leave 'From' blank and it will replace all suffices with 'To', instead of only the matching ones.");
}
}
private void DrawMissingFilesTab()
{
if( _editor!.MissingFiles.Count == 0 )
{
if (_editor!.MissingFiles.Count == 0)
return;
}
using var tab = ImRaii.TabItem( "Missing Files" );
if( !tab )
{
using var tab = ImRaii.TabItem("Missing Files");
if (!tab)
return;
}
ImGui.NewLine();
if( ImGui.Button( "Remove Missing Files from Mod" ) )
{
if (ImGui.Button("Remove Missing Files from Mod"))
_editor.RemoveMissingPaths();
}
using var child = ImRaii.Child( "##unusedFiles", -Vector2.One, true );
if( !child )
{
using var child = ImRaii.Child("##unusedFiles", -Vector2.One, true);
if (!child)
return;
}
using var table = ImRaii.Table( "##missingFiles", 1, ImGuiTableFlags.RowBg, -Vector2.One );
if( !table )
{
using var table = ImRaii.Table("##missingFiles", 1, ImGuiTableFlags.RowBg, -Vector2.One);
if (!table)
return;
}
foreach( var path in _editor.MissingFiles )
foreach (var path in _editor.MissingFiles)
{
ImGui.TableNextColumn();
ImGui.TextUnformatted( path.FullName );
ImGui.TextUnformatted(path.FullName);
}
}
private void DrawDuplicatesTab()
{
using var tab = ImRaii.TabItem( "Duplicates" );
if( !tab )
{
using var tab = ImRaii.TabItem("Duplicates");
if (!tab)
return;
}
var buttonText = _editor!.DuplicatesFinished ? "Scan for Duplicates###ScanButton" : "Scanning for Duplicates...###ScanButton";
if( ImGuiUtil.DrawDisabledButton( buttonText, Vector2.Zero, "Search for identical files in this mod. This may take a while.",
!_editor.DuplicatesFinished ) )
{
if (ImGuiUtil.DrawDisabledButton(buttonText, Vector2.Zero, "Search for identical files in this mod. This may take a while.",
!_editor.DuplicatesFinished))
_editor.StartDuplicateCheck();
}
const string desc = "Tries to create a unique copy of a file for every game path manipulated and put them in [Groupname]/[Optionname]/[GamePath] order.\n"
const string desc =
"Tries to create a unique copy of a file for every game path manipulated and put them in [Groupname]/[Optionname]/[GamePath] order.\n"
+ "This will also delete all unused files and directories if it succeeds.\n"
+ "Care was taken that a failure should not destroy the mod but revert to its original state, but you use this at your own risk anyway.";
var modifier = Penumbra.Config.DeleteModModifier.IsActive();
var tt = _allowReduplicate ? desc : modifier ? desc : desc + $"\n\nNo duplicates detected! Hold {Penumbra.Config.DeleteModModifier} to force normalization anyway.";
var tt = _allowReduplicate ? desc :
modifier ? desc : desc + $"\n\nNo duplicates detected! Hold {Penumbra.Config.DeleteModModifier} to force normalization anyway.";
if( ImGuiUtil.DrawDisabledButton( "Re-Duplicate and Normalize Mod", Vector2.Zero, tt, !_allowReduplicate && !modifier ) )
if (ImGuiUtil.DrawDisabledButton("Re-Duplicate and Normalize Mod", Vector2.Zero, tt, !_allowReduplicate && !modifier))
{
_mod!.Normalize( Penumbra.ModManager );
_mod!.Normalize(Penumbra.ModManager);
_editor.RevertFiles();
}
if( !_editor.DuplicatesFinished )
if (!_editor.DuplicatesFinished)
{
ImGui.SameLine();
if( ImGui.Button( "Cancel" ) )
{
if (ImGui.Button("Cancel"))
_editor.Cancel();
}
return;
}
if( _editor.Duplicates.Count == 0 )
if (_editor.Duplicates.Count == 0)
{
ImGui.NewLine();
ImGui.TextUnformatted( "No duplicates found." );
ImGui.TextUnformatted("No duplicates found.");
return;
}
if( ImGui.Button( "Delete and Redirect Duplicates" ) )
{
if (ImGui.Button("Delete and Redirect Duplicates"))
_editor.DeleteDuplicates();
}
if( _editor.SavedSpace > 0 )
if (_editor.SavedSpace > 0)
{
ImGui.SameLine();
ImGui.TextUnformatted( $"Frees up {Functions.HumanReadableSize( _editor.SavedSpace )} from your hard drive." );
ImGui.TextUnformatted($"Frees up {Functions.HumanReadableSize(_editor.SavedSpace)} from your hard drive.");
}
using var child = ImRaii.Child( "##duptable", -Vector2.One, true );
if( !child )
{
using var child = ImRaii.Child("##duptable", -Vector2.One, true);
if (!child)
return;
}
using var table = ImRaii.Table( "##duplicates", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One );
if( !table )
{
using var table = ImRaii.Table("##duplicates", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One);
if (!table)
return;
}
var width = ImGui.CalcTextSize( "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN " ).X;
ImGui.TableSetupColumn( "file", ImGuiTableColumnFlags.WidthStretch );
ImGui.TableSetupColumn( "size", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize( "NNN.NNN " ).X );
ImGui.TableSetupColumn( "hash", ImGuiTableColumnFlags.WidthFixed,
ImGui.GetWindowWidth() > 2 * width ? width : ImGui.CalcTextSize( "NNNNNNNN... " ).X );
foreach( var (set, size, hash) in _editor.Duplicates.Where( s => s.Paths.Length > 1 ) )
var width = ImGui.CalcTextSize("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN ").X;
ImGui.TableSetupColumn("file", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("size", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("NNN.NNN ").X);
ImGui.TableSetupColumn("hash", ImGuiTableColumnFlags.WidthFixed,
ImGui.GetWindowWidth() > 2 * width ? width : ImGui.CalcTextSize("NNNNNNNN... ").X);
foreach (var (set, size, hash) in _editor.Duplicates.Where(s => s.Paths.Length > 1))
{
ImGui.TableNextColumn();
using var tree = ImRaii.TreeNode( set[ 0 ].FullName[ ( _mod!.ModPath.FullName.Length + 1 ).. ],
ImGuiTreeNodeFlags.NoTreePushOnOpen );
using var tree = ImRaii.TreeNode(set[0].FullName[(_mod!.ModPath.FullName.Length + 1)..],
ImGuiTreeNodeFlags.NoTreePushOnOpen);
ImGui.TableNextColumn();
ImGuiUtil.RightAlign( Functions.HumanReadableSize( size ) );
ImGuiUtil.RightAlign(Functions.HumanReadableSize(size));
ImGui.TableNextColumn();
using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) )
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont))
{
if( ImGui.GetWindowWidth() > 2 * width )
{
ImGuiUtil.RightAlign( string.Concat( hash.Select( b => b.ToString( "X2" ) ) ) );
}
if (ImGui.GetWindowWidth() > 2 * width)
ImGuiUtil.RightAlign(string.Concat(hash.Select(b => b.ToString("X2"))));
else
{
ImGuiUtil.RightAlign( string.Concat( hash.Take( 4 ).Select( b => b.ToString( "X2" ) ) ) + "..." );
}
ImGuiUtil.RightAlign(string.Concat(hash.Take(4).Select(b => b.ToString("X2"))) + "...");
}
if( !tree )
{
if (!tree)
continue;
}
using var indent = ImRaii.PushIndent();
foreach( var duplicate in set.Skip( 1 ) )
foreach (var duplicate in set.Skip(1))
{
ImGui.TableNextColumn();
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint );
using var node = ImRaii.TreeNode( duplicate.FullName[ ( _mod!.ModPath.FullName.Length + 1 ).. ], ImGuiTreeNodeFlags.Leaf );
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
using var node = ImRaii.TreeNode(duplicate.FullName[(_mod!.ModPath.FullName.Length + 1)..], ImGuiTreeNodeFlags.Leaf);
ImGui.TableNextColumn();
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint );
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
ImGui.TableNextColumn();
ImGui.TableSetBgColor( ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint );
ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, Colors.RedTableBgTint);
}
}
}
@ -404,34 +343,26 @@ public partial class ModEditWindow : Window, IDisposable
private void DrawOptionSelectHeader()
{
const string defaultOption = "Default Option";
using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, Vector2.Zero ).Push( ImGuiStyleVar.FrameRounding, 0 );
var width = new Vector2( ImGui.GetWindowWidth() / 3, 0 );
if( ImGuiUtil.DrawDisabledButton( defaultOption, width, "Switch to the default option for the mod.\nThis resets unsaved changes.",
_editor!.CurrentOption.IsDefault ) )
{
_editor.SetSubMod( _mod!.Default );
}
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero).Push(ImGuiStyleVar.FrameRounding, 0);
var width = new Vector2(ImGui.GetWindowWidth() / 3, 0);
if (ImGuiUtil.DrawDisabledButton(defaultOption, width, "Switch to the default option for the mod.\nThis resets unsaved changes.",
_editor!.CurrentOption.IsDefault))
_editor.SetSubMod(_mod!.Default);
ImGui.SameLine();
if( ImGuiUtil.DrawDisabledButton( "Refresh Data", width, "Refresh data for the current option.\nThis resets unsaved changes.", false ) )
{
_editor.SetSubMod( _editor.CurrentOption );
}
if (ImGuiUtil.DrawDisabledButton("Refresh Data", width, "Refresh data for the current option.\nThis resets unsaved changes.", false))
_editor.SetSubMod(_editor.CurrentOption);
ImGui.SameLine();
using var combo = ImRaii.Combo( "##optionSelector", _editor.CurrentOption.FullName, ImGuiComboFlags.NoArrowButton );
if( !combo )
{
using var combo = ImRaii.Combo("##optionSelector", _editor.CurrentOption.FullName, ImGuiComboFlags.NoArrowButton);
if (!combo)
return;
}
foreach( var option in _mod!.AllSubMods )
foreach (var option in _mod!.AllSubMods)
{
if( ImGui.Selectable( option.FullName, option == _editor.CurrentOption ) )
{
_editor.SetSubMod( option );
}
if (ImGui.Selectable(option.FullName, option == _editor.CurrentOption))
_editor.SetSubMod(option);
}
}
@ -440,100 +371,84 @@ public partial class ModEditWindow : Window, IDisposable
private void DrawSwapTab()
{
using var tab = ImRaii.TabItem( "File Swaps" );
if( !tab )
{
using var tab = ImRaii.TabItem("File Swaps");
if (!tab)
return;
}
DrawOptionSelectHeader();
var setsEqual = _editor!.CurrentSwaps.SetEquals( _editor.CurrentOption.FileSwaps );
var setsEqual = _editor!.CurrentSwaps.SetEquals(_editor.CurrentOption.FileSwaps);
var tt = setsEqual ? "No changes staged." : "Apply the currently staged changes to the option.";
ImGui.NewLine();
if( ImGuiUtil.DrawDisabledButton( "Apply Changes", Vector2.Zero, tt, setsEqual ) )
{
if (ImGuiUtil.DrawDisabledButton("Apply Changes", Vector2.Zero, tt, setsEqual))
_editor.ApplySwaps();
}
ImGui.SameLine();
tt = setsEqual ? "No changes staged." : "Revert all currently staged changes.";
if( ImGuiUtil.DrawDisabledButton( "Revert Changes", Vector2.Zero, tt, setsEqual ) )
{
if (ImGuiUtil.DrawDisabledButton("Revert Changes", Vector2.Zero, tt, setsEqual))
_editor.RevertSwaps();
}
using var child = ImRaii.Child( "##swaps", -Vector2.One, true );
if( !child )
{
using var child = ImRaii.Child("##swaps", -Vector2.One, true);
if (!child)
return;
}
using var list = ImRaii.Table( "##table", 3, ImGuiTableFlags.RowBg, -Vector2.One );
if( !list )
{
using var list = ImRaii.Table("##table", 3, ImGuiTableFlags.RowBg, -Vector2.One);
if (!list)
return;
}
var idx = 0;
var iconSize = ImGui.GetFrameHeight() * Vector2.One;
var pathSize = ImGui.GetContentRegionAvail().X / 2 - iconSize.X;
ImGui.TableSetupColumn( "button", ImGuiTableColumnFlags.WidthFixed, iconSize.X );
ImGui.TableSetupColumn( "source", ImGuiTableColumnFlags.WidthFixed, pathSize );
ImGui.TableSetupColumn( "value", ImGuiTableColumnFlags.WidthFixed, pathSize );
ImGui.TableSetupColumn("button", ImGuiTableColumnFlags.WidthFixed, iconSize.X);
ImGui.TableSetupColumn("source", ImGuiTableColumnFlags.WidthFixed, pathSize);
ImGui.TableSetupColumn("value", ImGuiTableColumnFlags.WidthFixed, pathSize);
foreach( var (gamePath, file) in _editor!.CurrentSwaps.ToList() )
foreach (var (gamePath, file) in _editor!.CurrentSwaps.ToList())
{
using var id = ImRaii.PushId( idx++ );
using var id = ImRaii.PushId(idx++);
ImGui.TableNextColumn();
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), iconSize, "Delete this swap.", false, true ) )
{
_editor.CurrentSwaps.Remove( gamePath );
}
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), iconSize, "Delete this swap.", false, true))
_editor.CurrentSwaps.Remove(gamePath);
ImGui.TableNextColumn();
var tmp = gamePath.Path.ToString();
ImGui.SetNextItemWidth( -1 );
if( ImGui.InputText( "##key", ref tmp, Utf8GamePath.MaxGamePathLength )
&& Utf8GamePath.FromString( tmp, out var path )
&& !_editor.CurrentSwaps.ContainsKey( path ) )
ImGui.SetNextItemWidth(-1);
if (ImGui.InputText("##key", ref tmp, Utf8GamePath.MaxGamePathLength)
&& Utf8GamePath.FromString(tmp, out var path)
&& !_editor.CurrentSwaps.ContainsKey(path))
{
_editor.CurrentSwaps.Remove( gamePath );
if( path.Length > 0 )
{
_editor.CurrentSwaps[ path ] = file;
}
_editor.CurrentSwaps.Remove(gamePath);
if (path.Length > 0)
_editor.CurrentSwaps[path] = file;
}
ImGui.TableNextColumn();
tmp = file.FullName;
ImGui.SetNextItemWidth( -1 );
if( ImGui.InputText( "##value", ref tmp, Utf8GamePath.MaxGamePathLength ) && tmp.Length > 0 )
{
_editor.CurrentSwaps[ gamePath ] = new FullPath( tmp );
}
ImGui.SetNextItemWidth(-1);
if (ImGui.InputText("##value", ref tmp, Utf8GamePath.MaxGamePathLength) && tmp.Length > 0)
_editor.CurrentSwaps[gamePath] = new FullPath(tmp);
}
ImGui.TableNextColumn();
var addable = Utf8GamePath.FromString( _newSwapKey, out var newPath )
&& newPath.Length > 0
var addable = Utf8GamePath.FromString(_newSwapKey, out var newPath)
&& newPath.Length > 0
&& _newSwapValue.Length > 0
&& _newSwapValue != _newSwapKey
&& !_editor.CurrentSwaps.ContainsKey( newPath );
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), iconSize, "Add a new file swap to this option.", !addable,
true ) )
&& _newSwapValue != _newSwapKey
&& !_editor.CurrentSwaps.ContainsKey(newPath);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, "Add a new file swap to this option.", !addable,
true))
{
_editor.CurrentSwaps[ newPath ] = new FullPath( _newSwapValue );
_newSwapKey = string.Empty;
_newSwapValue = string.Empty;
_editor.CurrentSwaps[newPath] = new FullPath(_newSwapValue);
_newSwapKey = string.Empty;
_newSwapValue = string.Empty;
}
ImGui.TableNextColumn();
ImGui.SetNextItemWidth( -1 );
ImGui.InputTextWithHint( "##swapKey", "Load this file...", ref _newSwapValue, Utf8GamePath.MaxGamePathLength );
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("##swapKey", "Load this file...", ref _newSwapValue, Utf8GamePath.MaxGamePathLength);
ImGui.TableNextColumn();
ImGui.SetNextItemWidth( -1 );
ImGui.InputTextWithHint( "##swapValue", "... instead of this file.", ref _newSwapKey, Utf8GamePath.MaxGamePathLength );
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("##swapValue", "... instead of this file.", ref _newSwapKey, Utf8GamePath.MaxGamePathLength);
}
/// <summary>
@ -544,50 +459,44 @@ public partial class ModEditWindow : Window, IDisposable
/// If none exists, goes through all options in the currently selected mod (if any) in order of priority and resolves in them.
/// If no redirection is found in either of those options, returns the original path.
/// </remarks>
private FullPath FindBestMatch( Utf8GamePath path )
private FullPath FindBestMatch(Utf8GamePath path)
{
var currentFile = Penumbra.CollectionManager.Current.ResolvePath( path );
if( currentFile != null )
{
var currentFile = Penumbra.CollectionManager.Current.ResolvePath(path);
if (currentFile != null)
return currentFile.Value;
}
if( _mod != null )
{
foreach( var option in _mod.Groups.OrderByDescending( g => g.Priority )
.SelectMany( g => g.WithIndex().OrderByDescending( o => g.OptionPriority( o.Index ) ).Select( g => g.Value ) )
.Append( _mod.Default ) )
if (_mod != null)
foreach (var option in _mod.Groups.OrderByDescending(g => g.Priority)
.SelectMany(g => g.WithIndex().OrderByDescending(o => g.OptionPriority(o.Index)).Select(g => g.Value))
.Append(_mod.Default))
{
if( option.Files.TryGetValue( path, out var value ) || option.FileSwaps.TryGetValue( path, out value ) )
{
if (option.Files.TryGetValue(path, out var value) || option.FileSwaps.TryGetValue(path, out value))
return value;
}
}
}
return new FullPath( path );
return new FullPath(path);
}
public ModEditWindow(CommunicatorService communicator)
: base( WindowBaseLabel )
{
_swapWindow = new ItemSwapWindow( communicator );
_materialTab = new FileEditor< MtrlTab >( "Materials", ".mtrl",
() => _editor?.MtrlFiles ?? Array.Empty< Editor.FileRegistry >(),
: base(WindowBaseLabel)
{
_swapWindow = new ItemSwapWindow(communicator);
_materialTab = new FileEditor<MtrlTab>("Materials", ".mtrl",
() => _editor?.MtrlFiles ?? Array.Empty<Editor.FileRegistry>(),
DrawMaterialPanel,
() => _mod?.ModPath.FullName ?? string.Empty,
bytes => new MtrlTab( this, new MtrlFile( bytes ) ) );
_modelTab = new FileEditor< MdlFile >( "Models", ".mdl",
() => _editor?.MdlFiles ?? Array.Empty< Editor.FileRegistry >(),
bytes => new MtrlTab(this, new MtrlFile(bytes)));
_modelTab = new FileEditor<MdlFile>("Models", ".mdl",
() => _editor?.MdlFiles ?? Array.Empty<Editor.FileRegistry>(),
DrawModelPanel,
() => _mod?.ModPath.FullName ?? string.Empty,
null );
_shaderPackageTab = new FileEditor< ShpkTab >( "Shader Packages", ".shpk",
() => _editor?.ShpkFiles ?? Array.Empty< Editor.FileRegistry >(),
null);
_shaderPackageTab = new FileEditor<ShpkTab>("Shader Packages", ".shpk",
() => _editor?.ShpkFiles ?? Array.Empty<Editor.FileRegistry>(),
DrawShaderPackagePanel,
() => _mod?.ModPath.FullName ?? string.Empty,
null );
_center = new CombinedTexture( _left, _right );
null);
_center = new CombinedTexture(_left, _right);
}
public void Dispose()
@ -598,4 +507,4 @@ public partial class ModEditWindow : Window, IDisposable
_center.Dispose();
_swapWindow.Dispose();
}
}
}