diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index 7565c8b3..b48128e0 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -38,20 +38,20 @@ public class IpcTester : IDisposable private readonly ModSettings _modSettings; private readonly Temporary _temporary; - public IpcTester( DalamudPluginInterface pi, PenumbraIpcProviders ipcProviders ) + public IpcTester(DalamudPluginInterface pi, PenumbraIpcProviders ipcProviders, Mod.Manager modManager) { _ipcProviders = ipcProviders; - _pluginState = new PluginState( pi ); - _configuration = new Configuration( pi ); - _ui = new Ui( pi ); - _redrawing = new Redrawing( pi ); - _gameState = new GameState( pi ); - _resolve = new Resolve( pi ); - _collections = new Collections( pi ); - _meta = new Meta( pi ); - _mods = new Mods( pi ); - _modSettings = new ModSettings( pi ); - _temporary = new Temporary( pi ); + _pluginState = new PluginState(pi); + _configuration = new Configuration(pi); + _ui = new Ui(pi); + _redrawing = new Redrawing(pi); + _gameState = new GameState(pi); + _resolve = new Resolve(pi); + _collections = new Collections(pi); + _meta = new Meta(pi); + _mods = new Mods(pi); + _modSettings = new ModSettings(pi); + _temporary = new Temporary(pi, modManager); UnsubscribeEvents(); } @@ -60,7 +60,7 @@ public class IpcTester : IDisposable try { SubscribeEvents(); - ImGui.TextUnformatted( $"API Version: {_ipcProviders.Api.ApiVersion.Breaking}.{_ipcProviders.Api.ApiVersion.Feature:D4}" ); + ImGui.TextUnformatted($"API Version: {_ipcProviders.Api.ApiVersion.Breaking}.{_ipcProviders.Api.ApiVersion.Feature:D4}"); _pluginState.Draw(); _configuration.Draw(); _ui.Draw(); @@ -75,15 +75,15 @@ public class IpcTester : IDisposable _temporary.DrawCollections(); _temporary.DrawMods(); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Error( $"Error during IPC Tests:\n{e}" ); + Penumbra.Log.Error($"Error during IPC Tests:\n{e}"); } } private void SubscribeEvents() { - if( !_subscribed ) + if (!_subscribed) { _pluginState.Initialized.Enable(); _pluginState.Disposed.Enable(); @@ -105,7 +105,7 @@ public class IpcTester : IDisposable public void UnsubscribeEvents() { - if( _subscribed ) + if (_subscribed) { _pluginState.Initialized.Disable(); _pluginState.Disposed.Disable(); @@ -148,131 +148,121 @@ public class IpcTester : IDisposable _subscribed = false; } - private static void DrawIntro( string label, string info ) + private static void DrawIntro(string label, string info) { ImGui.TableNextColumn(); - ImGui.TextUnformatted( label ); + ImGui.TextUnformatted(label); ImGui.TableNextColumn(); - ImGui.TextUnformatted( info ); + ImGui.TextUnformatted(info); ImGui.TableNextColumn(); } private class PluginState { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber Initialized; - public readonly EventSubscriber Disposed; - public readonly EventSubscriber< bool > EnabledChange; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber Initialized; + public readonly EventSubscriber Disposed; + public readonly EventSubscriber EnabledChange; - private readonly List< DateTimeOffset > _initializedList = new(); - private readonly List< DateTimeOffset > _disposedList = new(); + private readonly List _initializedList = new(); + private readonly List _disposedList = new(); private DateTimeOffset _lastEnabledChange = DateTimeOffset.UnixEpoch; private bool? _lastEnabledValue; - public PluginState( DalamudPluginInterface pi ) + public PluginState(DalamudPluginInterface pi) { _pi = pi; - Initialized = Ipc.Initialized.Subscriber( pi, AddInitialized ); - Disposed = Ipc.Disposed.Subscriber( pi, AddDisposed ); - EnabledChange = Ipc.EnabledChange.Subscriber( pi, SetLastEnabled ); + Initialized = Ipc.Initialized.Subscriber(pi, AddInitialized); + Disposed = Ipc.Disposed.Subscriber(pi, AddDisposed); + EnabledChange = Ipc.EnabledChange.Subscriber(pi, SetLastEnabled); } public void Draw() { - using var _ = ImRaii.TreeNode( "Plugin State" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Plugin State"); + if (!_) return; - } - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - void DrawList( string label, string text, List< DateTimeOffset > list ) + void DrawList(string label, string text, List list) { - DrawIntro( label, text ); - if( list.Count == 0 ) + DrawIntro(label, text); + if (list.Count == 0) { - ImGui.TextUnformatted( "Never" ); + ImGui.TextUnformatted("Never"); } else { - ImGui.TextUnformatted( list[ ^1 ].LocalDateTime.ToString( CultureInfo.CurrentCulture ) ); - if( list.Count > 1 && ImGui.IsItemHovered() ) - { - ImGui.SetTooltip( string.Join( "\n", - list.SkipLast( 1 ).Select( t => t.LocalDateTime.ToString( CultureInfo.CurrentCulture ) ) ) ); - } + ImGui.TextUnformatted(list[^1].LocalDateTime.ToString(CultureInfo.CurrentCulture)); + if (list.Count > 1 && ImGui.IsItemHovered()) + ImGui.SetTooltip(string.Join("\n", + list.SkipLast(1).Select(t => t.LocalDateTime.ToString(CultureInfo.CurrentCulture)))); } } - DrawList( Ipc.Initialized.Label, "Last Initialized", _initializedList ); - DrawList( Ipc.Disposed.Label, "Last Disposed", _disposedList ); - DrawIntro( Ipc.ApiVersions.Label, "Current Version" ); - var (breaking, features) = Ipc.ApiVersions.Subscriber( _pi ).Invoke(); - ImGui.TextUnformatted( $"{breaking}.{features:D4}" ); - DrawIntro( Ipc.GetEnabledState.Label, "Current State" ); - ImGui.TextUnformatted( $"{Ipc.GetEnabledState.Subscriber( _pi ).Invoke()}" ); - DrawIntro( Ipc.EnabledChange.Label, "Last Change" ); - ImGui.TextUnformatted( _lastEnabledValue is { } v ? $"{_lastEnabledChange} (to {v})" : "Never" ); + DrawList(Ipc.Initialized.Label, "Last Initialized", _initializedList); + DrawList(Ipc.Disposed.Label, "Last Disposed", _disposedList); + DrawIntro(Ipc.ApiVersions.Label, "Current Version"); + var (breaking, features) = Ipc.ApiVersions.Subscriber(_pi).Invoke(); + ImGui.TextUnformatted($"{breaking}.{features:D4}"); + DrawIntro(Ipc.GetEnabledState.Label, "Current State"); + ImGui.TextUnformatted($"{Ipc.GetEnabledState.Subscriber(_pi).Invoke()}"); + DrawIntro(Ipc.EnabledChange.Label, "Last Change"); + ImGui.TextUnformatted(_lastEnabledValue is { } v ? $"{_lastEnabledChange} (to {v})" : "Never"); } private void AddInitialized() - => _initializedList.Add( DateTimeOffset.UtcNow ); + => _initializedList.Add(DateTimeOffset.UtcNow); private void AddDisposed() - => _disposedList.Add( DateTimeOffset.UtcNow ); + => _disposedList.Add(DateTimeOffset.UtcNow); - private void SetLastEnabled( bool val ) - => ( _lastEnabledChange, _lastEnabledValue ) = ( DateTimeOffset.Now, val ); + private void SetLastEnabled(bool val) + => (_lastEnabledChange, _lastEnabledValue) = (DateTimeOffset.Now, val); } private class Configuration { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber< string, bool > ModDirectoryChanged; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber ModDirectoryChanged; private string _currentConfiguration = string.Empty; private string _lastModDirectory = string.Empty; private bool _lastModDirectoryValid; private DateTimeOffset _lastModDirectoryTime = DateTimeOffset.MinValue; - public Configuration( DalamudPluginInterface pi ) + public Configuration(DalamudPluginInterface pi) { _pi = pi; - ModDirectoryChanged = Ipc.ModDirectoryChanged.Subscriber( pi, UpdateModDirectoryChanged ); + ModDirectoryChanged = Ipc.ModDirectoryChanged.Subscriber(pi, UpdateModDirectoryChanged); } public void Draw() { - using var _ = ImRaii.TreeNode( "Configuration" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Configuration"); + if (!_) return; - } - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( Ipc.GetModDirectory.Label, "Current Mod Directory" ); - ImGui.TextUnformatted( Ipc.GetModDirectory.Subscriber( _pi ).Invoke() ); - DrawIntro( Ipc.ModDirectoryChanged.Label, "Last Mod Directory Change" ); - ImGui.TextUnformatted( _lastModDirectoryTime > DateTimeOffset.MinValue - ? $"{_lastModDirectory} ({( _lastModDirectoryValid ? "Valid" : "Invalid" )}) at {_lastModDirectoryTime}" - : "None" ); - DrawIntro( Ipc.GetConfiguration.Label, "Configuration" ); - if( ImGui.Button( "Get" ) ) + DrawIntro(Ipc.GetModDirectory.Label, "Current Mod Directory"); + ImGui.TextUnformatted(Ipc.GetModDirectory.Subscriber(_pi).Invoke()); + DrawIntro(Ipc.ModDirectoryChanged.Label, "Last Mod Directory Change"); + ImGui.TextUnformatted(_lastModDirectoryTime > DateTimeOffset.MinValue + ? $"{_lastModDirectory} ({(_lastModDirectoryValid ? "Valid" : "Invalid")}) at {_lastModDirectoryTime}" + : "None"); + DrawIntro(Ipc.GetConfiguration.Label, "Configuration"); + if (ImGui.Button("Get")) { - _currentConfiguration = Ipc.GetConfiguration.Subscriber( _pi ).Invoke(); - ImGui.OpenPopup( "Config Popup" ); + _currentConfiguration = Ipc.GetConfiguration.Subscriber(_pi).Invoke(); + ImGui.OpenPopup("Config Popup"); } DrawConfigPopup(); @@ -280,33 +270,31 @@ public class IpcTester : IDisposable private void DrawConfigPopup() { - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var popup = ImRaii.Popup( "Config Popup" ); - if( popup ) + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); + using var popup = ImRaii.Popup("Config Popup"); + if (popup) { - using( var font = ImRaii.PushFont( UiBuilder.MonoFont ) ) + using (var font = ImRaii.PushFont(UiBuilder.MonoFont)) { - ImGuiUtil.TextWrapped( _currentConfiguration ); + ImGuiUtil.TextWrapped(_currentConfiguration); } - if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) - { + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) ImGui.CloseCurrentPopup(); - } } } - private void UpdateModDirectoryChanged( string path, bool valid ) - => ( _lastModDirectory, _lastModDirectoryValid, _lastModDirectoryTime ) = ( path, valid, DateTimeOffset.Now ); + private void UpdateModDirectoryChanged(string path, bool valid) + => (_lastModDirectory, _lastModDirectoryValid, _lastModDirectoryTime) = (path, valid, DateTimeOffset.Now); } private class Ui { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber< string > PreSettingsDraw; - public readonly EventSubscriber< string > PostSettingsDraw; - public readonly EventSubscriber< ChangedItemType, uint > Tooltip; - public readonly EventSubscriber< MouseButton, ChangedItemType, uint > Click; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber PreSettingsDraw; + public readonly EventSubscriber PostSettingsDraw; + public readonly EventSubscriber Tooltip; + public readonly EventSubscriber Click; private string _lastDrawnMod = string.Empty; private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue; @@ -318,194 +306,161 @@ public class IpcTester : IDisposable private string _modName = string.Empty; private PenumbraApiEc _ec = PenumbraApiEc.Success; - public Ui( DalamudPluginInterface pi ) + public Ui(DalamudPluginInterface pi) { _pi = pi; - PreSettingsDraw = Ipc.PreSettingsDraw.Subscriber( pi, UpdateLastDrawnMod ); - PostSettingsDraw = Ipc.PostSettingsDraw.Subscriber( pi, UpdateLastDrawnMod ); - Tooltip = Ipc.ChangedItemTooltip.Subscriber( pi, AddedTooltip ); - Click = Ipc.ChangedItemClick.Subscriber( pi, AddedClick ); + PreSettingsDraw = Ipc.PreSettingsDraw.Subscriber(pi, UpdateLastDrawnMod); + PostSettingsDraw = Ipc.PostSettingsDraw.Subscriber(pi, UpdateLastDrawnMod); + Tooltip = Ipc.ChangedItemTooltip.Subscriber(pi, AddedTooltip); + Click = Ipc.ChangedItemClick.Subscriber(pi, AddedClick); } public void Draw() { - using var _ = ImRaii.TreeNode( "UI" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("UI"); + if (!_) return; - } - using( var combo = ImRaii.Combo( "Tab to Open at", _selectTab.ToString() ) ) + using (var combo = ImRaii.Combo("Tab to Open at", _selectTab.ToString())) { - if( combo ) - { - foreach( var val in Enum.GetValues< TabType >() ) + if (combo) + foreach (var val in Enum.GetValues()) { - if( ImGui.Selectable( val.ToString(), _selectTab == val ) ) - { + if (ImGui.Selectable(val.ToString(), _selectTab == val)) _selectTab = val; - } } - } } - ImGui.InputTextWithHint( "##openMod", "Mod to Open at...", ref _modName, 256 ); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + ImGui.InputTextWithHint("##openMod", "Mod to Open at...", ref _modName, 256); + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( Ipc.PostSettingsDraw.Label, "Last Drawn Mod" ); - ImGui.TextUnformatted( _lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None" ); + DrawIntro(Ipc.PostSettingsDraw.Label, "Last Drawn Mod"); + ImGui.TextUnformatted(_lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None"); - DrawIntro( Ipc.ChangedItemTooltip.Label, "Add Tooltip" ); - if( ImGui.Checkbox( "##tooltip", ref _subscribedToTooltip ) ) + DrawIntro(Ipc.ChangedItemTooltip.Label, "Add Tooltip"); + if (ImGui.Checkbox("##tooltip", ref _subscribedToTooltip)) { - if( _subscribedToTooltip ) - { + if (_subscribedToTooltip) Tooltip.Enable(); - } else - { Tooltip.Disable(); - } } ImGui.SameLine(); - ImGui.TextUnformatted( _lastHovered ); + ImGui.TextUnformatted(_lastHovered); - DrawIntro( Ipc.ChangedItemClick.Label, "Subscribe Click" ); - if( ImGui.Checkbox( "##click", ref _subscribedToClick ) ) + DrawIntro(Ipc.ChangedItemClick.Label, "Subscribe Click"); + if (ImGui.Checkbox("##click", ref _subscribedToClick)) { - if( _subscribedToClick ) - { + if (_subscribedToClick) Click.Enable(); - } else - { Click.Disable(); - } } ImGui.SameLine(); - ImGui.TextUnformatted( _lastClicked ); - DrawIntro( Ipc.OpenMainWindow.Label, "Open Mod Window" ); - if( ImGui.Button( "Open##window" ) ) - { - _ec = Ipc.OpenMainWindow.Subscriber( _pi ).Invoke( _selectTab, _modName, _modName ); - } + ImGui.TextUnformatted(_lastClicked); + DrawIntro(Ipc.OpenMainWindow.Label, "Open Mod Window"); + if (ImGui.Button("Open##window")) + _ec = Ipc.OpenMainWindow.Subscriber(_pi).Invoke(_selectTab, _modName, _modName); ImGui.SameLine(); - ImGui.TextUnformatted( _ec.ToString() ); + ImGui.TextUnformatted(_ec.ToString()); - DrawIntro( Ipc.CloseMainWindow.Label, "Close Mod Window" ); - if( ImGui.Button( "Close##window" ) ) - { - Ipc.CloseMainWindow.Subscriber( _pi ).Invoke(); - } + DrawIntro(Ipc.CloseMainWindow.Label, "Close Mod Window"); + if (ImGui.Button("Close##window")) + Ipc.CloseMainWindow.Subscriber(_pi).Invoke(); } - private void UpdateLastDrawnMod( string name ) - => ( _lastDrawnMod, _lastDrawnModTime ) = ( name, DateTimeOffset.Now ); + private void UpdateLastDrawnMod(string name) + => (_lastDrawnMod, _lastDrawnModTime) = (name, DateTimeOffset.Now); - private void AddedTooltip( ChangedItemType type, uint id ) + private void AddedTooltip(ChangedItemType type, uint id) { - _lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; - ImGui.TextUnformatted( "IPC Test Successful" ); + _lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString(CultureInfo.CurrentCulture)}"; + ImGui.TextUnformatted("IPC Test Successful"); } - private void AddedClick( MouseButton button, ChangedItemType type, uint id ) + private void AddedClick(MouseButton button, ChangedItemType type, uint id) { - _lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; + _lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString(CultureInfo.CurrentCulture)}"; } } private class Redrawing { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber< IntPtr, int > Redrawn; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber Redrawn; private string _redrawName = string.Empty; private int _redrawIndex = 0; private string _lastRedrawnString = "None"; - public Redrawing( DalamudPluginInterface pi ) + public Redrawing(DalamudPluginInterface pi) { _pi = pi; - Redrawn = Ipc.GameObjectRedrawn.Subscriber( pi, SetLastRedrawn ); + Redrawn = Ipc.GameObjectRedrawn.Subscriber(pi, SetLastRedrawn); } public void Draw() { - using var _ = ImRaii.TreeNode( "Redrawing" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Redrawing"); + if (!_) return; - } - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( Ipc.RedrawObjectByName.Label, "Redraw by Name" ); - ImGui.SetNextItemWidth( 100 * UiHelpers.Scale ); - ImGui.InputTextWithHint( "##redrawName", "Name...", ref _redrawName, 32 ); + DrawIntro(Ipc.RedrawObjectByName.Label, "Redraw by Name"); + ImGui.SetNextItemWidth(100 * UiHelpers.Scale); + ImGui.InputTextWithHint("##redrawName", "Name...", ref _redrawName, 32); ImGui.SameLine(); - if( ImGui.Button( "Redraw##Name" ) ) - { - Ipc.RedrawObjectByName.Subscriber( _pi ).Invoke( _redrawName, RedrawType.Redraw ); - } + if (ImGui.Button("Redraw##Name")) + Ipc.RedrawObjectByName.Subscriber(_pi).Invoke(_redrawName, RedrawType.Redraw); - DrawIntro( Ipc.RedrawObject.Label, "Redraw Player Character" ); - if( ImGui.Button( "Redraw##pc" ) && DalamudServices.SClientState.LocalPlayer != null ) - { - Ipc.RedrawObject.Subscriber( _pi ).Invoke( DalamudServices.SClientState.LocalPlayer, RedrawType.Redraw ); - } + DrawIntro(Ipc.RedrawObject.Label, "Redraw Player Character"); + if (ImGui.Button("Redraw##pc") && DalamudServices.SClientState.LocalPlayer != null) + Ipc.RedrawObject.Subscriber(_pi).Invoke(DalamudServices.SClientState.LocalPlayer, RedrawType.Redraw); - DrawIntro( Ipc.RedrawObjectByIndex.Label, "Redraw by Index" ); + DrawIntro(Ipc.RedrawObjectByIndex.Label, "Redraw by Index"); var tmp = _redrawIndex; - ImGui.SetNextItemWidth( 100 * UiHelpers.Scale ); - if( ImGui.DragInt( "##redrawIndex", ref tmp, 0.1f, 0, DalamudServices.SObjects.Length ) ) - { - _redrawIndex = Math.Clamp( tmp, 0, DalamudServices.SObjects.Length ); - } + ImGui.SetNextItemWidth(100 * UiHelpers.Scale); + if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, DalamudServices.SObjects.Length)) + _redrawIndex = Math.Clamp(tmp, 0, DalamudServices.SObjects.Length); ImGui.SameLine(); - if( ImGui.Button( "Redraw##Index" ) ) - { - Ipc.RedrawObjectByIndex.Subscriber( _pi ).Invoke( _redrawIndex, RedrawType.Redraw ); - } + if (ImGui.Button("Redraw##Index")) + Ipc.RedrawObjectByIndex.Subscriber(_pi).Invoke(_redrawIndex, RedrawType.Redraw); - DrawIntro( Ipc.RedrawAll.Label, "Redraw All" ); - if( ImGui.Button( "Redraw##All" ) ) - { - Ipc.RedrawAll.Subscriber( _pi ).Invoke( RedrawType.Redraw ); - } + DrawIntro(Ipc.RedrawAll.Label, "Redraw All"); + if (ImGui.Button("Redraw##All")) + Ipc.RedrawAll.Subscriber(_pi).Invoke(RedrawType.Redraw); - DrawIntro( Ipc.GameObjectRedrawn.Label, "Last Redrawn Object:" ); - ImGui.TextUnformatted( _lastRedrawnString ); + DrawIntro(Ipc.GameObjectRedrawn.Label, "Last Redrawn Object:"); + ImGui.TextUnformatted(_lastRedrawnString); } - private void SetLastRedrawn( IntPtr address, int index ) + private void SetLastRedrawn(IntPtr address, int index) { - if( index < 0 || index > DalamudServices.SObjects.Length || address == IntPtr.Zero || DalamudServices.SObjects[ index ]?.Address != address ) - { + if (index < 0 + || index > DalamudServices.SObjects.Length + || address == IntPtr.Zero + || DalamudServices.SObjects[index]?.Address != address) _lastRedrawnString = "Invalid"; - } - _lastRedrawnString = $"{DalamudServices.SObjects[ index ]!.Name} (0x{address:X}, {index})"; + _lastRedrawnString = $"{DalamudServices.SObjects[index]!.Name} (0x{address:X}, {index})"; } } private class GameState { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr > CharacterBaseCreating; - public readonly EventSubscriber< IntPtr, string, IntPtr > CharacterBaseCreated; - public readonly EventSubscriber< IntPtr, string, string > GameObjectResourcePathResolved; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber CharacterBaseCreating; + public readonly EventSubscriber CharacterBaseCreated; + public readonly EventSubscriber GameObjectResourcePathResolved; private string _lastCreatedGameObjectName = string.Empty; @@ -519,94 +474,85 @@ public class IpcTester : IDisposable private IntPtr _currentDrawObject = IntPtr.Zero; private int _currentCutsceneActor = 0; - public GameState( DalamudPluginInterface pi ) + public GameState(DalamudPluginInterface pi) { _pi = pi; - CharacterBaseCreating = Ipc.CreatingCharacterBase.Subscriber( pi, UpdateLastCreated ); - CharacterBaseCreated = Ipc.CreatedCharacterBase.Subscriber( pi, UpdateLastCreated2 ); - GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Subscriber( pi, UpdateGameObjectResourcePath ); + CharacterBaseCreating = Ipc.CreatingCharacterBase.Subscriber(pi, UpdateLastCreated); + CharacterBaseCreated = Ipc.CreatedCharacterBase.Subscriber(pi, UpdateLastCreated2); + GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Subscriber(pi, UpdateGameObjectResourcePath); } public void Draw() { - using var _ = ImRaii.TreeNode( "Game State" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Game State"); + if (!_) return; - } - if( ImGui.InputTextWithHint( "##drawObject", "Draw Object Address..", ref _currentDrawObjectString, 16, - ImGuiInputTextFlags.CharsHexadecimal ) ) - { - _currentDrawObject = IntPtr.TryParse( _currentDrawObjectString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var tmp ) + if (ImGui.InputTextWithHint("##drawObject", "Draw Object Address..", ref _currentDrawObjectString, 16, + ImGuiInputTextFlags.CharsHexadecimal)) + _currentDrawObject = IntPtr.TryParse(_currentDrawObjectString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out var tmp) ? tmp : IntPtr.Zero; - } - ImGui.InputInt( "Cutscene Actor", ref _currentCutsceneActor, 0 ); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + ImGui.InputInt("Cutscene Actor", ref _currentCutsceneActor, 0); + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( Ipc.GetDrawObjectInfo.Label, "Draw Object Info" ); - if( _currentDrawObject == IntPtr.Zero ) + DrawIntro(Ipc.GetDrawObjectInfo.Label, "Draw Object Info"); + if (_currentDrawObject == IntPtr.Zero) { - ImGui.TextUnformatted( "Invalid" ); + ImGui.TextUnformatted("Invalid"); } else { - var (ptr, collection) = Ipc.GetDrawObjectInfo.Subscriber( _pi ).Invoke( _currentDrawObject ); - ImGui.TextUnformatted( ptr == IntPtr.Zero ? $"No Actor Associated, {collection}" : $"{ptr:X}, {collection}" ); + var (ptr, collection) = Ipc.GetDrawObjectInfo.Subscriber(_pi).Invoke(_currentDrawObject); + ImGui.TextUnformatted(ptr == IntPtr.Zero ? $"No Actor Associated, {collection}" : $"{ptr:X}, {collection}"); } - DrawIntro( Ipc.GetCutsceneParentIndex.Label, "Cutscene Parent" ); - ImGui.TextUnformatted( Ipc.GetCutsceneParentIndex.Subscriber( _pi ).Invoke( _currentCutsceneActor ).ToString() ); + DrawIntro(Ipc.GetCutsceneParentIndex.Label, "Cutscene Parent"); + ImGui.TextUnformatted(Ipc.GetCutsceneParentIndex.Subscriber(_pi).Invoke(_currentCutsceneActor).ToString()); - DrawIntro( Ipc.CreatingCharacterBase.Label, "Last Drawobject created" ); - if( _lastCreatedGameObjectTime < DateTimeOffset.Now ) - { - ImGui.TextUnformatted( _lastCreatedDrawObject != IntPtr.Zero + DrawIntro(Ipc.CreatingCharacterBase.Label, "Last Drawobject created"); + if (_lastCreatedGameObjectTime < DateTimeOffset.Now) + ImGui.TextUnformatted(_lastCreatedDrawObject != IntPtr.Zero ? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" - : $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" ); - } + : $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}"); - DrawIntro( Ipc.GameObjectResourcePathResolved.Label, "Last GamePath resolved" ); - if( _lastResolvedGamePathTime < DateTimeOffset.Now ) - { + DrawIntro(Ipc.GameObjectResourcePathResolved.Label, "Last GamePath resolved"); + if (_lastResolvedGamePathTime < DateTimeOffset.Now) ImGui.TextUnformatted( - $"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}" ); - } + $"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}"); } - private void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 ) + private void UpdateLastCreated(IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4) { - _lastCreatedGameObjectName = GetObjectName( gameObject ); + _lastCreatedGameObjectName = GetObjectName(gameObject); _lastCreatedGameObjectTime = DateTimeOffset.Now; _lastCreatedDrawObject = IntPtr.Zero; } - private void UpdateLastCreated2( IntPtr gameObject, string _, IntPtr drawObject ) + private void UpdateLastCreated2(IntPtr gameObject, string _, IntPtr drawObject) { - _lastCreatedGameObjectName = GetObjectName( gameObject ); + _lastCreatedGameObjectName = GetObjectName(gameObject); _lastCreatedGameObjectTime = DateTimeOffset.Now; _lastCreatedDrawObject = drawObject; } - private void UpdateGameObjectResourcePath( IntPtr gameObject, string gamePath, string fullPath ) + private void UpdateGameObjectResourcePath(IntPtr gameObject, string gamePath, string fullPath) { - _lastResolvedObject = GetObjectName( gameObject ); + _lastResolvedObject = GetObjectName(gameObject); _lastResolvedGamePath = gamePath; _lastResolvedFullPath = fullPath; _lastResolvedGamePathTime = DateTimeOffset.Now; } - private static unsafe string GetObjectName( IntPtr gameObject ) + private static unsafe string GetObjectName(IntPtr gameObject) { - var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; + var obj = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject; var name = obj != null ? obj->GetName() : null; - return name != null ? new ByteString( name ).ToString() : "Unknown"; + return name != null ? new ByteString(name).ToString() : "Unknown"; } } @@ -619,124 +565,110 @@ public class IpcTester : IDisposable private string _currentReversePath = string.Empty; private int _currentReverseIdx = 0; - public Resolve( DalamudPluginInterface pi ) + public Resolve(DalamudPluginInterface pi) => _pi = pi; public void Draw() { - using var _ = ImRaii.TreeNode( "Resolving" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Resolving"); + if (!_) return; - } - ImGui.InputTextWithHint( "##resolvePath", "Resolve this game path...", ref _currentResolvePath, Utf8GamePath.MaxGamePathLength ); - ImGui.InputTextWithHint( "##resolveCharacter", "Character Name (leave blank for default)...", ref _currentResolveCharacter, 32 ); - ImGui.InputTextWithHint( "##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath, - Utf8GamePath.MaxGamePathLength ); - ImGui.InputInt( "##resolveIdx", ref _currentReverseIdx, 0, 0 ); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + ImGui.InputTextWithHint("##resolvePath", "Resolve this game path...", ref _currentResolvePath, Utf8GamePath.MaxGamePathLength); + ImGui.InputTextWithHint("##resolveCharacter", "Character Name (leave blank for default)...", ref _currentResolveCharacter, 32); + ImGui.InputTextWithHint("##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath, + Utf8GamePath.MaxGamePathLength); + ImGui.InputInt("##resolveIdx", ref _currentReverseIdx, 0, 0); + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( Ipc.ResolveDefaultPath.Label, "Default Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( Ipc.ResolveDefaultPath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); - } + DrawIntro(Ipc.ResolveDefaultPath.Label, "Default Collection Resolve"); + if (_currentResolvePath.Length != 0) + ImGui.TextUnformatted(Ipc.ResolveDefaultPath.Subscriber(_pi).Invoke(_currentResolvePath)); - DrawIntro( Ipc.ResolveInterfacePath.Label, "Interface Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( Ipc.ResolveInterfacePath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); - } + DrawIntro(Ipc.ResolveInterfacePath.Label, "Interface Collection Resolve"); + if (_currentResolvePath.Length != 0) + ImGui.TextUnformatted(Ipc.ResolveInterfacePath.Subscriber(_pi).Invoke(_currentResolvePath)); - DrawIntro( Ipc.ResolvePlayerPath.Label, "Player Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( Ipc.ResolvePlayerPath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); - } + DrawIntro(Ipc.ResolvePlayerPath.Label, "Player Collection Resolve"); + if (_currentResolvePath.Length != 0) + ImGui.TextUnformatted(Ipc.ResolvePlayerPath.Subscriber(_pi).Invoke(_currentResolvePath)); - DrawIntro( Ipc.ResolveCharacterPath.Label, "Character Collection Resolve" ); - if( _currentResolvePath.Length != 0 && _currentResolveCharacter.Length != 0 ) - { - ImGui.TextUnformatted( Ipc.ResolveCharacterPath.Subscriber( _pi ).Invoke( _currentResolvePath, _currentResolveCharacter ) ); - } + DrawIntro(Ipc.ResolveCharacterPath.Label, "Character Collection Resolve"); + if (_currentResolvePath.Length != 0 && _currentResolveCharacter.Length != 0) + ImGui.TextUnformatted(Ipc.ResolveCharacterPath.Subscriber(_pi).Invoke(_currentResolvePath, _currentResolveCharacter)); - DrawIntro( Ipc.ResolveGameObjectPath.Label, "Game Object Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( Ipc.ResolveGameObjectPath.Subscriber( _pi ).Invoke( _currentResolvePath, _currentReverseIdx ) ); - } + DrawIntro(Ipc.ResolveGameObjectPath.Label, "Game Object Collection Resolve"); + if (_currentResolvePath.Length != 0) + ImGui.TextUnformatted(Ipc.ResolveGameObjectPath.Subscriber(_pi).Invoke(_currentResolvePath, _currentReverseIdx)); - DrawIntro( Ipc.ReverseResolvePath.Label, "Reversed Game Paths" ); - if( _currentReversePath.Length > 0 ) + DrawIntro(Ipc.ReverseResolvePath.Label, "Reversed Game Paths"); + if (_currentReversePath.Length > 0) { - var list = Ipc.ReverseResolvePath.Subscriber( _pi ).Invoke( _currentReversePath, _currentResolveCharacter ); - if( list.Length > 0 ) + var list = Ipc.ReverseResolvePath.Subscriber(_pi).Invoke(_currentReversePath, _currentResolveCharacter); + if (list.Length > 0) { - ImGui.TextUnformatted( list[ 0 ] ); - if( list.Length > 1 && ImGui.IsItemHovered() ) - { - ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); - } + ImGui.TextUnformatted(list[0]); + if (list.Length > 1 && ImGui.IsItemHovered()) + ImGui.SetTooltip(string.Join("\n", list.Skip(1))); } } - DrawIntro( Ipc.ReverseResolvePlayerPath.Label, "Reversed Game Paths (Player)" ); - if( _currentReversePath.Length > 0 ) + DrawIntro(Ipc.ReverseResolvePlayerPath.Label, "Reversed Game Paths (Player)"); + if (_currentReversePath.Length > 0) { - var list = Ipc.ReverseResolvePlayerPath.Subscriber( _pi ).Invoke( _currentReversePath ); - if( list.Length > 0 ) + var list = Ipc.ReverseResolvePlayerPath.Subscriber(_pi).Invoke(_currentReversePath); + if (list.Length > 0) { - ImGui.TextUnformatted( list[ 0 ] ); - if( list.Length > 1 && ImGui.IsItemHovered() ) - { - ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); - } + ImGui.TextUnformatted(list[0]); + if (list.Length > 1 && ImGui.IsItemHovered()) + ImGui.SetTooltip(string.Join("\n", list.Skip(1))); } } - DrawIntro( Ipc.ReverseResolveGameObjectPath.Label, "Reversed Game Paths (Game Object)" ); - if( _currentReversePath.Length > 0 ) + DrawIntro(Ipc.ReverseResolveGameObjectPath.Label, "Reversed Game Paths (Game Object)"); + if (_currentReversePath.Length > 0) { - var list = Ipc.ReverseResolveGameObjectPath.Subscriber( _pi ).Invoke( _currentReversePath, _currentReverseIdx ); - if( list.Length > 0 ) + var list = Ipc.ReverseResolveGameObjectPath.Subscriber(_pi).Invoke(_currentReversePath, _currentReverseIdx); + if (list.Length > 0) { - ImGui.TextUnformatted( list[ 0 ] ); - if( list.Length > 1 && ImGui.IsItemHovered() ) - { - ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); - } + ImGui.TextUnformatted(list[0]); + if (list.Length > 1 && ImGui.IsItemHovered()) + ImGui.SetTooltip(string.Join("\n", list.Skip(1))); } } - DrawIntro( Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)" ); - if( _currentResolvePath.Length > 0 || _currentReversePath.Length > 0 ) + DrawIntro(Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)"); + if (_currentResolvePath.Length > 0 || _currentReversePath.Length > 0) { - var forwardArray = _currentResolvePath.Length > 0 ? new[] { _currentResolvePath } : Array.Empty< string >(); - var reverseArray = _currentReversePath.Length > 0 ? new[] { _currentReversePath } : Array.Empty< string >(); - var ret = Ipc.ResolvePlayerPaths.Subscriber( _pi ).Invoke( forwardArray, reverseArray ); - var text = string.Empty; - if( ret.Item1.Length > 0 ) - { - if( ret.Item2.Length > 0 ) + var forwardArray = _currentResolvePath.Length > 0 + ? new[] { - text = $"Forward: {ret.Item1[ 0 ]} | Reverse: {string.Join( "; ", ret.Item2[ 0 ] )}."; + _currentResolvePath, } + : Array.Empty(); + var reverseArray = _currentReversePath.Length > 0 + ? new[] + { + _currentReversePath, + } + : Array.Empty(); + var ret = Ipc.ResolvePlayerPaths.Subscriber(_pi).Invoke(forwardArray, reverseArray); + var text = string.Empty; + if (ret.Item1.Length > 0) + { + if (ret.Item2.Length > 0) + text = $"Forward: {ret.Item1[0]} | Reverse: {string.Join("; ", ret.Item2[0])}."; else - { - text = $"Forward: {ret.Item1[ 0 ]}."; - } + text = $"Forward: {ret.Item1[0]}."; } - else if( ret.Item2.Length > 0 ) + else if (ret.Item2.Length > 0) { - text = $"Reverse: {string.Join( "; ", ret.Item2[ 0 ] )}."; + text = $"Reverse: {string.Join("; ", ret.Item2[0])}."; } - ImGui.TextUnformatted( text ); + ImGui.TextUnformatted(text); } } } @@ -751,95 +683,85 @@ public class IpcTester : IDisposable private bool _allowDeletion = true; private ApiCollectionType _type = ApiCollectionType.Current; - private string _characterCollectionName = string.Empty; - private IList< string > _collections = new List< string >(); - private string _changedItemCollection = string.Empty; - private IReadOnlyDictionary< string, object? > _changedItems = new Dictionary< string, object? >(); - private PenumbraApiEc _returnCode = PenumbraApiEc.Success; - private string? _oldCollection = null; + private string _characterCollectionName = string.Empty; + private IList _collections = new List(); + private string _changedItemCollection = string.Empty; + private IReadOnlyDictionary _changedItems = new Dictionary(); + private PenumbraApiEc _returnCode = PenumbraApiEc.Success; + private string? _oldCollection = null; - public Collections( DalamudPluginInterface pi ) + public Collections(DalamudPluginInterface pi) => _pi = pi; public void Draw() { - using var _ = ImRaii.TreeNode( "Collections" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Collections"); + if (!_) return; - } - ImGuiUtil.GenericEnumCombo( "Collection Type", 200, _type, out _type, t => ( ( CollectionType )t ).ToName() ); - ImGui.InputInt( "Object Index##Collections", ref _objectIdx, 0, 0 ); - ImGui.InputText( "Collection Name##Collections", ref _collectionName, 64 ); - ImGui.Checkbox( "Allow Assignment Creation", ref _allowCreation ); + ImGuiUtil.GenericEnumCombo("Collection Type", 200, _type, out _type, t => ((CollectionType)t).ToName()); + ImGui.InputInt("Object Index##Collections", ref _objectIdx, 0, 0); + ImGui.InputText("Collection Name##Collections", ref _collectionName, 64); + ImGui.Checkbox("Allow Assignment Creation", ref _allowCreation); ImGui.SameLine(); - ImGui.Checkbox( "Allow Assignment Deletion", ref _allowDeletion ); + ImGui.Checkbox("Allow Assignment Deletion", ref _allowDeletion); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( "Last Return Code", _returnCode.ToString() ); - if( _oldCollection != null ) - { - ImGui.TextUnformatted( _oldCollection.Length == 0 ? "Created" : _oldCollection ); - } + DrawIntro("Last Return Code", _returnCode.ToString()); + if (_oldCollection != null) + ImGui.TextUnformatted(_oldCollection.Length == 0 ? "Created" : _oldCollection); - DrawIntro( Ipc.GetCurrentCollectionName.Label, "Current Collection" ); - ImGui.TextUnformatted( Ipc.GetCurrentCollectionName.Subscriber( _pi ).Invoke() ); - DrawIntro( Ipc.GetDefaultCollectionName.Label, "Default Collection" ); - ImGui.TextUnformatted( Ipc.GetDefaultCollectionName.Subscriber( _pi ).Invoke() ); - DrawIntro( Ipc.GetInterfaceCollectionName.Label, "Interface Collection" ); - ImGui.TextUnformatted( Ipc.GetInterfaceCollectionName.Subscriber( _pi ).Invoke() ); - DrawIntro( Ipc.GetCharacterCollectionName.Label, "Character" ); - ImGui.SetNextItemWidth( 200 * UiHelpers.Scale ); - ImGui.InputTextWithHint( "##characterCollectionName", "Character Name...", ref _characterCollectionName, 64 ); - var (c, s) = Ipc.GetCharacterCollectionName.Subscriber( _pi ).Invoke( _characterCollectionName ); + DrawIntro(Ipc.GetCurrentCollectionName.Label, "Current Collection"); + ImGui.TextUnformatted(Ipc.GetCurrentCollectionName.Subscriber(_pi).Invoke()); + DrawIntro(Ipc.GetDefaultCollectionName.Label, "Default Collection"); + ImGui.TextUnformatted(Ipc.GetDefaultCollectionName.Subscriber(_pi).Invoke()); + DrawIntro(Ipc.GetInterfaceCollectionName.Label, "Interface Collection"); + ImGui.TextUnformatted(Ipc.GetInterfaceCollectionName.Subscriber(_pi).Invoke()); + DrawIntro(Ipc.GetCharacterCollectionName.Label, "Character"); + ImGui.SetNextItemWidth(200 * UiHelpers.Scale); + ImGui.InputTextWithHint("##characterCollectionName", "Character Name...", ref _characterCollectionName, 64); + var (c, s) = Ipc.GetCharacterCollectionName.Subscriber(_pi).Invoke(_characterCollectionName); ImGui.SameLine(); - ImGui.TextUnformatted( $"{c}, {( s ? "Custom" : "Default" )}" ); + ImGui.TextUnformatted($"{c}, {(s ? "Custom" : "Default")}"); - DrawIntro( Ipc.GetCollections.Label, "Collections" ); - if( ImGui.Button( "Get##Collections" ) ) + DrawIntro(Ipc.GetCollections.Label, "Collections"); + if (ImGui.Button("Get##Collections")) { - _collections = Ipc.GetCollections.Subscriber( _pi ).Invoke(); - ImGui.OpenPopup( "Collections" ); + _collections = Ipc.GetCollections.Subscriber(_pi).Invoke(); + ImGui.OpenPopup("Collections"); } - DrawIntro( Ipc.GetCollectionForType.Label, "Get Special Collection" ); - var name = Ipc.GetCollectionForType.Subscriber( _pi ).Invoke( _type ); - ImGui.TextUnformatted( name.Length == 0 ? "Unassigned" : name ); - DrawIntro( Ipc.SetCollectionForType.Label, "Set Special Collection" ); - if( ImGui.Button( "Set##TypeCollection" ) ) - { - ( _returnCode, _oldCollection ) = Ipc.SetCollectionForType.Subscriber( _pi ).Invoke( _type, _collectionName, _allowCreation, _allowDeletion ); - } + DrawIntro(Ipc.GetCollectionForType.Label, "Get Special Collection"); + var name = Ipc.GetCollectionForType.Subscriber(_pi).Invoke(_type); + ImGui.TextUnformatted(name.Length == 0 ? "Unassigned" : name); + DrawIntro(Ipc.SetCollectionForType.Label, "Set Special Collection"); + if (ImGui.Button("Set##TypeCollection")) + (_returnCode, _oldCollection) = + Ipc.SetCollectionForType.Subscriber(_pi).Invoke(_type, _collectionName, _allowCreation, _allowDeletion); - DrawIntro( Ipc.GetCollectionForObject.Label, "Get Object Collection" ); - ( var valid, var individual, name ) = Ipc.GetCollectionForObject.Subscriber( _pi ).Invoke( _objectIdx ); + DrawIntro(Ipc.GetCollectionForObject.Label, "Get Object Collection"); + (var valid, var individual, name) = Ipc.GetCollectionForObject.Subscriber(_pi).Invoke(_objectIdx); ImGui.TextUnformatted( - $"{( valid ? "Valid" : "Invalid" )} Object, {( name.Length == 0 ? "Unassigned" : name )}{( individual ? " (Individual Assignment)" : string.Empty )}" ); - DrawIntro( Ipc.SetCollectionForObject.Label, "Set Object Collection" ); - if( ImGui.Button( "Set##ObjectCollection" ) ) - { - ( _returnCode, _oldCollection ) = Ipc.SetCollectionForObject.Subscriber( _pi ).Invoke( _objectIdx, _collectionName, _allowCreation, _allowDeletion ); - } + $"{(valid ? "Valid" : "Invalid")} Object, {(name.Length == 0 ? "Unassigned" : name)}{(individual ? " (Individual Assignment)" : string.Empty)}"); + DrawIntro(Ipc.SetCollectionForObject.Label, "Set Object Collection"); + if (ImGui.Button("Set##ObjectCollection")) + (_returnCode, _oldCollection) = Ipc.SetCollectionForObject.Subscriber(_pi) + .Invoke(_objectIdx, _collectionName, _allowCreation, _allowDeletion); - if( _returnCode == PenumbraApiEc.NothingChanged && _oldCollection.IsNullOrEmpty() ) - { + if (_returnCode == PenumbraApiEc.NothingChanged && _oldCollection.IsNullOrEmpty()) _oldCollection = null; - } - DrawIntro( Ipc.GetChangedItems.Label, "Changed Item List" ); - ImGui.SetNextItemWidth( 200 * UiHelpers.Scale ); - ImGui.InputTextWithHint( "##changedCollection", "Collection Name...", ref _changedItemCollection, 64 ); + DrawIntro(Ipc.GetChangedItems.Label, "Changed Item List"); + ImGui.SetNextItemWidth(200 * UiHelpers.Scale); + ImGui.InputTextWithHint("##changedCollection", "Collection Name...", ref _changedItemCollection, 64); ImGui.SameLine(); - if( ImGui.Button( "Get" ) ) + if (ImGui.Button("Get")) { - _changedItems = Ipc.GetChangedItems.Subscriber( _pi ).Invoke( _changedItemCollection ); - ImGui.OpenPopup( "Changed Item List" ); + _changedItems = Ipc.GetChangedItems.Subscriber(_pi).Invoke(_changedItemCollection); + ImGui.OpenPopup("Changed Item List"); } DrawChangedItemPopup(); @@ -848,42 +770,30 @@ public class IpcTester : IDisposable private void DrawChangedItemPopup() { - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var p = ImRaii.Popup( "Changed Item List" ); - if( !p ) - { + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); + using var p = ImRaii.Popup("Changed Item List"); + if (!p) return; - } - foreach( var item in _changedItems ) - { - ImGui.TextUnformatted( item.Key ); - } + foreach (var item in _changedItems) + ImGui.TextUnformatted(item.Key); - if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) - { + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) ImGui.CloseCurrentPopup(); - } } private void DrawCollectionPopup() { - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var p = ImRaii.Popup( "Collections" ); - if( !p ) - { + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); + using var p = ImRaii.Popup("Collections"); + if (!p) return; - } - foreach( var collection in _collections ) - { - ImGui.TextUnformatted( collection ); - } + foreach (var collection in _collections) + ImGui.TextUnformatted(collection); - if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) - { + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) ImGui.CloseCurrentPopup(); - } } } @@ -894,44 +804,40 @@ public class IpcTester : IDisposable private string _characterName = string.Empty; private int _gameObjectIndex = 0; - public Meta( DalamudPluginInterface pi ) + public Meta(DalamudPluginInterface pi) => _pi = pi; public void Draw() { - using var _ = ImRaii.TreeNode( "Meta" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Meta"); + if (!_) return; - } - ImGui.InputTextWithHint( "##characterName", "Character Name...", ref _characterName, 64 ); - ImGui.InputInt( "##metaIdx", ref _gameObjectIndex, 0, 0 ); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + ImGui.InputTextWithHint("##characterName", "Character Name...", ref _characterName, 64); + ImGui.InputInt("##metaIdx", ref _gameObjectIndex, 0, 0); + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; + + DrawIntro(Ipc.GetMetaManipulations.Label, "Meta Manipulations"); + if (ImGui.Button("Copy to Clipboard")) + { + var base64 = Ipc.GetMetaManipulations.Subscriber(_pi).Invoke(_characterName); + ImGui.SetClipboardText(base64); } - DrawIntro( Ipc.GetMetaManipulations.Label, "Meta Manipulations" ); - if( ImGui.Button( "Copy to Clipboard" ) ) + DrawIntro(Ipc.GetPlayerMetaManipulations.Label, "Player Meta Manipulations"); + if (ImGui.Button("Copy to Clipboard##Player")) { - var base64 = Ipc.GetMetaManipulations.Subscriber( _pi ).Invoke( _characterName ); - ImGui.SetClipboardText( base64 ); + var base64 = Ipc.GetPlayerMetaManipulations.Subscriber(_pi).Invoke(); + ImGui.SetClipboardText(base64); } - DrawIntro( Ipc.GetPlayerMetaManipulations.Label, "Player Meta Manipulations" ); - if( ImGui.Button( "Copy to Clipboard##Player" ) ) + DrawIntro(Ipc.GetGameObjectMetaManipulations.Label, "Game Object Manipulations"); + if (ImGui.Button("Copy to Clipboard##GameObject")) { - var base64 = Ipc.GetPlayerMetaManipulations.Subscriber( _pi ).Invoke(); - ImGui.SetClipboardText( base64 ); - } - - DrawIntro( Ipc.GetGameObjectMetaManipulations.Label, "Game Object Manipulations" ); - if( ImGui.Button( "Copy to Clipboard##GameObject" ) ) - { - var base64 = Ipc.GetGameObjectMetaManipulations.Subscriber( _pi ).Invoke( _gameObjectIndex ); - ImGui.SetClipboardText( base64 ); + var base64 = Ipc.GetGameObjectMetaManipulations.Subscriber(_pi).Invoke(_gameObjectIndex); + ImGui.SetClipboardText(base64); } } } @@ -940,18 +846,18 @@ public class IpcTester : IDisposable { private readonly DalamudPluginInterface _pi; - private string _modDirectory = string.Empty; - private string _modName = string.Empty; - private string _pathInput = string.Empty; - private PenumbraApiEc _lastReloadEc; - private PenumbraApiEc _lastAddEc; - private PenumbraApiEc _lastDeleteEc; - private PenumbraApiEc _lastSetPathEc; - private IList< (string, string) > _mods = new List< (string, string) >(); + private string _modDirectory = string.Empty; + private string _modName = string.Empty; + private string _pathInput = string.Empty; + private PenumbraApiEc _lastReloadEc; + private PenumbraApiEc _lastAddEc; + private PenumbraApiEc _lastDeleteEc; + private PenumbraApiEc _lastSetPathEc; + private IList<(string, string)> _mods = new List<(string, string)>(); - public readonly EventSubscriber< string > DeleteSubscriber; - public readonly EventSubscriber< string > AddSubscriber; - public readonly EventSubscriber< string, string > MoveSubscriber; + public readonly EventSubscriber DeleteSubscriber; + public readonly EventSubscriber AddSubscriber; + public readonly EventSubscriber MoveSubscriber; private DateTimeOffset _lastDeletedModTime = DateTimeOffset.UnixEpoch; private string _lastDeletedMod = string.Empty; @@ -961,137 +867,113 @@ public class IpcTester : IDisposable private string _lastMovedModFrom = string.Empty; private string _lastMovedModTo = string.Empty; - public Mods( DalamudPluginInterface pi ) + public Mods(DalamudPluginInterface pi) { _pi = pi; - DeleteSubscriber = Ipc.ModDeleted.Subscriber( pi, s => + DeleteSubscriber = Ipc.ModDeleted.Subscriber(pi, s => { _lastDeletedModTime = DateTimeOffset.UtcNow; _lastDeletedMod = s; - } ); - AddSubscriber = Ipc.ModAdded.Subscriber( pi, s => + }); + AddSubscriber = Ipc.ModAdded.Subscriber(pi, s => { _lastAddedModTime = DateTimeOffset.UtcNow; _lastAddedMod = s; - } ); - MoveSubscriber = Ipc.ModMoved.Subscriber( pi, ( s1, s2 ) => + }); + MoveSubscriber = Ipc.ModMoved.Subscriber(pi, (s1, s2) => { _lastMovedModTime = DateTimeOffset.UtcNow; _lastMovedModFrom = s1; _lastMovedModTo = s2; - } ); + }); } public void Draw() { - using var _ = ImRaii.TreeNode( "Mods" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Mods"); + if (!_) return; - } - ImGui.InputTextWithHint( "##modDir", "Mod Directory Name...", ref _modDirectory, 100 ); - ImGui.InputTextWithHint( "##modName", "Mod Name...", ref _modName, 100 ); - ImGui.InputTextWithHint( "##path", "New Path...", ref _pathInput, 100 ); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + ImGui.InputTextWithHint("##modDir", "Mod Directory Name...", ref _modDirectory, 100); + ImGui.InputTextWithHint("##modName", "Mod Name...", ref _modName, 100); + ImGui.InputTextWithHint("##path", "New Path...", ref _pathInput, 100); + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; + + DrawIntro(Ipc.GetMods.Label, "Mods"); + if (ImGui.Button("Get##Mods")) + { + _mods = Ipc.GetMods.Subscriber(_pi).Invoke(); + ImGui.OpenPopup("Mods"); } - DrawIntro( Ipc.GetMods.Label, "Mods" ); - if( ImGui.Button( "Get##Mods" ) ) - { - _mods = Ipc.GetMods.Subscriber( _pi ).Invoke(); - ImGui.OpenPopup( "Mods" ); - } - - DrawIntro( Ipc.ReloadMod.Label, "Reload Mod" ); - if( ImGui.Button( "Reload" ) ) - { - _lastReloadEc = Ipc.ReloadMod.Subscriber( _pi ).Invoke( _modDirectory, _modName ); - } + DrawIntro(Ipc.ReloadMod.Label, "Reload Mod"); + if (ImGui.Button("Reload")) + _lastReloadEc = Ipc.ReloadMod.Subscriber(_pi).Invoke(_modDirectory, _modName); ImGui.SameLine(); - ImGui.TextUnformatted( _lastReloadEc.ToString() ); + ImGui.TextUnformatted(_lastReloadEc.ToString()); - DrawIntro( Ipc.AddMod.Label, "Add Mod" ); - if( ImGui.Button( "Add" ) ) - { - _lastAddEc = Ipc.AddMod.Subscriber( _pi ).Invoke( _modDirectory ); - } + DrawIntro(Ipc.AddMod.Label, "Add Mod"); + if (ImGui.Button("Add")) + _lastAddEc = Ipc.AddMod.Subscriber(_pi).Invoke(_modDirectory); ImGui.SameLine(); - ImGui.TextUnformatted( _lastAddEc.ToString() ); + ImGui.TextUnformatted(_lastAddEc.ToString()); - DrawIntro( Ipc.DeleteMod.Label, "Delete Mod" ); - if( ImGui.Button( "Delete" ) ) - { - _lastDeleteEc = Ipc.DeleteMod.Subscriber( _pi ).Invoke( _modDirectory, _modName ); - } + DrawIntro(Ipc.DeleteMod.Label, "Delete Mod"); + if (ImGui.Button("Delete")) + _lastDeleteEc = Ipc.DeleteMod.Subscriber(_pi).Invoke(_modDirectory, _modName); ImGui.SameLine(); - ImGui.TextUnformatted( _lastDeleteEc.ToString() ); + ImGui.TextUnformatted(_lastDeleteEc.ToString()); - DrawIntro( Ipc.GetModPath.Label, "Current Path" ); - var (ec, path, def) = Ipc.GetModPath.Subscriber( _pi ).Invoke( _modDirectory, _modName ); - ImGui.TextUnformatted( $"{path} ({( def ? "Custom" : "Default" )}) [{ec}]" ); + DrawIntro(Ipc.GetModPath.Label, "Current Path"); + var (ec, path, def) = Ipc.GetModPath.Subscriber(_pi).Invoke(_modDirectory, _modName); + ImGui.TextUnformatted($"{path} ({(def ? "Custom" : "Default")}) [{ec}]"); - DrawIntro( Ipc.SetModPath.Label, "Set Path" ); - if( ImGui.Button( "Set" ) ) - { - _lastSetPathEc = Ipc.SetModPath.Subscriber( _pi ).Invoke( _modDirectory, _modName, _pathInput ); - } + DrawIntro(Ipc.SetModPath.Label, "Set Path"); + if (ImGui.Button("Set")) + _lastSetPathEc = Ipc.SetModPath.Subscriber(_pi).Invoke(_modDirectory, _modName, _pathInput); ImGui.SameLine(); - ImGui.TextUnformatted( _lastSetPathEc.ToString() ); + ImGui.TextUnformatted(_lastSetPathEc.ToString()); - DrawIntro( Ipc.ModDeleted.Label, "Last Mod Deleted" ); - if( _lastDeletedModTime > DateTimeOffset.UnixEpoch ) - { - ImGui.TextUnformatted( $"{_lastDeletedMod} at {_lastDeletedModTime}" ); - } + DrawIntro(Ipc.ModDeleted.Label, "Last Mod Deleted"); + if (_lastDeletedModTime > DateTimeOffset.UnixEpoch) + ImGui.TextUnformatted($"{_lastDeletedMod} at {_lastDeletedModTime}"); - DrawIntro( Ipc.ModAdded.Label, "Last Mod Added" ); - if( _lastAddedModTime > DateTimeOffset.UnixEpoch ) - { - ImGui.TextUnformatted( $"{_lastAddedMod} at {_lastAddedModTime}" ); - } + DrawIntro(Ipc.ModAdded.Label, "Last Mod Added"); + if (_lastAddedModTime > DateTimeOffset.UnixEpoch) + ImGui.TextUnformatted($"{_lastAddedMod} at {_lastAddedModTime}"); - DrawIntro( Ipc.ModMoved.Label, "Last Mod Moved" ); - if( _lastMovedModTime > DateTimeOffset.UnixEpoch ) - { - ImGui.TextUnformatted( $"{_lastMovedModFrom} -> {_lastMovedModTo} at {_lastMovedModTime}" ); - } + DrawIntro(Ipc.ModMoved.Label, "Last Mod Moved"); + if (_lastMovedModTime > DateTimeOffset.UnixEpoch) + ImGui.TextUnformatted($"{_lastMovedModFrom} -> {_lastMovedModTo} at {_lastMovedModTime}"); DrawModsPopup(); } private void DrawModsPopup() { - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var p = ImRaii.Popup( "Mods" ); - if( !p ) - { + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); + using var p = ImRaii.Popup("Mods"); + if (!p) return; - } - foreach( var (modDir, modName) in _mods ) - { - ImGui.TextUnformatted( $"{modDir}: {modName}" ); - } + foreach (var (modDir, modName) in _mods) + ImGui.TextUnformatted($"{modDir}: {modName}"); - if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) - { + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) ImGui.CloseCurrentPopup(); - } } } private class ModSettings { - private readonly DalamudPluginInterface _pi; - public readonly EventSubscriber< ModSettingChange, string, string, bool > SettingChanged; + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber SettingChanged; private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success; private ModSettingChange _lastSettingChangeType; @@ -1100,60 +982,57 @@ public class IpcTester : IDisposable private bool _lastSettingChangeInherited; private DateTimeOffset _lastSettingChange; - private string _settingsModDirectory = string.Empty; - private string _settingsModName = string.Empty; - private string _settingsCollection = string.Empty; - private bool _settingsAllowInheritance = true; - private bool _settingsInherit = false; - private bool _settingsEnabled = false; - private int _settingsPriority = 0; - private IDictionary< string, (IList< string >, GroupType) >? _availableSettings; - private IDictionary< string, IList< string > >? _currentSettings = null; + private string _settingsModDirectory = string.Empty; + private string _settingsModName = string.Empty; + private string _settingsCollection = string.Empty; + private bool _settingsAllowInheritance = true; + private bool _settingsInherit = false; + private bool _settingsEnabled = false; + private int _settingsPriority = 0; + private IDictionary, GroupType)>? _availableSettings; + private IDictionary>? _currentSettings = null; - public ModSettings( DalamudPluginInterface pi ) + public ModSettings(DalamudPluginInterface pi) { _pi = pi; - SettingChanged = Ipc.ModSettingChanged.Subscriber( pi, UpdateLastModSetting ); + SettingChanged = Ipc.ModSettingChanged.Subscriber(pi, UpdateLastModSetting); } public void Draw() { - using var _ = ImRaii.TreeNode( "Mod Settings" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Mod Settings"); + if (!_) return; - } - ImGui.InputTextWithHint( "##settingsDir", "Mod Directory Name...", ref _settingsModDirectory, 100 ); - ImGui.InputTextWithHint( "##settingsName", "Mod Name...", ref _settingsModName, 100 ); - ImGui.InputTextWithHint( "##settingsCollection", "Collection...", ref _settingsCollection, 100 ); - ImGui.Checkbox( "Allow Inheritance", ref _settingsAllowInheritance ); + ImGui.InputTextWithHint("##settingsDir", "Mod Directory Name...", ref _settingsModDirectory, 100); + ImGui.InputTextWithHint("##settingsName", "Mod Name...", ref _settingsModName, 100); + ImGui.InputTextWithHint("##settingsCollection", "Collection...", ref _settingsCollection, 100); + ImGui.Checkbox("Allow Inheritance", ref _settingsAllowInheritance); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( "Last Error", _lastSettingsError.ToString() ); - DrawIntro( Ipc.ModSettingChanged.Label, "Last Mod Setting Changed" ); - ImGui.TextUnformatted( _lastSettingChangeMod.Length > 0 - ? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{( _lastSettingChangeInherited ? " (Inherited)" : string.Empty )} at {_lastSettingChange}" - : "None" ); - DrawIntro( Ipc.GetAvailableModSettings.Label, "Get Available Settings" ); - if( ImGui.Button( "Get##Available" ) ) + DrawIntro("Last Error", _lastSettingsError.ToString()); + DrawIntro(Ipc.ModSettingChanged.Label, "Last Mod Setting Changed"); + ImGui.TextUnformatted(_lastSettingChangeMod.Length > 0 + ? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{(_lastSettingChangeInherited ? " (Inherited)" : string.Empty)} at {_lastSettingChange}" + : "None"); + DrawIntro(Ipc.GetAvailableModSettings.Label, "Get Available Settings"); + if (ImGui.Button("Get##Available")) { - _availableSettings = Ipc.GetAvailableModSettings.Subscriber( _pi ).Invoke( _settingsModDirectory, _settingsModName ); + _availableSettings = Ipc.GetAvailableModSettings.Subscriber(_pi).Invoke(_settingsModDirectory, _settingsModName); _lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; } - DrawIntro( Ipc.GetCurrentModSettings.Label, "Get Current Settings" ); - if( ImGui.Button( "Get##Current" ) ) + DrawIntro(Ipc.GetCurrentModSettings.Label, "Get Current Settings"); + if (ImGui.Button("Get##Current")) { - var ret = Ipc.GetCurrentModSettings.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsAllowInheritance ); + var ret = Ipc.GetCurrentModSettings.Subscriber(_pi) + .Invoke(_settingsCollection, _settingsModDirectory, _settingsModName, _settingsAllowInheritance); _lastSettingsError = ret.Item1; - if( ret.Item1 == PenumbraApiEc.Success ) + if (ret.Item1 == PenumbraApiEc.Success) { _settingsEnabled = ret.Item2?.Item1 ?? false; _settingsInherit = ret.Item2?.Item4 ?? false; @@ -1166,107 +1045,88 @@ public class IpcTester : IDisposable } } - DrawIntro( Ipc.TryInheritMod.Label, "Inherit Mod" ); - ImGui.Checkbox( "##inherit", ref _settingsInherit ); + DrawIntro(Ipc.TryInheritMod.Label, "Inherit Mod"); + ImGui.Checkbox("##inherit", ref _settingsInherit); ImGui.SameLine(); - if( ImGui.Button( "Set##Inherit" ) ) - { - _lastSettingsError = Ipc.TryInheritMod.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit ); - } + if (ImGui.Button("Set##Inherit")) + _lastSettingsError = Ipc.TryInheritMod.Subscriber(_pi) + .Invoke(_settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit); - DrawIntro( Ipc.TrySetMod.Label, "Set Enabled" ); - ImGui.Checkbox( "##enabled", ref _settingsEnabled ); + DrawIntro(Ipc.TrySetMod.Label, "Set Enabled"); + ImGui.Checkbox("##enabled", ref _settingsEnabled); ImGui.SameLine(); - if( ImGui.Button( "Set##Enabled" ) ) - { - _lastSettingsError = Ipc.TrySetMod.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsEnabled ); - } + if (ImGui.Button("Set##Enabled")) + _lastSettingsError = Ipc.TrySetMod.Subscriber(_pi) + .Invoke(_settingsCollection, _settingsModDirectory, _settingsModName, _settingsEnabled); - DrawIntro( Ipc.TrySetModPriority.Label, "Set Priority" ); - ImGui.SetNextItemWidth( 200 * UiHelpers.Scale ); - ImGui.DragInt( "##Priority", ref _settingsPriority ); + DrawIntro(Ipc.TrySetModPriority.Label, "Set Priority"); + ImGui.SetNextItemWidth(200 * UiHelpers.Scale); + ImGui.DragInt("##Priority", ref _settingsPriority); ImGui.SameLine(); - if( ImGui.Button( "Set##Priority" ) ) - { - _lastSettingsError = Ipc.TrySetModPriority.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority ); - } + if (ImGui.Button("Set##Priority")) + _lastSettingsError = Ipc.TrySetModPriority.Subscriber(_pi) + .Invoke(_settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority); - DrawIntro( Ipc.CopyModSettings.Label, "Copy Mod Settings" ); - if( ImGui.Button( "Copy Settings" ) ) - { - _lastSettingsError = Ipc.CopyModSettings.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName ); - } + DrawIntro(Ipc.CopyModSettings.Label, "Copy Mod Settings"); + if (ImGui.Button("Copy Settings")) + _lastSettingsError = Ipc.CopyModSettings.Subscriber(_pi).Invoke(_settingsCollection, _settingsModDirectory, _settingsModName); - ImGuiUtil.HoverTooltip( "Copy settings from Mod Directory Name to Mod Name (as directory) in collection." ); + ImGuiUtil.HoverTooltip("Copy settings from Mod Directory Name to Mod Name (as directory) in collection."); - DrawIntro( Ipc.TrySetModSetting.Label, "Set Setting(s)" ); - if( _availableSettings == null ) - { + DrawIntro(Ipc.TrySetModSetting.Label, "Set Setting(s)"); + if (_availableSettings == null) return; - } - foreach( var (group, (list, type)) in _availableSettings ) + foreach (var (group, (list, type)) in _availableSettings) { - using var id = ImRaii.PushId( group ); - var preview = list.Count > 0 ? list[ 0 ] : string.Empty; - IList< string > current; - if( _currentSettings != null && _currentSettings.TryGetValue( group, out current! ) && current.Count > 0 ) + using var id = ImRaii.PushId(group); + var preview = list.Count > 0 ? list[0] : string.Empty; + IList current; + if (_currentSettings != null && _currentSettings.TryGetValue(group, out current!) && current.Count > 0) { - preview = current[ 0 ]; + preview = current[0]; } else { - current = new List< string >(); - if( _currentSettings != null ) - { - _currentSettings[ group ] = current; - } + current = new List(); + if (_currentSettings != null) + _currentSettings[group] = current; } - ImGui.SetNextItemWidth( 200 * UiHelpers.Scale ); - using( var c = ImRaii.Combo( "##group", preview ) ) + ImGui.SetNextItemWidth(200 * UiHelpers.Scale); + using (var c = ImRaii.Combo("##group", preview)) { - if( c ) - { - foreach( var s in list ) + if (c) + foreach (var s in list) { - var contained = current.Contains( s ); - if( ImGui.Checkbox( s, ref contained ) ) + var contained = current.Contains(s); + if (ImGui.Checkbox(s, ref contained)) { - if( contained ) - { - current.Add( s ); - } + if (contained) + current.Add(s); else - { - current.Remove( s ); - } + current.Remove(s); } } - } } ImGui.SameLine(); - if( ImGui.Button( "Set##setting" ) ) + if (ImGui.Button("Set##setting")) { - if( type == GroupType.Single ) - { - _lastSettingsError = Ipc.TrySetModSetting.Subscriber( _pi ).Invoke( _settingsCollection, - _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[ 0 ] : string.Empty ); - } + if (type == GroupType.Single) + _lastSettingsError = Ipc.TrySetModSetting.Subscriber(_pi).Invoke(_settingsCollection, + _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[0] : string.Empty); else - { - _lastSettingsError = Ipc.TrySetModSettings.Subscriber( _pi ).Invoke( _settingsCollection, - _settingsModDirectory, _settingsModName, group, current.ToArray() ); - } + _lastSettingsError = Ipc.TrySetModSettings.Subscriber(_pi).Invoke(_settingsCollection, + _settingsModDirectory, _settingsModName, group, current.ToArray()); } ImGui.SameLine(); - ImGui.TextUnformatted( group ); + ImGui.TextUnformatted(group); } } - private void UpdateLastModSetting( ModSettingChange type, string collection, string mod, bool inherited ) + private void UpdateLastModSetting(ModSettingChange type, string collection, string mod, bool inherited) { _lastSettingChangeType = type; _lastSettingChangeCollection = collection; @@ -1278,10 +1138,14 @@ public class IpcTester : IDisposable private class Temporary { - public readonly DalamudPluginInterface _pi; + private readonly DalamudPluginInterface _pi; + private readonly Mod.Manager _modManager; - public Temporary( DalamudPluginInterface pi ) - => _pi = pi; + public Temporary(DalamudPluginInterface pi, Mod.Manager modManager) + { + _pi = pi; + _modManager = modManager; + } public string LastCreatedCollectionName = string.Empty; @@ -1297,182 +1161,152 @@ public class IpcTester : IDisposable public void Draw() { - using var _ = ImRaii.TreeNode( "Temporary" ); - if( !_ ) - { + using var _ = ImRaii.TreeNode("Temporary"); + if (!_) return; - } - ImGui.InputTextWithHint( "##tempCollection", "Collection Name...", ref _tempCollectionName, 128 ); - ImGui.InputTextWithHint( "##tempCollectionChar", "Collection Character...", ref _tempCharacterName, 32 ); - ImGui.InputInt( "##tempActorIndex", ref _tempActorIndex, 0, 0 ); - ImGui.InputTextWithHint( "##tempMod", "Temporary Mod Name...", ref _tempModName, 32 ); - ImGui.InputTextWithHint( "##tempGame", "Game Path...", ref _tempGamePath, 256 ); - ImGui.InputTextWithHint( "##tempFile", "File Path...", ref _tempFilePath, 256 ); - ImGui.InputTextWithHint( "##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256 ); - ImGui.Checkbox( "Force Character Collection Overwrite", ref _forceOverwrite ); + ImGui.InputTextWithHint("##tempCollection", "Collection Name...", ref _tempCollectionName, 128); + ImGui.InputTextWithHint("##tempCollectionChar", "Collection Character...", ref _tempCharacterName, 32); + ImGui.InputInt("##tempActorIndex", ref _tempActorIndex, 0, 0); + ImGui.InputTextWithHint("##tempMod", "Temporary Mod Name...", ref _tempModName, 32); + ImGui.InputTextWithHint("##tempGame", "Game Path...", ref _tempGamePath, 256); + ImGui.InputTextWithHint("##tempFile", "File Path...", ref _tempFilePath, 256); + ImGui.InputTextWithHint("##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256); + ImGui.Checkbox("Force Character Collection Overwrite", ref _forceOverwrite); - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { + using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); + if (!table) return; - } - DrawIntro( "Last Error", _lastTempError.ToString() ); - DrawIntro( "Last Created Collection", LastCreatedCollectionName ); - DrawIntro( Ipc.CreateTemporaryCollection.Label, "Create Temporary Collection" ); + DrawIntro("Last Error", _lastTempError.ToString()); + DrawIntro("Last Created Collection", LastCreatedCollectionName); + DrawIntro(Ipc.CreateTemporaryCollection.Label, "Create Temporary Collection"); #pragma warning disable 0612 - if( ImGui.Button( "Create##Collection" ) ) - { - ( _lastTempError, LastCreatedCollectionName ) = Ipc.CreateTemporaryCollection.Subscriber( _pi ).Invoke( _tempCollectionName, _tempCharacterName, _forceOverwrite ); - } + if (ImGui.Button("Create##Collection")) + (_lastTempError, LastCreatedCollectionName) = Ipc.CreateTemporaryCollection.Subscriber(_pi) + .Invoke(_tempCollectionName, _tempCharacterName, _forceOverwrite); - DrawIntro( Ipc.CreateNamedTemporaryCollection.Label, "Create Named Temporary Collection" ); - if( ImGui.Button( "Create##NamedCollection" ) ) - { - _lastTempError = Ipc.CreateNamedTemporaryCollection.Subscriber( _pi ).Invoke( _tempCollectionName ); - } + DrawIntro(Ipc.CreateNamedTemporaryCollection.Label, "Create Named Temporary Collection"); + if (ImGui.Button("Create##NamedCollection")) + _lastTempError = Ipc.CreateNamedTemporaryCollection.Subscriber(_pi).Invoke(_tempCollectionName); - DrawIntro( Ipc.RemoveTemporaryCollection.Label, "Remove Temporary Collection from Character" ); - if( ImGui.Button( "Delete##Collection" ) ) - { - _lastTempError = Ipc.RemoveTemporaryCollection.Subscriber( _pi ).Invoke( _tempCharacterName ); - } + DrawIntro(Ipc.RemoveTemporaryCollection.Label, "Remove Temporary Collection from Character"); + if (ImGui.Button("Delete##Collection")) + _lastTempError = Ipc.RemoveTemporaryCollection.Subscriber(_pi).Invoke(_tempCharacterName); #pragma warning restore 0612 - DrawIntro( Ipc.RemoveTemporaryCollectionByName.Label, "Remove Temporary Collection" ); - if( ImGui.Button( "Delete##NamedCollection" ) ) + DrawIntro(Ipc.RemoveTemporaryCollectionByName.Label, "Remove Temporary Collection"); + if (ImGui.Button("Delete##NamedCollection")) + _lastTempError = Ipc.RemoveTemporaryCollectionByName.Subscriber(_pi).Invoke(_tempCollectionName); + + DrawIntro(Ipc.AssignTemporaryCollection.Label, "Assign Temporary Collection"); + if (ImGui.Button("Assign##NamedCollection")) + _lastTempError = Ipc.AssignTemporaryCollection.Subscriber(_pi).Invoke(_tempCollectionName, _tempActorIndex, _forceOverwrite); + + DrawIntro(Ipc.AddTemporaryMod.Label, "Add Temporary Mod to specific Collection"); + if (ImGui.Button("Add##Mod")) + _lastTempError = Ipc.AddTemporaryMod.Subscriber(_pi).Invoke(_tempModName, _tempCollectionName, + new Dictionary { { _tempGamePath, _tempFilePath } }, + _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue); + + DrawIntro(Ipc.CreateTemporaryCollection.Label, "Copy Existing Collection"); + if (ImGuiUtil.DrawDisabledButton("Copy##Collection", Vector2.Zero, + "Copies the effective list from the collection named in Temporary Mod Name...", + !Penumbra.CollectionManager.ByName(_tempModName, out var copyCollection)) + && copyCollection is { HasCache: true }) { - _lastTempError = Ipc.RemoveTemporaryCollectionByName.Subscriber( _pi ).Invoke( _tempCollectionName ); + var files = copyCollection.ResolvedFiles.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value.Path.ToString()); + var manips = Functions.ToCompressedBase64(copyCollection.MetaCache?.Manipulations.ToArray() ?? Array.Empty(), + MetaManipulation.CurrentVersion); + _lastTempError = Ipc.AddTemporaryMod.Subscriber(_pi).Invoke(_tempModName, _tempCollectionName, files, manips, 999); } - DrawIntro( Ipc.AssignTemporaryCollection.Label, "Assign Temporary Collection" ); - if( ImGui.Button( "Assign##NamedCollection" ) ) - { - _lastTempError = Ipc.AssignTemporaryCollection.Subscriber( _pi ).Invoke( _tempCollectionName, _tempActorIndex, _forceOverwrite ); - } + DrawIntro(Ipc.AddTemporaryModAll.Label, "Add Temporary Mod to all Collections"); + if (ImGui.Button("Add##All")) + _lastTempError = Ipc.AddTemporaryModAll.Subscriber(_pi).Invoke(_tempModName, + new Dictionary { { _tempGamePath, _tempFilePath } }, + _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue); - DrawIntro( Ipc.AddTemporaryMod.Label, "Add Temporary Mod to specific Collection" ); - if( ImGui.Button( "Add##Mod" ) ) - { - _lastTempError = Ipc.AddTemporaryMod.Subscriber( _pi ).Invoke( _tempModName, _tempCollectionName, - new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, - _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue ); - } + DrawIntro(Ipc.RemoveTemporaryMod.Label, "Remove Temporary Mod from specific Collection"); + if (ImGui.Button("Remove##Mod")) + _lastTempError = Ipc.RemoveTemporaryMod.Subscriber(_pi).Invoke(_tempModName, _tempCollectionName, int.MaxValue); - DrawIntro( Ipc.CreateTemporaryCollection.Label, "Copy Existing Collection" ); - if( ImGuiUtil.DrawDisabledButton( "Copy##Collection", Vector2.Zero, "Copies the effective list from the collection named in Temporary Mod Name...", - !Penumbra.CollectionManager.ByName( _tempModName, out var copyCollection ) ) - && copyCollection is { HasCache: true } ) - { - var files = copyCollection.ResolvedFiles.ToDictionary( kvp => kvp.Key.ToString(), kvp => kvp.Value.Path.ToString() ); - var manips = Functions.ToCompressedBase64( copyCollection.MetaCache?.Manipulations.ToArray() ?? Array.Empty< MetaManipulation >(), - MetaManipulation.CurrentVersion ); - _lastTempError = Ipc.AddTemporaryMod.Subscriber( _pi ).Invoke( _tempModName, _tempCollectionName, files, manips, 999 ); - } - - DrawIntro( Ipc.AddTemporaryModAll.Label, "Add Temporary Mod to all Collections" ); - if( ImGui.Button( "Add##All" ) ) - { - _lastTempError = Ipc.AddTemporaryModAll.Subscriber( _pi ).Invoke( _tempModName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, - _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue ); - } - - DrawIntro( Ipc.RemoveTemporaryMod.Label, "Remove Temporary Mod from specific Collection" ); - if( ImGui.Button( "Remove##Mod" ) ) - { - _lastTempError = Ipc.RemoveTemporaryMod.Subscriber( _pi ).Invoke( _tempModName, _tempCollectionName, int.MaxValue ); - } - - DrawIntro( Ipc.RemoveTemporaryModAll.Label, "Remove Temporary Mod from all Collections" ); - if( ImGui.Button( "Remove##ModAll" ) ) - { - _lastTempError = Ipc.RemoveTemporaryModAll.Subscriber( _pi ).Invoke( _tempModName, int.MaxValue ); - } + DrawIntro(Ipc.RemoveTemporaryModAll.Label, "Remove Temporary Mod from all Collections"); + if (ImGui.Button("Remove##ModAll")) + _lastTempError = Ipc.RemoveTemporaryModAll.Subscriber(_pi).Invoke(_tempModName, int.MaxValue); } public void DrawCollections() { - using var collTree = ImRaii.TreeNode( "Collections##TempCollections" ); - if( !collTree ) - { + using var collTree = ImRaii.TreeNode("Collections##TempCollections"); + if (!collTree) return; - } - using var table = ImRaii.Table( "##collTree", 5 ); - if( !table ) - { + using var table = ImRaii.Table("##collTree", 5); + if (!table) return; - } - foreach( var collection in Penumbra.TempCollections.Values ) + foreach (var collection in Penumbra.TempCollections.Values) { ImGui.TableNextColumn(); - var character = Penumbra.TempCollections.Collections.Where( p => p.Collection == collection ).Select( p => p.DisplayName ).FirstOrDefault() ?? "Unknown"; - if( ImGui.Button( $"Save##{collection.Name}" ) ) - { - Mod.TemporaryMod.SaveTempCollection( collection, character ); - } + var character = Penumbra.TempCollections.Collections.Where(p => p.Collection == collection).Select(p => p.DisplayName) + .FirstOrDefault() + ?? "Unknown"; + if (ImGui.Button($"Save##{collection.Name}")) + Mod.TemporaryMod.SaveTempCollection(_modManager, collection, character); - ImGuiUtil.DrawTableColumn( collection.Name ); - ImGuiUtil.DrawTableColumn( collection.ResolvedFiles.Count.ToString() ); - ImGuiUtil.DrawTableColumn( collection.MetaCache?.Count.ToString() ?? "0" ); - ImGuiUtil.DrawTableColumn( string.Join( ", ", Penumbra.TempCollections.Collections.Where( p => p.Collection == collection ).Select( c => c.DisplayName ) ) ); + ImGuiUtil.DrawTableColumn(collection.Name); + ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString()); + ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0"); + ImGuiUtil.DrawTableColumn(string.Join(", ", + Penumbra.TempCollections.Collections.Where(p => p.Collection == collection).Select(c => c.DisplayName))); } } public void DrawMods() { - using var modTree = ImRaii.TreeNode( "Mods##TempMods" ); - if( !modTree ) - { + using var modTree = ImRaii.TreeNode("Mods##TempMods"); + if (!modTree) return; - } - using var table = ImRaii.Table( "##modTree", 5 ); + using var table = ImRaii.Table("##modTree", 5); - void PrintList( string collectionName, IReadOnlyList< Mod.TemporaryMod > list ) + void PrintList(string collectionName, IReadOnlyList list) { - foreach( var mod in list ) + foreach (var mod in list) { ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Name ); + ImGui.TextUnformatted(mod.Name); ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Priority.ToString() ); + ImGui.TextUnformatted(mod.Priority.ToString()); ImGui.TableNextColumn(); - ImGui.TextUnformatted( collectionName ); + ImGui.TextUnformatted(collectionName); ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Default.Files.Count.ToString() ); - if( ImGui.IsItemHovered() ) + ImGui.TextUnformatted(mod.Default.Files.Count.ToString()); + if (ImGui.IsItemHovered()) { using var tt = ImRaii.Tooltip(); - foreach( var (path, file) in mod.Default.Files ) - { - ImGui.TextUnformatted( $"{path} -> {file}" ); - } + foreach (var (path, file) in mod.Default.Files) + ImGui.TextUnformatted($"{path} -> {file}"); } ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.TotalManipulations.ToString() ); - if( ImGui.IsItemHovered() ) + ImGui.TextUnformatted(mod.TotalManipulations.ToString()); + if (ImGui.IsItemHovered()) { using var tt = ImRaii.Tooltip(); - foreach( var manip in mod.Default.Manipulations ) - { - ImGui.TextUnformatted( manip.ToString() ); - } + foreach (var manip in mod.Default.Manipulations) + ImGui.TextUnformatted(manip.ToString()); } } } - if( table ) + if (table) { - PrintList( "All", Penumbra.TempMods.ModsForAllCollections ); - foreach( var (collection, list) in Penumbra.TempMods.Mods ) - { - PrintList( collection.Name, list ); - } + PrintList("All", Penumbra.TempMods.ModsForAllCollections); + foreach (var (collection, list) in Penumbra.TempMods.Mods) + PrintList(collection.Name, list); } } } -} \ No newline at end of file +} diff --git a/Penumbra/Api/PenumbraIpcProviders.cs b/Penumbra/Api/PenumbraIpcProviders.cs index 90d316d0..50768ddf 100644 --- a/Penumbra/Api/PenumbraIpcProviders.cs +++ b/Penumbra/Api/PenumbraIpcProviders.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Penumbra.Api.Enums; using Penumbra.Api.Helpers; +using Penumbra.Mods; namespace Penumbra.Api; @@ -111,7 +112,7 @@ public class PenumbraIpcProviders : IDisposable internal readonly FuncProvider< string, int, PenumbraApiEc > RemoveTemporaryModAll; internal readonly FuncProvider< string, string, int, PenumbraApiEc > RemoveTemporaryMod; - public PenumbraIpcProviders( DalamudPluginInterface pi, IPenumbraApi api ) + public PenumbraIpcProviders( DalamudPluginInterface pi, IPenumbraApi api, Mod.Manager modManager ) { Api = api; @@ -219,7 +220,7 @@ public class PenumbraIpcProviders : IDisposable RemoveTemporaryModAll = Ipc.RemoveTemporaryModAll.Provider( pi, Api.RemoveTemporaryModAll ); RemoveTemporaryMod = Ipc.RemoveTemporaryMod.Provider( pi, Api.RemoveTemporaryMod ); - Tester = new IpcTester( pi, this ); + Tester = new IpcTester( pi, this, modManager ); Initialized.Invoke(); } diff --git a/Penumbra/Collections/CollectionManager.Active.cs b/Penumbra/Collections/CollectionManager.Active.cs index 61a37961..1d571468 100644 --- a/Penumbra/Collections/CollectionManager.Active.cs +++ b/Penumbra/Collections/CollectionManager.Active.cs @@ -17,7 +17,7 @@ namespace Penumbra.Collections; public partial class ModCollection { - public sealed partial class Manager : ISaveable + public sealed partial class Manager : ISavable { public const int Version = 1; diff --git a/Penumbra/Collections/ModCollection.File.cs b/Penumbra/Collections/ModCollection.File.cs index 6d13deb1..c2516519 100644 --- a/Penumbra/Collections/ModCollection.File.cs +++ b/Penumbra/Collections/ModCollection.File.cs @@ -11,7 +11,7 @@ using Penumbra.Util; namespace Penumbra.Collections; // File operations like saving, loading and deleting for a collection. -public partial class ModCollection : ISaveable +public partial class ModCollection : ISavable { // Since inheritances depend on other collections existing, // we return them as a list to be applied after reading all collections. diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index b1468c2c..ff2a5e64 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -9,23 +9,21 @@ using OtterGui.Classes; using OtterGui.Filesystem; using OtterGui.Widgets; using Penumbra.GameData.Enums; -using Penumbra.Import.Structs; +using Penumbra.Import.Structs; using Penumbra.Mods; using Penumbra.Services; using Penumbra.UI; using Penumbra.UI.Classes; +using Penumbra.Util; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Penumbra; [Serializable] -public class Configuration : IPluginConfiguration +public class Configuration : IPluginConfiguration, ISavable { [JsonIgnore] - private readonly string _fileName; - - [JsonIgnore] - private readonly FrameworkManager _framework; + private readonly SaveService _saveService; public int Version { get; set; } = Constants.CurrentVersion; @@ -101,14 +99,13 @@ public class Configuration : IPluginConfiguration /// Load the current configuration. /// Includes adding new colors and migrating from old versions. /// - public Configuration(FilenameService fileNames, ConfigMigrationService migrator, FrameworkManager framework) + public Configuration(FilenameService fileNames, ConfigMigrationService migrator, SaveService saveService) { - _fileName = fileNames.ConfigFile; - _framework = framework; - Load(migrator); + _saveService = saveService; + Load(fileNames, migrator); } - public void Load(ConfigMigrationService migrator) + public void Load(FilenameService fileNames, ConfigMigrationService migrator) { static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs) { @@ -117,9 +114,9 @@ public class Configuration : IPluginConfiguration errorArgs.ErrorContext.Handled = true; } - if (File.Exists(_fileName)) + if (File.Exists(fileNames.ConfigFile)) { - var text = File.ReadAllText(_fileName); + var text = File.ReadAllText(fileNames.ConfigFile); JsonConvert.PopulateObject(text, this, new JsonSerializerSettings { Error = HandleDeserializationError, @@ -130,21 +127,8 @@ public class Configuration : IPluginConfiguration } /// Save the current configuration. - private void SaveConfiguration() - { - try - { - var text = JsonConvert.SerializeObject(this, Formatting.Indented); - File.WriteAllText(_fileName, text); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not save plugin configuration:\n{e}"); - } - } - public void Save() - => _framework.RegisterDelayed(nameof(SaveConfiguration), SaveConfiguration); + => _saveService.QueueSave(this); /// Contains some default values or boundaries for config values. public static class Constants @@ -192,4 +176,14 @@ public class Configuration : IPluginConfiguration return mode; } } + + public string ToFilename(FilenameService fileNames) + => fileNames.ConfigFile; + + public void Save(StreamWriter writer) + { + using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }; + var serializer = new JsonSerializer { Formatting = Formatting.Indented }; + serializer.Serialize(jWriter, this); + } } diff --git a/Penumbra/Import/TexToolsImport.cs b/Penumbra/Import/TexToolsImport.cs index 548cf90a..974c8b2e 100644 --- a/Penumbra/Import/TexToolsImport.cs +++ b/Penumbra/Import/TexToolsImport.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; +using Penumbra.Api; using Penumbra.Import.Structs; using Penumbra.Mods; using FileMode = System.IO.FileMode; @@ -33,22 +34,19 @@ public partial class TexToolsImporter : IDisposable public ImporterState State { get; private set; } public readonly List< (FileInfo File, DirectoryInfo? Mod, Exception? Error) > ExtractedMods; - public TexToolsImporter( DirectoryInfo baseDirectory, ICollection< FileInfo > files, - Action< FileInfo, DirectoryInfo?, Exception? > handler, Configuration config, ModEditor editor) - : this( baseDirectory, files.Count, files, handler, config, editor) - { } - private readonly Configuration _config; - private readonly ModEditor _editor; + private readonly ModEditor _editor; + private readonly Mod.Manager _modManager; public TexToolsImporter( DirectoryInfo baseDirectory, int count, IEnumerable< FileInfo > modPackFiles, - Action< FileInfo, DirectoryInfo?, Exception? > handler, Configuration config, ModEditor editor) + Action< FileInfo, DirectoryInfo?, Exception? > handler, Configuration config, ModEditor editor, Mod.Manager modManager) { _baseDirectory = baseDirectory; _tmpFile = Path.Combine( _baseDirectory.FullName, TempFileName ); _modPackFiles = modPackFiles; _config = config; - _editor = editor; + _editor = editor; + _modManager = modManager; _modPackCount = count; ExtractedMods = new List< (FileInfo, DirectoryInfo?, Exception?) >( count ); _token = _cancellation.Token; diff --git a/Penumbra/Import/TexToolsImporter.ModPack.cs b/Penumbra/Import/TexToolsImporter.ModPack.cs index fd141ef3..a8cb6608 100644 --- a/Penumbra/Import/TexToolsImporter.ModPack.cs +++ b/Penumbra/Import/TexToolsImporter.ModPack.cs @@ -35,7 +35,7 @@ public partial class TexToolsImporter _currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) ); // Create a new ModMeta from the TTMP mod list info - Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, DefaultTexToolsData.Author, DefaultTexToolsData.Description, null, null ); + _modManager.DataEditor.CreateMeta( _currentModDirectory, _currentModName, DefaultTexToolsData.Author, DefaultTexToolsData.Description, null, null ); // Open the mod data file from the mod pack as a SqPackStream _streamDisposer = GetSqPackStreamStream( extractedModPack, "TTMPD.mpd" ); @@ -90,7 +90,7 @@ public partial class TexToolsImporter Penumbra.Log.Information( " -> Importing Simple V2 ModPack" ); _currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, _currentModName ); - Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, modList.Author, string.IsNullOrEmpty( modList.Description ) + _modManager.DataEditor.CreateMeta( _currentModDirectory, _currentModName, modList.Author, string.IsNullOrEmpty( modList.Description ) ? "Mod imported from TexTools mod pack" : modList.Description, modList.Version, modList.Url ); @@ -135,7 +135,7 @@ public partial class TexToolsImporter _currentModName = modList.Name; _currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, _currentModName ); - Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, modList.Author, modList.Description, modList.Version, modList.Url ); + _modManager.DataEditor.CreateMeta( _currentModDirectory, _currentModName, modList.Author, modList.Description, modList.Version, modList.Url ); if( _currentNumOptions == 0 ) { diff --git a/Penumbra/Mods/Editor/DuplicateManager.cs b/Penumbra/Mods/Editor/DuplicateManager.cs index 44197193..8f480851 100644 --- a/Penumbra/Mods/Editor/DuplicateManager.cs +++ b/Penumbra/Mods/Editor/DuplicateManager.cs @@ -243,7 +243,7 @@ public class DuplicateManager try { var mod = new Mod(modDirectory); - mod.Reload(true, out _); + mod.Reload(_modManager, true, out _); Finished = false; _files.UpdateAll(mod, mod.Default); diff --git a/Penumbra/Mods/Editor/ModFileEditor.cs b/Penumbra/Mods/Editor/ModFileEditor.cs index 2e46314c..5215973c 100644 --- a/Penumbra/Mods/Editor/ModFileEditor.cs +++ b/Penumbra/Mods/Editor/ModFileEditor.cs @@ -154,7 +154,7 @@ public class ModFileEditor if (deletions <= 0) return; - mod.Reload(false, out _); + mod.Reload(_modManager, false, out _); _files.UpdateAll(mod, option); } diff --git a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs index 4b93bd24..65e3e10f 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs @@ -8,20 +8,20 @@ public partial class Mod { public partial class Manager { - public delegate void ModPathChangeDelegate( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, - DirectoryInfo? newDirectory ); + public delegate void ModPathChangeDelegate(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, + DirectoryInfo? newDirectory); public event ModPathChangeDelegate ModPathChanged; // Rename/Move a mod directory. // Updates all collection settings and sort order settings. - public void MoveModDirectory( int idx, string newName ) + public void MoveModDirectory(int idx, string newName) { - var mod = this[ idx ]; + var mod = this[idx]; var oldName = mod.Name; var oldDirectory = mod.ModPath; - switch( NewDirectoryValid( oldDirectory.Name, newName, out var dir ) ) + switch (NewDirectoryValid(oldDirectory.Name, newName, out var dir)) { case NewDirectoryState.NonExisting: // Nothing to do @@ -29,11 +29,11 @@ public partial class Mod case NewDirectoryState.ExistsEmpty: try { - Directory.Delete( dir!.FullName ); + Directory.Delete(dir!.FullName); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Error( $"Could not delete empty directory {dir!.FullName} to move {mod.Name} to it:\n{e}" ); + Penumbra.Log.Error($"Could not delete empty directory {dir!.FullName} to move {mod.Name} to it:\n{e}"); return; } @@ -50,105 +50,97 @@ public partial class Mod try { - Directory.Move( oldDirectory.FullName, dir!.FullName ); + Directory.Move(oldDirectory.FullName, dir!.FullName); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Error( $"Could not move {mod.Name} from {oldDirectory.Name} to {dir!.Name}:\n{e}" ); + Penumbra.Log.Error($"Could not move {mod.Name} from {oldDirectory.Name} to {dir!.Name}:\n{e}"); return; } - MoveDataFile( oldDirectory, dir ); - new ModBackup( this, mod ).Move( null, dir.Name ); + DataEditor.MoveDataFile(oldDirectory, dir); + new ModBackup(this, mod).Move(null, dir.Name); dir.Refresh(); mod.ModPath = dir; - if( !mod.Reload( false, out var metaChange ) ) + if (!mod.Reload(this, false, out var metaChange)) { - Penumbra.Log.Error( $"Error reloading moved mod {mod.Name}." ); + Penumbra.Log.Error($"Error reloading moved mod {mod.Name}."); return; } - ModPathChanged.Invoke( ModPathChangeType.Moved, mod, oldDirectory, dir ); - if( metaChange != ModDataChangeType.None ) - { - ModDataChanged?.Invoke( metaChange, mod, oldName ); - } + ModPathChanged.Invoke(ModPathChangeType.Moved, mod, oldDirectory, dir); + if (metaChange != ModDataChangeType.None) + _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); } - // Reload a mod without changing its base directory. - // If the base directory does not exist anymore, the mod will be deleted. - public void ReloadMod( int idx ) + /// + /// Reload a mod without changing its base directory. + /// If the base directory does not exist anymore, the mod will be deleted. + /// + public void ReloadMod(int idx) { - var mod = this[ idx ]; + var mod = this[idx]; var oldName = mod.Name; - ModPathChanged.Invoke( ModPathChangeType.StartingReload, mod, mod.ModPath, mod.ModPath ); - if( !mod.Reload( true, out var metaChange ) ) + ModPathChanged.Invoke(ModPathChangeType.StartingReload, mod, mod.ModPath, mod.ModPath); + if (!mod.Reload(this, true, out var metaChange)) { - Penumbra.Log.Warning( mod.Name.Length == 0 + Penumbra.Log.Warning(mod.Name.Length == 0 ? $"Reloading mod {oldName} has failed, new name is empty. Deleting instead." - : $"Reloading mod {oldName} failed, {mod.ModPath.FullName} does not exist anymore or it ha. Deleting instead." ); + : $"Reloading mod {oldName} failed, {mod.ModPath.FullName} does not exist anymore or it ha. Deleting instead."); - DeleteMod( idx ); + DeleteMod(idx); return; } - ModPathChanged.Invoke( ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath ); - if( metaChange != ModDataChangeType.None ) - { - ModDataChanged?.Invoke( metaChange, mod, oldName ); - } + ModPathChanged.Invoke(ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath); + if (metaChange != ModDataChangeType.None) + _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); } - // Delete a mod by its index. The event is invoked before the mod is removed from the list. - // Deletes from filesystem as well as from internal data. - // Updates indices of later mods. - public void DeleteMod( int idx ) + /// + /// Delete a mod by its index. The event is invoked before the mod is removed from the list. + /// Deletes from filesystem as well as from internal data. + /// Updates indices of later mods. + /// + public void DeleteMod(int idx) { - var mod = this[ idx ]; - if( Directory.Exists( mod.ModPath.FullName ) ) - { + var mod = this[idx]; + if (Directory.Exists(mod.ModPath.FullName)) try { - Directory.Delete( mod.ModPath.FullName, true ); - Penumbra.Log.Debug( $"Deleted directory {mod.ModPath.FullName} for {mod.Name}." ); + Directory.Delete(mod.ModPath.FullName, true); + Penumbra.Log.Debug($"Deleted directory {mod.ModPath.FullName} for {mod.Name}."); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Error( $"Could not delete the mod {mod.ModPath.Name}:\n{e}" ); + Penumbra.Log.Error($"Could not delete the mod {mod.ModPath.Name}:\n{e}"); } - } - ModPathChanged.Invoke( ModPathChangeType.Deleted, mod, mod.ModPath, null ); - _mods.RemoveAt( idx ); - foreach( var remainingMod in _mods.Skip( idx ) ) - { + ModPathChanged.Invoke(ModPathChangeType.Deleted, mod, mod.ModPath, null); + _mods.RemoveAt(idx); + foreach (var remainingMod in _mods.Skip(idx)) --remainingMod.Index; - } - Penumbra.Log.Debug( $"Deleted mod {mod.Name}." ); + Penumbra.Log.Debug($"Deleted mod {mod.Name}."); } - // Load a new mod and add it to the manager if successful. - public void AddMod( DirectoryInfo modFolder ) + /// Load a new mod and add it to the manager if successful. + public void AddMod(DirectoryInfo modFolder) { - if( _mods.Any( m => m.ModPath.Name == modFolder.Name ) ) - { + if (_mods.Any(m => m.ModPath.Name == modFolder.Name)) return; - } - Creator.SplitMultiGroups( modFolder ); - var mod = LoadMod( modFolder, true ); - if( mod == null ) - { + Creator.SplitMultiGroups(modFolder); + var mod = LoadMod(this, modFolder, true); + if (mod == null) return; - } mod.Index = _mods.Count; - _mods.Add( mod ); - ModPathChanged.Invoke( ModPathChangeType.Added, mod, null, mod.ModPath ); - Penumbra.Log.Debug( $"Added new mod {mod.Name} from {modFolder.FullName}." ); + _mods.Add(mod); + ModPathChanged.Invoke(ModPathChangeType.Added, mod, null, mod.ModPath); + Penumbra.Log.Debug($"Added new mod {mod.Name} from {modFolder.FullName}."); } public enum NewDirectoryState @@ -162,66 +154,52 @@ public partial class Mod Empty, } - // Return the state of the new potential name of a directory. - public NewDirectoryState NewDirectoryValid( string oldName, string newName, out DirectoryInfo? directory ) + /// Return the state of the new potential name of a directory. + public NewDirectoryState NewDirectoryValid(string oldName, string newName, out DirectoryInfo? directory) { directory = null; - if( newName.Length == 0 ) - { + if (newName.Length == 0) return NewDirectoryState.Empty; - } - if( oldName == newName ) - { + if (oldName == newName) return NewDirectoryState.Identical; - } - var fixedNewName = Creator.ReplaceBadXivSymbols( newName ); - if( fixedNewName != newName ) - { + var fixedNewName = Creator.ReplaceBadXivSymbols(newName); + if (fixedNewName != newName) return NewDirectoryState.ContainsInvalidSymbols; - } - directory = new DirectoryInfo( Path.Combine( BasePath.FullName, fixedNewName ) ); - if( File.Exists( directory.FullName ) ) - { + directory = new DirectoryInfo(Path.Combine(BasePath.FullName, fixedNewName)); + if (File.Exists(directory.FullName)) return NewDirectoryState.ExistsAsFile; - } - if( !Directory.Exists( directory.FullName ) ) - { + if (!Directory.Exists(directory.FullName)) return NewDirectoryState.NonExisting; - } - if( directory.EnumerateFileSystemInfos().Any() ) - { + if (directory.EnumerateFileSystemInfos().Any()) return NewDirectoryState.ExistsNonEmpty; - } return NewDirectoryState.ExistsEmpty; } - // Add new mods to NewMods and remove deleted mods from NewMods. - private void OnModPathChange( ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, - DirectoryInfo? newDirectory ) + /// Add new mods to NewMods and remove deleted mods from NewMods. + private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, + DirectoryInfo? newDirectory) { - switch( type ) + switch (type) { case ModPathChangeType.Added: - NewMods.Add( mod ); + NewMods.Add(mod); break; case ModPathChangeType.Deleted: - NewMods.Remove( mod ); + NewMods.Remove(mod); break; case ModPathChangeType.Moved: - if( oldDirectory != null && newDirectory != null ) - { - MoveDataFile( oldDirectory, newDirectory ); - } + if (oldDirectory != null && newDirectory != null) + DataEditor.MoveDataFile(oldDirectory, newDirectory); break; } } } -} \ No newline at end of file +} diff --git a/Penumbra/Mods/Manager/Mod.Manager.Local.cs b/Penumbra/Mods/Manager/Mod.Manager.Local.cs index 9b2fc839..f838677f 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Local.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Local.cs @@ -7,67 +7,6 @@ public sealed partial class Mod { public partial class Manager { - public void ChangeModFavorite( Index idx, bool state ) - { - var mod = this[ idx ]; - if( mod.Favorite != state ) - { - mod.Favorite = state; - mod.SaveLocalData(); - ModDataChanged?.Invoke( ModDataChangeType.Favorite, mod, null ); - } - } - - public void ChangeModNote( Index idx, string newNote ) - { - var mod = this[ idx ]; - if( mod.Note != newNote ) - { - mod.Note = newNote; - mod.SaveLocalData(); - ModDataChanged?.Invoke( ModDataChangeType.Favorite, mod, null ); - } - } - - - private void ChangeTag( Index idx, int tagIdx, string newTag, bool local ) - { - var mod = this[ idx ]; - var which = local ? mod.LocalTags : mod.ModTags; - if( tagIdx < 0 || tagIdx > which.Count ) - { - return; - } - - ModDataChangeType flags = 0; - if( tagIdx == which.Count ) - { - flags = mod.UpdateTags( local ? null : which.Append( newTag ), local ? which.Append( newTag ) : null ); - } - else - { - var tmp = which.ToArray(); - tmp[ tagIdx ] = newTag; - flags = mod.UpdateTags( local ? null : tmp, local ? tmp : null ); - } - - if( flags.HasFlag( ModDataChangeType.ModTags ) ) - { - mod.SaveMeta(); - } - - if( flags.HasFlag( ModDataChangeType.LocalTags ) ) - { - mod.SaveLocalData(); - } - - if( flags != 0 ) - { - ModDataChanged?.Invoke( flags, mod, null ); - } - } - - public void ChangeLocalTag( Index idx, int tagIdx, string newTag ) - => ChangeTag( idx, tagIdx, newTag, true ); + } } \ No newline at end of file diff --git a/Penumbra/Mods/Manager/Mod.Manager.Meta.cs b/Penumbra/Mods/Manager/Mod.Manager.Meta.cs deleted file mode 100644 index 9c4d48ee..00000000 --- a/Penumbra/Mods/Manager/Mod.Manager.Meta.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; - -namespace Penumbra.Mods; - -public sealed partial class Mod -{ - public partial class Manager - { - public delegate void ModDataChangeDelegate( ModDataChangeType type, Mod mod, string? oldName ); - public event ModDataChangeDelegate? ModDataChanged; - - public void ChangeModName( Index idx, string newName ) - { - var mod = this[ idx ]; - if( mod.Name.Text != newName ) - { - var oldName = mod.Name; - mod.Name = newName; - mod.SaveMeta(); - ModDataChanged?.Invoke( ModDataChangeType.Name, mod, oldName.Text ); - } - } - - public void ChangeModAuthor( Index idx, string newAuthor ) - { - var mod = this[ idx ]; - if( mod.Author != newAuthor ) - { - mod.Author = newAuthor; - mod.SaveMeta(); - ModDataChanged?.Invoke( ModDataChangeType.Author, mod, null ); - } - } - - public void ChangeModDescription( Index idx, string newDescription ) - { - var mod = this[ idx ]; - if( mod.Description != newDescription ) - { - mod.Description = newDescription; - mod.SaveMeta(); - ModDataChanged?.Invoke( ModDataChangeType.Description, mod, null ); - } - } - - public void ChangeModVersion( Index idx, string newVersion ) - { - var mod = this[ idx ]; - if( mod.Version != newVersion ) - { - mod.Version = newVersion; - mod.SaveMeta(); - ModDataChanged?.Invoke( ModDataChangeType.Version, mod, null ); - } - } - - public void ChangeModWebsite( Index idx, string newWebsite ) - { - var mod = this[ idx ]; - if( mod.Website != newWebsite ) - { - mod.Website = newWebsite; - mod.SaveMeta(); - ModDataChanged?.Invoke( ModDataChangeType.Website, mod, null ); - } - } - - public void ChangeModTag( Index idx, int tagIdx, string newTag ) - => ChangeTag( idx, tagIdx, newTag, false ); - } -} \ No newline at end of file diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index fbc30e64..a9b11b26 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -305,18 +305,18 @@ public sealed partial class Mod public 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) + if (path.Length != 0 + && !mod.Groups.Any(o => !ReferenceEquals(o, group) && string.Equals(o.Name.RemoveInvalidPathSymbols(), path, StringComparison.OrdinalIgnoreCase))) - { - if (message) - _chat.NotificationMessage($"Could not name option {newName} because option with same filename {path} already exists.", - "Warning", NotificationType.Warning); + return true; - return false; - } + if (message) + Penumbra.ChatService.NotificationMessage( + $"Could not name option {newName} because option with same filename {path} already exists.", + "Warning", NotificationType.Warning); + + return false; - return true; } private static SubMod GetSubMod(Mod mod, int groupIdx, int optionIdx) diff --git a/Penumbra/Mods/Manager/Mod.Manager.Root.cs b/Penumbra/Mods/Manager/Mod.Manager.Root.cs index bd7fb0fd..79dd780b 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Root.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Root.cs @@ -97,7 +97,7 @@ public sealed partial class Mod var queue = new ConcurrentQueue< Mod >(); Parallel.ForEach( BasePath.EnumerateDirectories(), options, dir => { - var mod = LoadMod( dir, false ); + var mod = LoadMod( this, dir, false ); if( mod != null ) { queue.Enqueue( mod ); diff --git a/Penumbra/Mods/Manager/Mod.Manager.cs b/Penumbra/Mods/Manager/Mod.Manager.cs index 95f40592..0e47f3ae 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Penumbra.Services; using Penumbra.Util; namespace Penumbra.Mods; @@ -36,14 +37,16 @@ public sealed partial class Mod IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private readonly Configuration _config; - private readonly ChatService _chat; + private readonly Configuration _config; + private readonly CommunicatorService _communicator; + public readonly ModDataEditor DataEditor; - public Manager(StartTracker time, Configuration config, ChatService chat) + public Manager(StartTracker time, Configuration config, CommunicatorService communicator, ModDataEditor dataEditor) { using var timer = time.Measure(StartTimeType.Mods); _config = config; - _chat = chat; + _communicator = communicator; + DataEditor = dataEditor; ModDirectoryChanged += OnModDirectoryChange; SetBaseDirectory(config.ModDirectory, true); UpdateExportDirectory(_config.ExportDirectory, false); diff --git a/Penumbra/Mods/Manager/ModDataChangeType.cs b/Penumbra/Mods/Manager/ModDataChangeType.cs new file mode 100644 index 00000000..eccf83cb --- /dev/null +++ b/Penumbra/Mods/Manager/ModDataChangeType.cs @@ -0,0 +1,21 @@ +using System; + +namespace Penumbra.Mods; + +[Flags] +public enum ModDataChangeType : ushort +{ + None = 0x0000, + Name = 0x0001, + Author = 0x0002, + Description = 0x0004, + Version = 0x0008, + Website = 0x0010, + Deletion = 0x0020, + Migration = 0x0040, + ModTags = 0x0080, + ImportDate = 0x0100, + Favorite = 0x0200, + LocalTags = 0x0400, + Note = 0x0800, +} diff --git a/Penumbra/Mods/Manager/ModDataEditor.cs b/Penumbra/Mods/Manager/ModDataEditor.cs new file mode 100644 index 00000000..7b74f83a --- /dev/null +++ b/Penumbra/Mods/Manager/ModDataEditor.cs @@ -0,0 +1,362 @@ +using System; +using System.IO; +using System.Linq; +using Dalamud.Utility; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OtterGui.Classes; +using Penumbra.Services; +using Penumbra.Util; + +namespace Penumbra.Mods; + +public class ModDataEditor +{ + private readonly FilenameService _filenameService; + private readonly SaveService _saveService; + private readonly CommunicatorService _communicatorService; + + public ModDataEditor(FilenameService filenameService, SaveService saveService, CommunicatorService communicatorService) + { + _filenameService = filenameService; + _saveService = saveService; + _communicatorService = communicatorService; + } + + public string MetaFile(Mod mod) + => _filenameService.ModMetaPath(mod); + + public string DataFile(Mod mod) + => _filenameService.LocalDataFile(mod); + + /// Create the file containing the meta information about a mod from scratch. + public void CreateMeta(DirectoryInfo directory, string? name, string? author, string? description, string? version, + string? website) + { + var mod = new Mod(directory); + mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString(name!); + mod.Author = author != null ? new LowerString(author) : mod.Author; + mod.Description = description ?? mod.Description; + mod.Version = version ?? mod.Version; + mod.Website = website ?? mod.Website; + _saveService.ImmediateSave(new ModMeta(mod)); + } + + public ModDataChangeType LoadLocalData(Mod mod) + { + var dataFile = _filenameService.LocalDataFile(mod); + + var importDate = 0L; + var localTags = Enumerable.Empty(); + var favorite = false; + var note = string.Empty; + + var save = true; + if (File.Exists(dataFile)) + { + save = false; + try + { + var text = File.ReadAllText(dataFile); + var json = JObject.Parse(text); + + importDate = json[nameof(Mod.ImportDate)]?.Value() ?? importDate; + favorite = json[nameof(Mod.Favorite)]?.Value() ?? favorite; + note = json[nameof(Mod.Note)]?.Value() ?? note; + localTags = json[nameof(Mod.LocalTags)]?.Values().OfType() ?? localTags; + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not load local mod data:\n{e}"); + } + } + + if (importDate == 0) + importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + ModDataChangeType changes = 0; + if (mod.ImportDate != importDate) + { + mod.ImportDate = importDate; + changes |= ModDataChangeType.ImportDate; + } + + changes |= mod.UpdateTags(null, localTags); + + if (mod.Favorite != favorite) + { + mod.Favorite = favorite; + changes |= ModDataChangeType.Favorite; + } + + if (mod.Note != note) + { + mod.Note = note; + changes |= ModDataChangeType.Note; + } + + if (save) + _saveService.QueueSave(new ModData(mod)); + + return changes; + } + + public ModDataChangeType LoadMeta(Mod mod) + { + var metaFile = _filenameService.ModMetaPath(mod); + if (!File.Exists(metaFile)) + { + Penumbra.Log.Debug($"No mod meta found for {mod.ModPath.Name}."); + return ModDataChangeType.Deletion; + } + + try + { + var text = File.ReadAllText(metaFile); + var json = JObject.Parse(text); + + var newName = json[nameof(Mod.Name)]?.Value() ?? string.Empty; + var newAuthor = json[nameof(Mod.Author)]?.Value() ?? string.Empty; + var newDescription = json[nameof(Mod.Description)]?.Value() ?? string.Empty; + var newVersion = json[nameof(Mod.Version)]?.Value() ?? string.Empty; + var newWebsite = json[nameof(Mod.Website)]?.Value() ?? string.Empty; + var newFileVersion = json[nameof(Mod.FileVersion)]?.Value() ?? 0; + var importDate = json[nameof(Mod.ImportDate)]?.Value(); + var modTags = json[nameof(Mod.ModTags)]?.Values().OfType(); + + ModDataChangeType changes = 0; + if (mod.Name != newName) + { + changes |= ModDataChangeType.Name; + mod.Name = newName; + } + + if (mod.Author != newAuthor) + { + changes |= ModDataChangeType.Author; + mod.Author = newAuthor; + } + + if (mod.Description != newDescription) + { + changes |= ModDataChangeType.Description; + mod.Description = newDescription; + } + + if (mod.Version != newVersion) + { + changes |= ModDataChangeType.Version; + mod.Version = newVersion; + } + + if (mod.Website != newWebsite) + { + changes |= ModDataChangeType.Website; + mod.Website = newWebsite; + } + + if (mod.FileVersion != newFileVersion) + { + mod.FileVersion = newFileVersion; + if (Mod.Migration.Migrate(mod, json)) + { + changes |= ModDataChangeType.Migration; + _saveService.ImmediateSave(new ModMeta(mod)); + } + } + + if (importDate != null && mod.ImportDate != importDate.Value) + { + mod.ImportDate = importDate.Value; + changes |= ModDataChangeType.ImportDate; + } + + changes |= mod.UpdateTags(modTags, null); + + return changes; + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not load mod meta:\n{e}"); + return ModDataChangeType.Deletion; + } + } + + public void ChangeModName(Mod mod, string newName) + { + if (mod.Name.Text == newName) + return; + + var oldName = mod.Name; + mod.Name = newName; + _saveService.QueueSave(new ModMeta(mod)); + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Name, mod, oldName.Text); + } + + public void ChangeModAuthor(Mod mod, string newAuthor) + { + if (mod.Author == newAuthor) + return; + + mod.Author = newAuthor; + _saveService.QueueSave(new ModMeta(mod)); + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Author, mod, null); + } + + public void ChangeModDescription(Mod mod, string newDescription) + { + if (mod.Description == newDescription) + return; + + mod.Description = newDescription; + _saveService.QueueSave(new ModMeta(mod)); + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Description, mod, null); + } + + public void ChangeModVersion(Mod mod, string newVersion) + { + if (mod.Version == newVersion) + return; + + mod.Version = newVersion; + _saveService.QueueSave(new ModMeta(mod)); + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Version, mod, null); + } + + public void ChangeModWebsite(Mod mod, string newWebsite) + { + if (mod.Website == newWebsite) + return; + + mod.Website = newWebsite; + _saveService.QueueSave(new ModMeta(mod)); + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Website, mod, null); + } + + public void ChangeModTag(Mod mod, int tagIdx, string newTag) + => ChangeTag(mod, tagIdx, newTag, false); + + public void ChangeLocalTag(Mod mod, int tagIdx, string newTag) + => ChangeTag(mod, tagIdx, newTag, true); + + public void ChangeModFavorite(Mod mod, bool state) + { + if (mod.Favorite == state) + return; + + mod.Favorite = state; + _saveService.QueueSave(new ModData(mod)); + ; + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null); + } + + public void ChangeModNote(Mod mod, string newNote) + { + if (mod.Note == newNote) + return; + + mod.Note = newNote; + _saveService.QueueSave(new ModData(mod)); + ; + _communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null); + } + + + private void ChangeTag(Mod mod, int tagIdx, string newTag, bool local) + { + var which = local ? mod.LocalTags : mod.ModTags; + if (tagIdx < 0 || tagIdx > which.Count) + return; + + ModDataChangeType flags = 0; + if (tagIdx == which.Count) + { + flags = mod.UpdateTags(local ? null : which.Append(newTag), local ? which.Append(newTag) : null); + } + else + { + var tmp = which.ToArray(); + tmp[tagIdx] = newTag; + flags = mod.UpdateTags(local ? null : tmp, local ? tmp : null); + } + + if (flags.HasFlag(ModDataChangeType.ModTags)) + _saveService.QueueSave(new ModMeta(mod)); + + if (flags.HasFlag(ModDataChangeType.LocalTags)) + _saveService.QueueSave(new ModData(mod)); + + if (flags != 0) + _communicatorService.ModDataChanged.Invoke(flags, mod, null); + } + + public void MoveDataFile(DirectoryInfo oldMod, DirectoryInfo newMod) + { + var oldFile = _filenameService.LocalDataFile(oldMod.Name); + var newFile = _filenameService.LocalDataFile(newMod.Name); + if (!File.Exists(oldFile)) + return; + + try + { + File.Move(oldFile, newFile, true); + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not move local data file {oldFile} to {newFile}:\n{e}"); + } + } + + + private readonly struct ModMeta : ISavable + { + private readonly Mod _mod; + + public ModMeta(Mod mod) + => _mod = mod; + + public string ToFilename(FilenameService fileNames) + => fileNames.ModMetaPath(_mod); + + public void Save(StreamWriter writer) + { + var jObject = new JObject + { + { nameof(Mod.FileVersion), JToken.FromObject(_mod.FileVersion) }, + { nameof(Mod.Name), JToken.FromObject(_mod.Name) }, + { nameof(Mod.Author), JToken.FromObject(_mod.Author) }, + { nameof(Mod.Description), JToken.FromObject(_mod.Description) }, + { nameof(Mod.Version), JToken.FromObject(_mod.Version) }, + { nameof(Mod.Website), JToken.FromObject(_mod.Website) }, + { nameof(Mod.ModTags), JToken.FromObject(_mod.ModTags) }, + }; + using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }; + jObject.WriteTo(jWriter); + } + } + + private readonly struct ModData : ISavable + { + private readonly Mod _mod; + + public ModData(Mod mod) + => _mod = mod; + + public string ToFilename(FilenameService fileNames) + => fileNames.LocalDataFile(_mod); + + public void Save(StreamWriter writer) + { + var jObject = new JObject + { + { nameof(Mod.FileVersion), JToken.FromObject(_mod.FileVersion) }, + { nameof(Mod.ImportDate), JToken.FromObject(_mod.ImportDate) }, + { nameof(Mod.LocalTags), JToken.FromObject(_mod.LocalTags) }, + { nameof(Mod.Note), JToken.FromObject(_mod.Note) }, + { nameof(Mod.Favorite), JToken.FromObject(_mod.Favorite) }, + }; + using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }; + jObject.WriteTo(jWriter); + } + } +} diff --git a/Penumbra/Mods/Mod.BasePath.cs b/Penumbra/Mods/Mod.BasePath.cs index 8423d023..c7367a68 100644 --- a/Penumbra/Mods/Mod.BasePath.cs +++ b/Penumbra/Mods/Mod.BasePath.cs @@ -16,6 +16,8 @@ public enum ModPathChangeType public partial class Mod { public DirectoryInfo ModPath { get; private set; } + public string Identifier + => Index >= 0 ? ModPath.Name : Name; public int Index { get; private set; } = -1; public bool IsTemporary @@ -31,7 +33,7 @@ public partial class Mod _default = new SubMod( this ); } - private static Mod? LoadMod( DirectoryInfo modPath, bool incorporateMetaChanges ) + private static Mod? LoadMod( Manager modManager, DirectoryInfo modPath, bool incorporateMetaChanges ) { modPath.Refresh(); if( !modPath.Exists ) @@ -40,18 +42,17 @@ public partial class Mod return null; } - var mod = new Mod( modPath ); - if( !mod.Reload( incorporateMetaChanges, out _ ) ) - { - // Can not be base path not existing because that is checked before. - Penumbra.Log.Warning( $"Mod at {modPath} without name is not supported." ); - return null; - } + var mod = new Mod(modPath); + if (mod.Reload(modManager, incorporateMetaChanges, out _)) + return mod; + + // Can not be base path not existing because that is checked before. + Penumbra.Log.Warning( $"Mod at {modPath} without name is not supported." ); + return null; - return mod; } - internal bool Reload( bool incorporateMetaChanges, out ModDataChangeType modDataChange ) + internal bool Reload(Manager modManager, bool incorporateMetaChanges, out ModDataChangeType modDataChange ) { modDataChange = ModDataChangeType.Deletion; ModPath.Refresh(); @@ -60,19 +61,19 @@ public partial class Mod return false; } - modDataChange = LoadMeta(); + modDataChange = modManager.DataEditor.LoadMeta(this); if( modDataChange.HasFlag( ModDataChangeType.Deletion ) || Name.Length == 0 ) { return false; } - LoadLocalData(); + modManager.DataEditor.LoadLocalData(this); LoadDefaultOption(); LoadAllGroups(); if( incorporateMetaChanges ) { - IncorporateAllMetaChanges( true ); + IncorporateAllMetaChanges(true); } ComputeChangedItems(); diff --git a/Penumbra/Mods/Mod.Creator.cs b/Penumbra/Mods/Mod.Creator.cs index 0074cf77..f67dbf33 100644 --- a/Penumbra/Mods/Mod.Creator.cs +++ b/Penumbra/Mods/Mod.Creator.cs @@ -64,19 +64,6 @@ public partial class Mod return newModFolder.Length == 0 ? null : new DirectoryInfo( newModFolder ); } - /// Create the file containing the meta information about a mod from scratch. - public static void CreateMeta( DirectoryInfo directory, string? name, string? author, string? description, string? version, - string? website ) - { - var mod = new Mod( directory ); - mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString( name! ); - mod.Author = author != null ? new LowerString( author ) : mod.Author; - mod.Description = description ?? mod.Description; - mod.Version = version ?? mod.Version; - mod.Website = website ?? mod.Website; - mod.SaveMetaFile(); // Not delayed. - } - /// Create a file for an option group from given data. public static void CreateOptionGroup( DirectoryInfo baseFolder, GroupType type, string name, int priority, int index, uint defaultSettings, string desc, IEnumerable< ISubMod > subMods ) @@ -147,13 +134,11 @@ public partial class Mod internal static void CreateDefaultFiles( DirectoryInfo directory ) { var mod = new Mod( directory ); - mod.Reload( false, out _ ); + mod.Reload( Penumbra.ModManager, false, out _ ); foreach( var file in mod.FindUnusedFiles() ) { if( Utf8GamePath.FromFile( new FileInfo( file.FullName ), directory, out var gamePath, true ) ) - { mod._default.FileData.TryAdd( gamePath, file ); - } } mod._default.IncorporateMetaChanges( directory, true ); diff --git a/Penumbra/Mods/Mod.LocalData.cs b/Penumbra/Mods/Mod.LocalData.cs index 57699fcb..29e11f3d 100644 --- a/Penumbra/Mods/Mod.LocalData.cs +++ b/Penumbra/Mods/Mod.LocalData.cs @@ -3,145 +3,30 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Dalamud.Plugin; -using Newtonsoft.Json; -using Penumbra.Services; - +using Penumbra.Services; + namespace Penumbra.Mods; public sealed partial class Mod { - public static DirectoryInfo LocalDataDirectory(DalamudPluginInterface pi) - => new(Path.Combine( pi.ConfigDirectory.FullName, "mod_data" )); + public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds(); - public long ImportDate { get; private set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds(); + public IReadOnlyList LocalTags { get; private set; } = Array.Empty(); - public IReadOnlyList< string > LocalTags { get; private set; } = Array.Empty< string >(); + public string AllTagsLower { get; private set; } = string.Empty; + public string Note { get; internal set; } = string.Empty; + public bool Favorite { get; internal set; } = false; - public string AllTagsLower { get; private set; } = string.Empty; - public string Note { get; private set; } = string.Empty; - public bool Favorite { get; private set; } = false; - - private FileInfo LocalDataFile - => new(Path.Combine( DalamudServices.PluginInterface.ConfigDirectory.FullName, "mod_data", $"{ModPath.Name}.json" )); - - private ModDataChangeType LoadLocalData() + internal ModDataChangeType UpdateTags(IEnumerable? newModTags, IEnumerable? newLocalTags) { - var dataFile = LocalDataFile; - - var importDate = 0L; - var localTags = Enumerable.Empty< string >(); - var favorite = false; - var note = string.Empty; - - var save = true; - if( File.Exists( dataFile.FullName ) ) - { - save = false; - try - { - var text = File.ReadAllText( dataFile.FullName ); - var json = JObject.Parse( text ); - - importDate = json[ nameof( ImportDate ) ]?.Value< long >() ?? importDate; - favorite = json[ nameof( Favorite ) ]?.Value< bool >() ?? favorite; - note = json[ nameof( Note ) ]?.Value< string >() ?? note; - localTags = json[ nameof( LocalTags ) ]?.Values< string >().OfType< string >() ?? localTags; - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Could not load local mod data:\n{e}" ); - } - } - - if( importDate == 0 ) - { - importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - } - - ModDataChangeType changes = 0; - if( ImportDate != importDate ) - { - ImportDate = importDate; - changes |= ModDataChangeType.ImportDate; - } - - changes |= UpdateTags( null, localTags ); - - if( Favorite != favorite ) - { - Favorite = favorite; - changes |= ModDataChangeType.Favorite; - } - - if( Note != note ) - { - Note = note; - changes |= ModDataChangeType.Note; - } - - if( save ) - { - SaveLocalDataFile(); - } - - return changes; - } - - private void SaveLocalData() - => Penumbra.Framework.RegisterDelayed( nameof( SaveLocalData ) + ModPath.Name, SaveLocalDataFile ); - - private void SaveLocalDataFile() - { - var dataFile = LocalDataFile; - try - { - var jObject = new JObject - { - { nameof( FileVersion ), JToken.FromObject( FileVersion ) }, - { nameof( ImportDate ), JToken.FromObject( ImportDate ) }, - { nameof( LocalTags ), JToken.FromObject( LocalTags ) }, - { nameof( Note ), JToken.FromObject( Note ) }, - { nameof( Favorite ), JToken.FromObject( Favorite ) }, - }; - dataFile.Directory!.Create(); - File.WriteAllText( dataFile.FullName, jObject.ToString( Formatting.Indented ) ); - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Could not write local data file for mod {Name} to {dataFile.FullName}:\n{e}" ); - } - } - - private static void MoveDataFile( DirectoryInfo oldMod, DirectoryInfo newMod ) - { - var oldFile = Path.Combine( DalamudServices.PluginInterface.ConfigDirectory.FullName, "mod_data", $"{oldMod.Name}.json" ); - var newFile = Path.Combine( DalamudServices.PluginInterface.ConfigDirectory.FullName, "mod_data", $"{newMod.Name}.json" ); - if( File.Exists( oldFile ) ) - { - try - { - File.Move( oldFile, newFile, true ); - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Could not move local data file {oldFile} to {newFile}:\n{e}" ); - } - } - } - - private ModDataChangeType UpdateTags( IEnumerable< string >? newModTags, IEnumerable< string >? newLocalTags ) - { - if( newModTags == null && newLocalTags == null ) - { + if (newModTags == null && newLocalTags == null) return 0; - } ModDataChangeType type = 0; - if( newModTags != null ) + if (newModTags != null) { - var modTags = newModTags.Where( t => t.Length > 0 ).Distinct().ToArray(); - if( !modTags.SequenceEqual( ModTags ) ) + var modTags = newModTags.Where(t => t.Length > 0).Distinct().ToArray(); + if (!modTags.SequenceEqual(ModTags)) { newLocalTags ??= LocalTags; ModTags = modTags; @@ -149,21 +34,19 @@ public sealed partial class Mod } } - if( newLocalTags != null ) + if (newLocalTags != null) { - var localTags = newLocalTags!.Where( t => t.Length > 0 && !ModTags.Contains( t ) ).Distinct().ToArray(); - if( !localTags.SequenceEqual( LocalTags ) ) + var localTags = newLocalTags!.Where(t => t.Length > 0 && !ModTags.Contains(t)).Distinct().ToArray(); + if (!localTags.SequenceEqual(LocalTags)) { LocalTags = localTags; type |= ModDataChangeType.LocalTags; } } - if( type != 0 ) - { - AllTagsLower = string.Join( '\0', ModTags.Concat( LocalTags ).Select( s => s.ToLowerInvariant() ) ); - } + if (type != 0) + AllTagsLower = string.Join('\0', ModTags.Concat(LocalTags).Select(s => s.ToLowerInvariant())); return type; } -} \ No newline at end of file +} diff --git a/Penumbra/Mods/Mod.Meta.Migration.cs b/Penumbra/Mods/Mod.Meta.Migration.cs index fa16f4c2..64501009 100644 --- a/Penumbra/Mods/Mod.Meta.Migration.cs +++ b/Penumbra/Mods/Mod.Meta.Migration.cs @@ -13,146 +13,115 @@ namespace Penumbra.Mods; public sealed partial class Mod { - private static class Migration + public static partial class Migration { - public static bool Migrate( Mod mod, JObject json ) - { - var ret = MigrateV0ToV1( mod, json ) || MigrateV1ToV2( mod ) || MigrateV2ToV3( mod ); - if( ret ) - { - // Immediately save on migration. - mod.SaveMetaFile(); - } + public static bool Migrate(Mod mod, JObject json) + => MigrateV0ToV1(mod, json) || MigrateV1ToV2(mod) || MigrateV2ToV3(mod); - return ret; - } - - private static bool MigrateV2ToV3( Mod mod ) + private static bool MigrateV2ToV3(Mod mod) { - if( mod.FileVersion > 2 ) - { + if (mod.FileVersion > 2) return false; - } // Remove import time. mod.FileVersion = 3; return true; } + [GeneratedRegex(@"group_\d{3}_", RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture)] + private static partial Regex GroupRegex(); - private static readonly Regex GroupRegex = new( @"group_\d{3}_", RegexOptions.Compiled ); - private static bool MigrateV1ToV2( Mod mod ) + private static bool MigrateV1ToV2(Mod mod) { - if( mod.FileVersion > 1 ) - { + if (mod.FileVersion > 1) return false; - } - if (!mod.GroupFiles.All( g => GroupRegex.IsMatch( g.Name ))) - { - foreach( var (group, index) in mod.GroupFiles.WithIndex().ToArray() ) + if (!mod.GroupFiles.All(g => GroupRegex().IsMatch(g.Name))) + foreach (var (group, index) in mod.GroupFiles.WithIndex().ToArray()) { - var newName = Regex.Replace( group.Name, "^group_", $"group_{index + 1:D3}_", RegexOptions.Compiled ); + var newName = Regex.Replace(group.Name, "^group_", $"group_{index + 1:D3}_", RegexOptions.Compiled); try { - if( newName != group.Name ) - { - group.MoveTo( Path.Combine( group.DirectoryName ?? string.Empty, newName ), false ); - } + if (newName != group.Name) + group.MoveTo(Path.Combine(group.DirectoryName ?? string.Empty, newName), false); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Error( $"Could not rename group file {group.Name} to {newName} during migration:\n{e}" ); + Penumbra.Log.Error($"Could not rename group file {group.Name} to {newName} during migration:\n{e}"); } } - } mod.FileVersion = 2; return true; } - private static bool MigrateV0ToV1( Mod mod, JObject json ) + private static bool MigrateV0ToV1(Mod mod, JObject json) { - if( mod.FileVersion > 0 ) - { + if (mod.FileVersion > 0) return false; - } - var swaps = json[ "FileSwaps" ]?.ToObject< Dictionary< Utf8GamePath, FullPath > >() - ?? new Dictionary< Utf8GamePath, FullPath >(); - var groups = json[ "Groups" ]?.ToObject< Dictionary< string, OptionGroupV0 > >() ?? new Dictionary< string, OptionGroupV0 >(); + var swaps = json["FileSwaps"]?.ToObject>() + ?? new Dictionary(); + var groups = json["Groups"]?.ToObject>() ?? new Dictionary(); var priority = 1; - var seenMetaFiles = new HashSet< FullPath >(); - foreach( var group in groups.Values ) - { - ConvertGroup( mod, group, ref priority, seenMetaFiles ); - } + var seenMetaFiles = new HashSet(); + foreach (var group in groups.Values) + ConvertGroup(mod, group, ref priority, seenMetaFiles); - foreach( var unusedFile in mod.FindUnusedFiles().Where( f => !seenMetaFiles.Contains( f ) ) ) + foreach (var unusedFile in mod.FindUnusedFiles().Where(f => !seenMetaFiles.Contains(f))) { - if( unusedFile.ToGamePath( mod.ModPath, out var gamePath ) - && !mod._default.FileData.TryAdd( gamePath, unusedFile ) ) - { - Penumbra.Log.Error( $"Could not add {gamePath} because it already points to {mod._default.FileData[ gamePath ]}." ); - } + if (unusedFile.ToGamePath(mod.ModPath, out var gamePath) + && !mod._default.FileData.TryAdd(gamePath, unusedFile)) + Penumbra.Log.Error($"Could not add {gamePath} because it already points to {mod._default.FileData[gamePath]}."); } mod._default.FileSwapData.Clear(); - mod._default.FileSwapData.EnsureCapacity( swaps.Count ); - foreach( var (gamePath, swapPath) in swaps ) - { - mod._default.FileSwapData.Add( gamePath, swapPath ); - } + mod._default.FileSwapData.EnsureCapacity(swaps.Count); + foreach (var (gamePath, swapPath) in swaps) + mod._default.FileSwapData.Add(gamePath, swapPath); - mod._default.IncorporateMetaChanges( mod.ModPath, true ); - foreach( var (group, index) in mod.Groups.WithIndex() ) - { - IModGroup.Save( group, mod.ModPath, index ); - } + mod._default.IncorporateMetaChanges(mod.ModPath, true); + foreach (var (group, index) in mod.Groups.WithIndex()) + IModGroup.Save(group, mod.ModPath, index); // Delete meta files. - foreach( var file in seenMetaFiles.Where( f => f.Exists ) ) + foreach (var file in seenMetaFiles.Where(f => f.Exists)) { try { - File.Delete( file.FullName ); + File.Delete(file.FullName); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Warning( $"Could not delete meta file {file.FullName} during migration:\n{e}" ); + Penumbra.Log.Warning($"Could not delete meta file {file.FullName} during migration:\n{e}"); } } // Delete old meta files. - var oldMetaFile = Path.Combine( mod.ModPath.FullName, "metadata_manipulations.json" ); - if( File.Exists( oldMetaFile ) ) - { + var oldMetaFile = Path.Combine(mod.ModPath.FullName, "metadata_manipulations.json"); + if (File.Exists(oldMetaFile)) try { - File.Delete( oldMetaFile ); + File.Delete(oldMetaFile); } - catch( Exception e ) + catch (Exception e) { - Penumbra.Log.Warning( $"Could not delete old meta file {oldMetaFile} during migration:\n{e}" ); + Penumbra.Log.Warning($"Could not delete old meta file {oldMetaFile} during migration:\n{e}"); } - } mod.FileVersion = 1; mod.SaveDefaultMod(); - mod.SaveMetaFile(); return true; } - private static void ConvertGroup( Mod mod, OptionGroupV0 group, ref int priority, HashSet< FullPath > seenMetaFiles ) + private static void ConvertGroup(Mod mod, OptionGroupV0 group, ref int priority, HashSet seenMetaFiles) { - if( group.Options.Count == 0 ) - { + if (group.Options.Count == 0) return; - } - switch( group.SelectionType ) + switch (group.SelectionType) { case GroupType.Multi: @@ -163,17 +132,15 @@ public sealed partial class Mod Priority = priority++, Description = string.Empty, }; - mod._groups.Add( newMultiGroup ); - foreach( var option in group.Options ) - { - newMultiGroup.PrioritizedOptions.Add( ( SubModFromOption( mod, option, seenMetaFiles ), optionPriority++ ) ); - } + mod._groups.Add(newMultiGroup); + foreach (var option in group.Options) + newMultiGroup.PrioritizedOptions.Add((SubModFromOption(mod, option, seenMetaFiles), optionPriority++)); break; case GroupType.Single: - if( group.Options.Count == 1 ) + if (group.Options.Count == 1) { - AddFilesToSubMod( mod._default, mod.ModPath, group.Options[ 0 ], seenMetaFiles ); + AddFilesToSubMod(mod._default, mod.ModPath, group.Options[0], seenMetaFiles); return; } @@ -183,38 +150,32 @@ public sealed partial class Mod Priority = priority++, Description = string.Empty, }; - mod._groups.Add( newSingleGroup ); - foreach( var option in group.Options ) - { - newSingleGroup.OptionData.Add( SubModFromOption( mod, option, seenMetaFiles ) ); - } + mod._groups.Add(newSingleGroup); + foreach (var option in group.Options) + newSingleGroup.OptionData.Add(SubModFromOption(mod, option, seenMetaFiles)); break; } } - private static void AddFilesToSubMod( SubMod mod, DirectoryInfo basePath, OptionV0 option, HashSet< FullPath > seenMetaFiles ) + private static void AddFilesToSubMod(SubMod mod, DirectoryInfo basePath, OptionV0 option, HashSet seenMetaFiles) { - foreach( var (relPath, gamePaths) in option.OptionFiles ) + foreach (var (relPath, gamePaths) in option.OptionFiles) { - var fullPath = new FullPath( basePath, relPath ); - foreach( var gamePath in gamePaths ) - { - mod.FileData.TryAdd( gamePath, fullPath ); - } + var fullPath = new FullPath(basePath, relPath); + foreach (var gamePath in gamePaths) + mod.FileData.TryAdd(gamePath, fullPath); - if( fullPath.Extension is ".meta" or ".rgsp" ) - { - seenMetaFiles.Add( fullPath ); - } + if (fullPath.Extension is ".meta" or ".rgsp") + seenMetaFiles.Add(fullPath); } } - private static SubMod SubModFromOption( Mod mod, OptionV0 option, HashSet< FullPath > seenMetaFiles ) + private static SubMod SubModFromOption(Mod mod, OptionV0 option, HashSet seenMetaFiles) { - var subMod = new SubMod( mod ) { Name = option.OptionName }; - AddFilesToSubMod( subMod, mod.ModPath, option, seenMetaFiles ); - subMod.IncorporateMetaChanges( mod.ModPath, false ); + var subMod = new SubMod(mod) { Name = option.OptionName }; + AddFilesToSubMod(subMod, mod.ModPath, option, seenMetaFiles); + subMod.IncorporateMetaChanges(mod.ModPath, false); return subMod; } @@ -223,8 +184,8 @@ public sealed partial class Mod public string OptionName = string.Empty; public string OptionDesc = string.Empty; - [JsonProperty( ItemConverterType = typeof( SingleOrArrayConverter< Utf8GamePath > ) )] - public Dictionary< Utf8RelPath, HashSet< Utf8GamePath > > OptionFiles = new(); + [JsonProperty(ItemConverterType = typeof(SingleOrArrayConverter))] + public Dictionary> OptionFiles = new(); public OptionV0() { } @@ -234,53 +195,49 @@ public sealed partial class Mod { public string GroupName = string.Empty; - [JsonConverter( typeof( Newtonsoft.Json.Converters.StringEnumConverter ) )] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] public GroupType SelectionType = GroupType.Single; - public List< OptionV0 > Options = new(); + public List Options = new(); public OptionGroupV0() { } } // Not used anymore, but required for migration. - private class SingleOrArrayConverter< T > : JsonConverter + private class SingleOrArrayConverter : JsonConverter { - public override bool CanConvert( Type objectType ) - => objectType == typeof( HashSet< T > ); + public override bool CanConvert(Type objectType) + => objectType == typeof(HashSet); - public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer ) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { - var token = JToken.Load( reader ); + var token = JToken.Load(reader); - if( token.Type == JTokenType.Array ) - { - return token.ToObject< HashSet< T > >() ?? new HashSet< T >(); - } + if (token.Type == JTokenType.Array) + return token.ToObject>() ?? new HashSet(); - var tmp = token.ToObject< T >(); + var tmp = token.ToObject(); return tmp != null - ? new HashSet< T > { tmp } - : new HashSet< T >(); + ? new HashSet { tmp } + : new HashSet(); } public override bool CanWrite => true; - public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer ) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { writer.WriteStartArray(); - if( value != null ) + if (value != null) { - var v = ( HashSet< T > )value; - foreach( var val in v ) - { - serializer.Serialize( writer, val?.ToString() ); - } + var v = (HashSet)value; + foreach (var val in v) + serializer.Serialize(writer, val?.ToString()); } writer.WriteEndArray(); } } } -} \ No newline at end of file +} diff --git a/Penumbra/Mods/Mod.Meta.cs b/Penumbra/Mods/Mod.Meta.cs index 5ba44286..16ae4d1f 100644 --- a/Penumbra/Mods/Mod.Meta.cs +++ b/Penumbra/Mods/Mod.Meta.cs @@ -1,31 +1,9 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using OtterGui.Classes; namespace Penumbra.Mods; -[Flags] -public enum ModDataChangeType : ushort -{ - None = 0x0000, - Name = 0x0001, - Author = 0x0002, - Description = 0x0004, - Version = 0x0008, - Website = 0x0010, - Deletion = 0x0020, - Migration = 0x0040, - ModTags = 0x0080, - ImportDate = 0x0100, - Favorite = 0x0200, - LocalTags = 0x0400, - Note = 0x0800, -} - public sealed partial class Mod : IMod { public static readonly TemporaryMod ForcedFiles = new() @@ -36,122 +14,13 @@ public sealed partial class Mod : IMod }; public const uint CurrentFileVersion = 3; - public uint FileVersion { get; private set; } = CurrentFileVersion; - public LowerString Name { get; private set; } = "New Mod"; - public LowerString Author { get; private set; } = LowerString.Empty; - public string Description { get; private set; } = string.Empty; - public string Version { get; private set; } = string.Empty; - public string Website { get; private set; } = string.Empty; - public IReadOnlyList< string > ModTags { get; private set; } = Array.Empty< string >(); - - internal FileInfo MetaFile - => new(Path.Combine( ModPath.FullName, "meta.json" )); - - private ModDataChangeType LoadMeta() - { - var metaFile = MetaFile; - if( !File.Exists( metaFile.FullName ) ) - { - Penumbra.Log.Debug( $"No mod meta found for {ModPath.Name}." ); - return ModDataChangeType.Deletion; - } - - try - { - var text = File.ReadAllText( metaFile.FullName ); - var json = JObject.Parse( text ); - - var newName = json[ nameof( Name ) ]?.Value< string >() ?? string.Empty; - var newAuthor = json[ nameof( Author ) ]?.Value< string >() ?? string.Empty; - var newDescription = json[ nameof( Description ) ]?.Value< string >() ?? string.Empty; - var newVersion = json[ nameof( Version ) ]?.Value< string >() ?? string.Empty; - var newWebsite = json[ nameof( Website ) ]?.Value< string >() ?? string.Empty; - var newFileVersion = json[ nameof( FileVersion ) ]?.Value< uint >() ?? 0; - var importDate = json[ nameof( ImportDate ) ]?.Value< long >(); - var modTags = json[ nameof( ModTags ) ]?.Values< string >().OfType< string >(); - - ModDataChangeType changes = 0; - if( Name != newName ) - { - changes |= ModDataChangeType.Name; - Name = newName; - } - - if( Author != newAuthor ) - { - changes |= ModDataChangeType.Author; - Author = newAuthor; - } - - if( Description != newDescription ) - { - changes |= ModDataChangeType.Description; - Description = newDescription; - } - - if( Version != newVersion ) - { - changes |= ModDataChangeType.Version; - Version = newVersion; - } - - if( Website != newWebsite ) - { - changes |= ModDataChangeType.Website; - Website = newWebsite; - } - - if( FileVersion != newFileVersion ) - { - FileVersion = newFileVersion; - if( Migration.Migrate( this, json ) ) - { - changes |= ModDataChangeType.Migration; - } - } - - if( importDate != null && ImportDate != importDate.Value ) - { - ImportDate = importDate.Value; - changes |= ModDataChangeType.ImportDate; - } - - changes |= UpdateTags( modTags, null ); - - return changes; - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Could not load mod meta:\n{e}" ); - return ModDataChangeType.Deletion; - } - } - - private void SaveMeta() - => Penumbra.Framework.RegisterDelayed( nameof( SaveMetaFile ) + ModPath.Name, SaveMetaFile ); - - private void SaveMetaFile() - { - var metaFile = MetaFile; - try - { - var jObject = new JObject - { - { nameof( FileVersion ), JToken.FromObject( FileVersion ) }, - { nameof( Name ), JToken.FromObject( Name ) }, - { nameof( Author ), JToken.FromObject( Author ) }, - { nameof( Description ), JToken.FromObject( Description ) }, - { nameof( Version ), JToken.FromObject( Version ) }, - { nameof( Website ), JToken.FromObject( Website ) }, - { nameof( ModTags ), JToken.FromObject( ModTags ) }, - }; - File.WriteAllText( metaFile.FullName, jObject.ToString( Formatting.Indented ) ); - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Could not write meta file for mod {Name} to {metaFile.FullName}:\n{e}" ); - } - } + public uint FileVersion { get; internal set; } = CurrentFileVersion; + public LowerString Name { get; internal set; } = "New Mod"; + public LowerString Author { get; internal set; } = LowerString.Empty; + public string Description { get; internal set; } = string.Empty; + public string Version { get; internal set; } = string.Empty; + public string Website { get; internal set; } = string.Empty; + public IReadOnlyList< string > ModTags { get; internal set; } = Array.Empty< string >(); public override string ToString() => Name.Text; diff --git a/Penumbra/Mods/Mod.TemporaryMod.cs b/Penumbra/Mods/Mod.TemporaryMod.cs index 6de0a682..e686ad0d 100644 --- a/Penumbra/Mods/Mod.TemporaryMod.cs +++ b/Penumbra/Mods/Mod.TemporaryMod.cs @@ -46,14 +46,14 @@ public sealed partial class Mod _default.ManipulationData = manips; } - public static void SaveTempCollection( ModCollection collection, string? character = null ) + public static void SaveTempCollection( Mod.Manager modManager, ModCollection collection, string? character = null ) { DirectoryInfo? dir = null; try { dir = Creator.CreateModFolder( Penumbra.ModManager.BasePath, collection.Name ); var fileDir = Directory.CreateDirectory( Path.Combine( dir.FullName, "files" ) ); - Creator.CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor, + modManager.DataEditor.CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor, $"Mod generated from temporary collection {collection.Name} for {character ?? "Unknown Character"}.", null, null ); var mod = new Mod( dir ); var defaultMod = mod._default; @@ -88,7 +88,7 @@ public sealed partial class Mod } mod.SaveDefaultMod(); - Penumbra.ModManager.AddMod( dir ); + modManager.AddMod( dir ); Penumbra.Log.Information( $"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}." ); } catch( Exception e ) diff --git a/Penumbra/Mods/ModFileSystem.cs b/Penumbra/Mods/ModFileSystem.cs index 9ea21820..9bd7488a 100644 --- a/Penumbra/Mods/ModFileSystem.cs +++ b/Penumbra/Mods/ModFileSystem.cs @@ -10,28 +10,30 @@ using Penumbra.Util; namespace Penumbra.Mods; -public sealed class ModFileSystem : FileSystem, IDisposable, ISaveable +public sealed class ModFileSystem : FileSystem, IDisposable, ISavable { - private readonly Mod.Manager _modManager; - private readonly FilenameService _files; + private readonly Mod.Manager _modManager; + private readonly CommunicatorService _communicator; + private readonly FilenameService _files; // Create a new ModFileSystem from the currently loaded mods and the current sort order file. - public ModFileSystem(Mod.Manager modManager, FilenameService files) + public ModFileSystem(Mod.Manager modManager, CommunicatorService communicator, FilenameService files) { - _modManager = modManager; - _files = files; + _modManager = modManager; + _communicator = communicator; + _files = files; Reload(); - Changed += OnChange; - _modManager.ModDiscoveryFinished += Reload; - _modManager.ModDataChanged += OnDataChange; - _modManager.ModPathChanged += OnModPathChange; + Changed += OnChange; + _modManager.ModDiscoveryFinished += Reload; + _communicator.ModDataChanged.Event += OnDataChange; + _modManager.ModPathChanged += OnModPathChange; } public void Dispose() { - _modManager.ModPathChanged -= OnModPathChange; - _modManager.ModDiscoveryFinished -= Reload; - _modManager.ModDataChanged -= OnDataChange; + _modManager.ModPathChanged -= OnModPathChange; + _modManager.ModDiscoveryFinished -= Reload; + _communicator.ModDataChanged.Event -= OnDataChange; } public struct ImportDate : ISortMode diff --git a/Penumbra/PenumbraNew.cs b/Penumbra/PenumbraNew.cs index ad9344a4..30ea8f73 100644 --- a/Penumbra/PenumbraNew.cs +++ b/Penumbra/PenumbraNew.cs @@ -92,6 +92,7 @@ public class PenumbraNew // Add Mod Services services.AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton(); diff --git a/Penumbra/Services/CommunicatorService.cs b/Penumbra/Services/CommunicatorService.cs index 0e69276a..3feb5b91 100644 --- a/Penumbra/Services/CommunicatorService.cs +++ b/Penumbra/Services/CommunicatorService.cs @@ -45,6 +45,13 @@ public class CommunicatorService : IDisposable /// public readonly EventWrapper CreatedCharacterBase = new(nameof(CreatedCharacterBase)); + /// + /// Parameter is the type of data change for the mod, which can be multiple flags. + /// Parameter is the changed mod. + /// Parameter is the old name of the mod in case of a name change, and null otherwise. + /// + public readonly EventWrapper ModDataChanged = new(nameof(ModDataChanged)); + public void Dispose() { CollectionChange.Dispose(); @@ -52,5 +59,6 @@ public class CommunicatorService : IDisposable ModMetaChange.Dispose(); CreatingCharacterBase.Dispose(); CreatedCharacterBase.Dispose(); + ModDataChanged.Dispose(); } } diff --git a/Penumbra/Services/FilenameService.cs b/Penumbra/Services/FilenameService.cs index d57c854e..26721257 100644 --- a/Penumbra/Services/FilenameService.cs +++ b/Penumbra/Services/FilenameService.cs @@ -38,7 +38,7 @@ public class FilenameService /// Obtain the path of the local data file given a mod directory. Returns an empty string if the mod is temporary. public string LocalDataFile(Mod mod) - => mod.IsTemporary ? string.Empty : LocalDataFile(mod.ModPath.FullName); + => LocalDataFile(mod.ModPath.FullName); /// Obtain the path of the local data file given a mod directory. public string LocalDataFile(string modDirectory) @@ -66,7 +66,7 @@ public class FilenameService /// Obtain the path of the meta file for a given mod. Returns an empty string if the mod is temporary. public string ModMetaPath(Mod mod) - => mod.IsTemporary ? string.Empty : ModMetaPath(mod.ModPath.FullName); + => ModMetaPath(mod.ModPath.FullName); /// Obtain the path of the meta file given a mod directory. public string ModMetaPath(string modDirectory) diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index f6def7d4..3a5d8ce6 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -267,7 +267,7 @@ public class ItemSwapTab : IDisposable, ITab private void CreateMod() { var newDir = Mod.Creator.CreateModFolder(_modManager.BasePath, _newModName); - Mod.Creator.CreateMeta(newDir, _newModName, _config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty); + _modManager.DataEditor.CreateMeta(newDir, _newModName, _config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty); Mod.Creator.CreateDefaultFiles(newDir); _modManager.AddMod(newDir); if (!_swapData.WriteMod(_modManager.Last(), diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index d6d33175..d7e7efa1 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -150,7 +150,7 @@ public partial class ModEditWindow : Window, IDisposable _itemSwapTab.DrawContent(); } - // A row of three buttonSizes and a help marker that can be used for material suffix changing. + /// A row of three buttonSizes and a help marker that can be used for material suffix changing. private static class MaterialSuffix { private static string _materialSuffixFrom = string.Empty; diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index 49b8ff3d..3b4f2232 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -15,7 +15,7 @@ using OtterGui.Raii; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Import; -using Penumbra.Import.Structs; +using Penumbra.Import.Structs; using Penumbra.Mods; using Penumbra.Services; using Penumbra.UI.Classes; @@ -78,7 +78,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector.Leaf mod) { if (ImGui.MenuItem(mod.Value.Favorite ? "Remove Favorite" : "Mark as Favorite")) - _modManager.ChangeModFavorite(mod.Value.Index, !mod.Value.Favorite); + _modManager.DataEditor.ChangeModFavorite(mod.Value, !mod.Value.Favorite); } private void SetDefaultImportFolder(ModFileSystem.Folder folder) { - if (ImGui.MenuItem("Set As Default Import Folder")) - { - var newName = folder.FullName(); - if (newName != _config.DefaultImportFolder) - { - _config.DefaultImportFolder = newName; - _config.Save(); - } - } + if (!ImGui.MenuItem("Set As Default Import Folder")) + return; + + var newName = folder.FullName(); + if (newName == _config.DefaultImportFolder) + return; + + _config.DefaultImportFolder = newName; + _config.Save(); } private void ClearDefaultImportFolder() { - if (ImGui.MenuItem("Clear Default Import Folder") && _config.DefaultImportFolder.Length > 0) - { - _config.DefaultImportFolder = string.Empty; - _config.Save(); - } + if (!ImGui.MenuItem("Clear Default Import Folder") || _config.DefaultImportFolder.Length <= 0) + return; + + _config.DefaultImportFolder = string.Empty; + _config.Save(); } private string _newModName = string.Empty; @@ -241,7 +241,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector new FileInfo(file)), - AddNewMod, _config, _modEditor); + AddNewMod, _config, _modEditor, _modManager); ImGui.OpenPopup("Import Status"); }, 0, modPath, _config.AlwaysOpenDefaultImport); } diff --git a/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs b/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs index d6d04872..3d65e85a 100644 --- a/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs @@ -42,7 +42,7 @@ public class ModPanelDescriptionTab : ITab out var editedTag); _tutorial.OpenTutorial(BasicTutorialSteps.Tags); if (tagIdx >= 0) - _modManager.ChangeLocalTag(_selector.Selected!.Index, tagIdx, editedTag); + _modManager.DataEditor.ChangeLocalTag(_selector.Selected!, tagIdx, editedTag); if (_selector.Selected!.ModTags.Count > 0) _modTags.Draw("Mod Tags: ", "Tags assigned by the mod creator and saved with the mod data. To edit these, look at Edit Mod.", diff --git a/Penumbra/UI/ModsTab/ModPanelEditTab.cs b/Penumbra/UI/ModsTab/ModPanelEditTab.cs index 8dbc94cb..c3c496cb 100644 --- a/Penumbra/UI/ModsTab/ModPanelEditTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelEditTab.cs @@ -77,7 +77,7 @@ public class ModPanelEditTab : ITab var tagIdx = _modTags.Draw("Mod Tags: ", "Edit tags by clicking them, or add new tags. Empty tags are removed.", _mod.ModTags, out var editedTag); if (tagIdx >= 0) - _modManager.ChangeModTag(_mod.Index, tagIdx, editedTag); + _modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag); UiHelpers.DefaultLineSpace(); AddOptionGroup.Draw(_modManager, _mod); @@ -172,18 +172,18 @@ public class ModPanelEditTab : ITab private void EditRegularMeta() { if (Input.Text("Name", Input.Name, Input.None, _mod.Name, out var newName, 256, UiHelpers.InputTextWidth.X)) - _modManager.ChangeModName(_mod.Index, newName); + _modManager.DataEditor.ChangeModName(_mod, newName); if (Input.Text("Author", Input.Author, Input.None, _mod.Author, out var newAuthor, 256, UiHelpers.InputTextWidth.X)) - Penumbra.ModManager.ChangeModAuthor(_mod.Index, newAuthor); + _modManager.DataEditor.ChangeModAuthor(_mod, newAuthor); if (Input.Text("Version", Input.Version, Input.None, _mod.Version, out var newVersion, 32, UiHelpers.InputTextWidth.X)) - _modManager.ChangeModVersion(_mod.Index, newVersion); + _modManager.DataEditor.ChangeModVersion(_mod, newVersion); if (Input.Text("Website", Input.Website, Input.None, _mod.Website, out var newWebsite, 256, UiHelpers.InputTextWidth.X)) - _modManager.ChangeModWebsite(_mod.Index, newWebsite); + _modManager.DataEditor.ChangeModWebsite(_mod, newWebsite); using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3)); @@ -192,13 +192,13 @@ public class ModPanelEditTab : ITab _delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, Input.Description)); ImGui.SameLine(); - var fileExists = File.Exists(_mod.MetaFile.FullName); + var fileExists = File.Exists(_modManager.DataEditor.MetaFile(_mod)); var tt = fileExists ? "Open the metadata json file in the text editor of your choice." : "The metadata json file does not exist."; if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.FileExport.ToIconString()}##metaFile", UiHelpers.IconButtonSize, tt, !fileExists, true)) - Process.Start(new ProcessStartInfo(_mod.MetaFile.FullName) { UseShellExecute = true }); + Process.Start(new ProcessStartInfo(_modManager.DataEditor.MetaFile(_mod)) { UseShellExecute = true }); } /// Do some edits outside of iterations. @@ -349,7 +349,7 @@ public class ModPanelEditTab : ITab switch (_newDescriptionIdx) { case Input.Description: - modManager.ChangeModDescription(_mod.Index, _newDescription); + modManager.DataEditor.ChangeModDescription(_mod, _newDescription); break; case >= 0: if (_newDescriptionOptionIdx < 0) diff --git a/Penumbra/UI/ModsTab/ModPanelTabBar.cs b/Penumbra/UI/ModsTab/ModPanelTabBar.cs index 39f795d0..2ea6e39e 100644 --- a/Penumbra/UI/ModsTab/ModPanelTabBar.cs +++ b/Penumbra/UI/ModsTab/ModPanelTabBar.cs @@ -140,7 +140,7 @@ public class ModPanelTabBar ImGui.SetCursorPos(newPos); if (ImGui.Button(FontAwesomeIcon.Star.ToIconString())) - _modManager.ChangeModFavorite(mod.Index, !mod.Favorite); + _modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite); } var hovered = ImGui.IsItemHovered(); diff --git a/Penumbra/Util/SaveService.cs b/Penumbra/Util/SaveService.cs index 71c8b6b4..221ead81 100644 --- a/Penumbra/Util/SaveService.cs +++ b/Penumbra/Util/SaveService.cs @@ -1,10 +1,8 @@ using System; using System.IO; -using System.Runtime.CompilerServices; using System.Text; using OtterGui.Classes; using OtterGui.Log; -using Penumbra.Api; using Penumbra.Services; namespace Penumbra.Util; @@ -12,7 +10,7 @@ namespace Penumbra.Util; /// /// Any file type that we want to save via SaveService. /// -public interface ISaveable +public interface ISavable { /// The full file name of a given object. public string ToFilename(FilenameService fileNames); @@ -42,7 +40,7 @@ public class SaveService } /// Queue a save for the next framework tick. - public void QueueSave(ISaveable value) + public void QueueSave(ISavable value) { var file = value.ToFilename(_fileNames); _framework.RegisterDelayed(value.GetType().Name + file, () => @@ -52,7 +50,7 @@ public class SaveService } /// Immediately trigger a save. - public void ImmediateSave(ISaveable value) + public void ImmediateSave(ISavable value) { var name = value.ToFilename(_fileNames); try @@ -75,7 +73,7 @@ public class SaveService } } - public void ImmediateDelete(ISaveable value) + public void ImmediateDelete(ISavable value) { var name = value.ToFilename(_fileNames); try