diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index ad4d328b..5970a141 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -1,6 +1,7 @@ using Dalamud.Configuration; using Dalamud.Plugin; using System; +using System.Collections.Generic; namespace Penumbra { @@ -11,7 +12,9 @@ namespace Penumbra public bool IsEnabled { get; set; } = true; - public string BaseFolder { get; set; } = @"D:/ffxiv/fs_mods/"; + public string CurrentCollection { get; set; } = @"D:/ffxiv/fs_mods/"; + + public List< string > ModCollections { get; set; } = new(); // the below exist just to make saving less cumbersome diff --git a/Penumbra/ModManager.cs b/Penumbra/ModManager.cs deleted file mode 100644 index 28216a0b..00000000 --- a/Penumbra/ModManager.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Dalamud.Plugin; -using Penumbra.Models; -using Newtonsoft.Json; - -namespace Penumbra -{ - public class ModManager - { - public DirectoryInfo BasePath { get; set; } - - public readonly Dictionary< string, ResourceMod > AvailableMods = new Dictionary< string, ResourceMod >(); - - public readonly Dictionary< string, FileInfo > ResolvedFiles = new Dictionary< string, FileInfo >(); - - public ModManager( DirectoryInfo basePath ) - { - BasePath = basePath; - } - - public ModManager() - { - } - - public void DiscoverMods() - { - if( BasePath == null ) - { - return; - } - - if( !BasePath.Exists ) - { - return; - } - - AvailableMods.Clear(); - ResolvedFiles.Clear(); - - // get all mod dirs - foreach( var modDir in BasePath.EnumerateDirectories() ) - { - var metaFile = modDir.EnumerateFiles().FirstOrDefault( f => f.Name == "meta.json" ); - - if( metaFile == null ) - { - PluginLog.LogError( "mod meta is missing for resource mod: {ResourceModLocation}", modDir ); - continue; - } - - var meta = JsonConvert.DeserializeObject< Models.ModMeta >( File.ReadAllText( metaFile.FullName ) ); - - var mod = new ResourceMod - { - Meta = meta, - ModBasePath = modDir - }; - - AvailableMods[ modDir.Name ] = mod; - mod.RefreshModFiles(); - } - - // todo: sort the mods by priority here so that the file discovery works correctly - - foreach( var mod in AvailableMods.Select( m => m.Value ) ) - { - // fixup path - var baseDir = mod.ModBasePath.FullName; - - foreach( var file in mod.ModFiles ) - { - var path = file.FullName.Substring( baseDir.Length ).ToLowerInvariant() - .TrimStart( '\\' ).Replace( '\\', '/' ); - - // todo: notify when collisions happen? or some extra state on the file? not sure yet - // this code is shit all the same - - if( !ResolvedFiles.ContainsKey( path ) ) - { - ResolvedFiles[ path ] = file; - } - else - { - PluginLog.LogError( - "a different mod already fucks this file: {FilePath}", - ResolvedFiles[ path ].FullName - ); - } - } - } - } - - public FileInfo GetCandidateForGameFile( string resourcePath ) - { - return ResolvedFiles.TryGetValue( resourcePath.ToLowerInvariant(), out var fileInfo ) ? fileInfo : null; - } - } -} \ No newline at end of file diff --git a/Penumbra/Models/ModInfo.cs b/Penumbra/Models/ModInfo.cs new file mode 100644 index 00000000..cbf86482 --- /dev/null +++ b/Penumbra/Models/ModInfo.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using Penumbra.Mods; + +namespace Penumbra.Models +{ + public class ModInfo + { + public string FolderName { get; set; } + public bool Enabled { get; set; } + public int Priority { get; set; } + + [JsonIgnore] + public ResourceMod Mod { get; set; } + } +} \ No newline at end of file diff --git a/Penumbra/Mods/ModCollection.cs b/Penumbra/Mods/ModCollection.cs new file mode 100644 index 00000000..75dbcd26 --- /dev/null +++ b/Penumbra/Mods/ModCollection.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Dalamud.Plugin; +using Newtonsoft.Json; +using Penumbra.Models; + +namespace Penumbra.Mods +{ + public class ModCollection + { + private readonly DirectoryInfo _basePath; + + public List< ModInfo > ModSettings { get; set; } + public ResourceMod[] EnabledMods { get; set; } + + public ModCollection( DirectoryInfo basePath ) + { + _basePath = basePath; + } + + public void Load() + { + // find the collection json + var collectionPath = Path.Combine( _basePath.FullName, "collection.json" ); + if( File.Exists( collectionPath ) ) + { + try + { + ModSettings = JsonConvert.DeserializeObject< List< ModInfo > >( File.ReadAllText( collectionPath ) ); + ModSettings = ModSettings.OrderBy( x => x.Priority ).ToList(); + } + catch( Exception e ) + { + PluginLog.Error( $"failed to read log collection information, failed path: {collectionPath}, err: {e.Message}" ); + } + } + +#if DEBUG + if( ModSettings != null ) + { + foreach( var ms in ModSettings ) + { + PluginLog.Information( + "mod: {ModName} Enabled: {Enabled} Priority: {Priority}", + ms.FolderName, ms.Enabled, ms.Priority + ); + } + } +#endif + + ModSettings ??= new(); + var foundMods = new List< string >(); + + foreach( var modDir in _basePath.EnumerateDirectories() ) + { + var metaFile = modDir.EnumerateFiles().FirstOrDefault( f => f.Name == "meta.json" ); + + if( metaFile == null ) + { + PluginLog.LogError( "mod meta is missing for resource mod: {ResourceModLocation}", modDir ); + continue; + } + + var meta = JsonConvert.DeserializeObject< ModMeta >( File.ReadAllText( metaFile.FullName ) ); + + var mod = new ResourceMod + { + Meta = meta, + ModBasePath = modDir + }; + + var modEntry = FindOrCreateModSettings( mod ); + foundMods.Add( modDir.Name ); + mod.RefreshModFiles(); + } + + // remove any mods from the collection we didn't find + ModSettings = ModSettings.Where( + x => + foundMods.Any( + fm => string.Equals( x.FolderName, fm, StringComparison.InvariantCultureIgnoreCase ) + ) + ).ToList(); + + // reorder the resourcemods list so we can just directly iterate + EnabledMods = GetOrderedAndEnabledModList().ToArray(); + + // write the collection metadata back to disk + Save(); + } + + public void Save() + { + var collectionPath = Path.Combine( _basePath.FullName, "collection.json" ); + + try + { + var data = JsonConvert.SerializeObject( ModSettings.OrderBy( x => x.Priority ).ToList() ); + File.WriteAllText( collectionPath, data ); + } + catch( Exception e ) + { + PluginLog.Error( $"failed to write log collection information, failed path: {collectionPath}, err: {e.Message}" ); + } + } + + public void ReorderMod( ModInfo info, bool up ) + { + // todo: certified fucked tier + + var prio = info.Priority; + var swapPrio = up ? prio + 1 : prio - 1; + var swapMeta = ModSettings.FirstOrDefault( x => x.Priority == swapPrio ); + + if( swapMeta == null ) + { + return; + } + + info.Priority = swapPrio; + swapMeta.Priority = prio; + + // reorder mods list + ModSettings = ModSettings.OrderBy( x => x.Priority ).ToList(); + EnabledMods = GetOrderedAndEnabledModList().ToArray(); + + // save new prios + Save(); + } + + public ModInfo FindModSettings( string name ) + { + var settings = ModSettings.FirstOrDefault( + x => string.Equals( x.FolderName, name, StringComparison.InvariantCultureIgnoreCase ) + ); +#if DEBUG + PluginLog.Information( "finding mod {ModName} - found: {ModSettingsExist}", name, settings != null ); +#endif + return settings; + } + + public ModInfo AddModSettings( ResourceMod mod ) + { + var entry = new ModInfo + { + Priority = ModSettings.Count, + FolderName = mod.ModBasePath.Name, + Enabled = true, + Mod = mod + }; + +#if DEBUG + PluginLog.Information( "creating mod settings {ModName}", entry.FolderName ); +#endif + + ModSettings.Add( entry ); + return entry; + } + + public ModInfo FindOrCreateModSettings( ResourceMod mod ) + { + var settings = FindModSettings( mod.ModBasePath.Name ); + if( settings != null ) + { + settings.Mod = mod; + return settings; + } + + return AddModSettings( mod ); + } + + public IEnumerable< ResourceMod > GetOrderedAndEnabledModList() + { + return ModSettings + .Where( x => x.Enabled ) + .OrderBy( x => x.Priority ) + .Select( x => x.Mod ); + } + } +} \ No newline at end of file diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs new file mode 100644 index 00000000..ff7903c1 --- /dev/null +++ b/Penumbra/Mods/ModManager.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.IO; +using Penumbra.Models; + +namespace Penumbra.Mods +{ + public class ModManager + { + public readonly Dictionary< string, FileInfo > ResolvedFiles = new(); + + public ModCollection Mods { get; set; } + + public ResourceMod[] AvailableMods => Mods?.EnabledMods; + + private DirectoryInfo _basePath; + + public void DiscoverMods() + { + if( _basePath == null ) + { + return; + } + + DiscoverMods( _basePath ); + } + + public void DiscoverMods( string basePath ) + { + DiscoverMods( new DirectoryInfo( basePath ) ); + } + + public void DiscoverMods( DirectoryInfo basePath ) + { + if( basePath == null ) + { + return; + } + + if( !basePath.Exists ) + { + Directory.CreateDirectory( basePath.FullName ); + } + + _basePath = basePath; + + ResolvedFiles.Clear(); + + Mods = new ModCollection( basePath ); + Mods.Load(); + Mods.Save(); + + CalculateEffectiveFileList(); + } + + public void CalculateEffectiveFileList() + { + ResolvedFiles.Clear(); + + var registeredFiles = new Dictionary< string, string >(); + + foreach( var mod in Mods.GetOrderedAndEnabledModList() ) + { + mod.FileConflicts?.Clear(); + + // fixup path + var baseDir = mod.ModBasePath.FullName; + + foreach( var file in mod.ModFiles ) + { + var path = file.FullName.Substring( baseDir.Length ) + .TrimStart( '\\' ).Replace( '\\', '/' ); + + if( !ResolvedFiles.ContainsKey( path ) ) + { + ResolvedFiles[ path ] = file; + registeredFiles[ path ] = mod.Meta.Name; + } + else if( registeredFiles.TryGetValue( path, out var modName ) ) + { + mod.AddConflict( modName, path ); + } + } + } + } + + public void ChangeModPriority( ModInfo info, bool up = false ) + { + Mods.ReorderMod( info, up ); + CalculateEffectiveFileList(); + } + + public void DeleteMod( ResourceMod mod ) + { + Directory.Delete( mod.ModBasePath.FullName, true ); + DiscoverMods(); + } + + + public FileInfo GetCandidateForGameFile( string resourcePath ) + { + return ResolvedFiles.TryGetValue( resourcePath.ToLowerInvariant(), out var fileInfo ) ? fileInfo : null; + } + } +} \ No newline at end of file diff --git a/Penumbra/ResourceMod.cs b/Penumbra/Mods/ResourceMod.cs similarity index 59% rename from Penumbra/ResourceMod.cs rename to Penumbra/Mods/ResourceMod.cs index 872a710d..e6272904 100644 --- a/Penumbra/ResourceMod.cs +++ b/Penumbra/Mods/ResourceMod.cs @@ -1,10 +1,9 @@ -using System; using System.Collections.Generic; using System.IO; using Dalamud.Plugin; using Penumbra.Models; -namespace Penumbra +namespace Penumbra.Mods { public class ResourceMod { @@ -12,7 +11,9 @@ namespace Penumbra public DirectoryInfo ModBasePath { get; set; } - public List< FileInfo > ModFiles { get; } = new List< FileInfo >(); + public List< FileInfo > ModFiles { get; } = new(); + + public Dictionary< string, List< string > > FileConflicts { get; set; } = new(); public void RefreshModFiles() { @@ -31,5 +32,23 @@ namespace Penumbra } } } + + public void AddConflict( string modName, string path ) + { + if( FileConflicts.TryGetValue( modName, out var arr ) ) + { + if( !arr.Contains( path ) ) + { + arr.Add( path ); + } + + return; + } + + FileConflicts[ modName ] = new List< string > + { + path + }; + } } } \ No newline at end of file diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index b60e8597..6aa92233 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -1,56 +1,63 @@ - - net472 - latest - Penumbra - absolute gangstas - Penumbra - Copyright © 2020 - 1.0.0.0 - 1.0.0.0 - bin\$(Configuration)\ - true - - - full - DEBUG;TRACE - - - pdbonly - - - - ..\libs\Dalamud.dll - $(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll - $(DALAMUD_ROOT)\Dalamud.dll - False - - - ..\libs\ImGui.NET.dll - $(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll - $(DALAMUD_ROOT)\ImGui.NET.dll - False - - - ..\libs\ImGuiScene.dll - $(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll - $(DALAMUD_ROOT)\ImGuiScene.dll - False - - - ..\libs\Lumina.dll - $(AppData)\XIVLauncher\addon\Hooks\Lumina.dll - $(DALAMUD_ROOT)\Lumina.dll - False - - - - - - - - - - - + + net472 + latest + Penumbra + absolute gangstas + Penumbra + Copyright © 2020 + 1.0.0.0 + 1.0.0.0 + bin\$(Configuration)\ + true + + + + full + DEBUG;TRACE + + + + pdbonly + + + + + ..\libs\Dalamud.dll + $(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll + $(DALAMUD_ROOT)\Dalamud.dll + False + + + ..\libs\ImGui.NET.dll + $(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll + $(DALAMUD_ROOT)\ImGui.NET.dll + False + + + ..\libs\ImGuiScene.dll + $(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll + $(DALAMUD_ROOT)\ImGuiScene.dll + False + + + ..\libs\Lumina.dll + $(AppData)\XIVLauncher\addon\Hooks\Lumina.dll + $(DALAMUD_ROOT)\Lumina.dll + False + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Penumbra/Plugin.cs b/Penumbra/Plugin.cs index f6ab017c..f4f3bc17 100644 --- a/Penumbra/Plugin.cs +++ b/Penumbra/Plugin.cs @@ -1,6 +1,13 @@ +using System; +using System.Diagnostics; +using System.Drawing; using System.IO; +using System.Runtime.InteropServices; using Dalamud.Game.Command; using Dalamud.Plugin; +using Penumbra.Extensions; +using Penumbra.Mods; +using Penumbra.UI; namespace Penumbra { @@ -19,6 +26,8 @@ namespace Penumbra public SettingsInterface SettingsInterface { get; set; } + public string PluginDebugTitleStr { get; private set; } + public void Initialize( DalamudPluginInterface pluginInterface ) { PluginInterface = pluginInterface; @@ -26,8 +35,8 @@ namespace Penumbra Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); Configuration.Initialize( PluginInterface ); - ModManager = new ModManager( new DirectoryInfo( Configuration.BaseFolder ) ); - ModManager.DiscoverMods(); + ModManager = new ModManager(); + ModManager.DiscoverMods( Configuration.CurrentCollection ); ResourceLoader = new ResourceLoader( this ); @@ -39,9 +48,11 @@ namespace Penumbra ResourceLoader.Init(); ResourceLoader.Enable(); - + SettingsInterface = new SettingsInterface( this ); PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw; + + PluginDebugTitleStr = $"{Name} - Debug Build"; } public void Dispose() diff --git a/Penumbra/Structs/FileMode.cs b/Penumbra/Structs/FileMode.cs index 8003cc4e..4e9c1d6a 100644 --- a/Penumbra/Structs/FileMode.cs +++ b/Penumbra/Structs/FileMode.cs @@ -4,6 +4,8 @@ { LoadUnpackedResource = 0, LoadFileResource = 1, // Shit in My Games uses this - LoadSqpackResource = 0x0B + // some shit here, the game does some jump if its < 0xA for other files for some reason but there's no impl, probs debug? + LoadIndexResource = 0xA, // load index/index2 + LoadSqPackResource = 0xB } } \ No newline at end of file diff --git a/Penumbra/SettingsInterface.cs b/Penumbra/UI/SettingsInterface.cs similarity index 51% rename from Penumbra/SettingsInterface.cs rename to Penumbra/UI/SettingsInterface.cs index 3aed9516..3917a985 100644 --- a/Penumbra/SettingsInterface.cs +++ b/Penumbra/UI/SettingsInterface.cs @@ -3,15 +3,15 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; -using System.Runtime.Remoting.Messaging; using System.Threading.Tasks; using System.Windows.Forms; using Dalamud.Interface; using Dalamud.Plugin; using ImGuiNET; using Penumbra.Importer; +using Penumbra.Models; -namespace Penumbra +namespace Penumbra.UI { public class SettingsInterface { @@ -22,11 +22,14 @@ namespace Penumbra private static readonly Vector2 AutoFillSize = new Vector2( -1, -1 ); private static readonly Vector2 ModListSize = new Vector2( 200, -1 ); - private static readonly Vector2 MinSettingsSize = new Vector2( 650, 450 ); + private static readonly Vector2 MinSettingsSize = new Vector2( 800, 450 ); private static readonly Vector2 MaxSettingsSize = new Vector2( 69420, 42069 ); + private const string DialogDeleteMod = "PenumbraDeleteMod"; + private int _selectedModIndex; - private ResourceMod _selectedMod; + private int? _selectedModDeleteIndex; + private ModInfo _selectedMod; private bool _isImportRunning = false; @@ -38,7 +41,11 @@ namespace Penumbra public void Draw() { ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize ); +#if DEBUG + var ret = ImGui.Begin( _plugin.PluginDebugTitleStr ); +#else var ret = ImGui.Begin( _plugin.Name ); +#endif if( !ret ) { return; @@ -50,6 +57,8 @@ namespace Penumbra DrawResourceMods(); DrawEffectiveFileList(); + DrawDeleteModal(); + ImGui.EndTabBar(); ImGui.End(); @@ -64,17 +73,17 @@ namespace Penumbra } // FUCKKKKK - var basePath = _plugin.Configuration.BaseFolder; + var basePath = _plugin.Configuration.CurrentCollection; if( ImGui.InputText( "Root Folder", ref basePath, 255 ) ) { - _plugin.Configuration.BaseFolder = basePath; + _plugin.Configuration.CurrentCollection = basePath; } if( ImGui.Button( "Rediscover Mods" ) ) { ReloadMods(); } - + if( ImGui.Button( "Reload Player Resource" ) ) { _plugin.ResourceLoader.ReloadPlayerResource(); @@ -103,7 +112,7 @@ namespace Penumbra try { var importer = - new TexToolsImport( new DirectoryInfo( _plugin.Configuration.BaseFolder ) ); + new TexToolsImport( new DirectoryInfo( _plugin.Configuration.CurrentCollection ) ); foreach( var fileName in picker.FileNames ) { @@ -135,7 +144,7 @@ namespace Penumbra { _plugin.Configuration.Save(); } - + if( _plugin.ResourceLoader != null ) { ImGui.Checkbox( "DEBUG Log all loaded files", ref _plugin.ResourceLoader.LogAllFiles ); @@ -151,16 +160,45 @@ namespace Penumbra ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, new Vector2( 0, 0 ) ); // Inlay selector list - ImGui.BeginChild( "availableModList", new Vector2( 180, -ImGui.GetFrameHeightWithSpacing() ), true ); + ImGui.BeginChild( "availableModList", new Vector2( 240, -ImGui.GetFrameHeightWithSpacing() ), true ); - for( var modIndex = 0; modIndex < _plugin.ModManager.AvailableMods.Count; modIndex++ ) + if( _plugin.ModManager.Mods != null ) { - var mod = _plugin.ModManager.AvailableMods.ElementAt( modIndex ); - - if( ImGui.Selectable( mod.Value.Meta.Name, modIndex == _selectedModIndex ) ) + for( var modIndex = 0; modIndex < _plugin.ModManager.Mods.ModSettings.Count; modIndex++ ) { - _selectedModIndex = modIndex; - _selectedMod = mod.Value; + var settings = _plugin.ModManager.Mods.ModSettings[ modIndex ]; + + var changedColour = false; + if( !settings.Enabled ) + { + ImGui.PushStyleColor( ImGuiCol.Text, 0xFF666666 ); + changedColour = true; + } + else if( settings.Mod.FileConflicts.Any() ) + { + ImGui.PushStyleColor( ImGuiCol.Text, 0xFFAAAAFF ); + changedColour = true; + } + +#if DEBUG + var selected = ImGui.Selectable( + $"id={modIndex} {settings.Mod.Meta.Name}", + modIndex == _selectedModIndex + ); +#else + var selected = ImGui.Selectable( settings.Mod.Meta.Name, modIndex == _selectedModIndex ); +#endif + + if( changedColour ) + { + ImGui.PopStyleColor(); + } + + if( selected ) + { + _selectedModIndex = modIndex; + _selectedMod = settings; + } } } @@ -172,14 +210,16 @@ namespace Penumbra ImGui.PushFont( UiBuilder.IconFont ); if( _selectedModIndex != 0 ) { - if( ImGui.Button( FontAwesomeIcon.ArrowUp.ToIconString(), new Vector2( 45, 0 ) ) ) + if( ImGui.Button( FontAwesomeIcon.ArrowUp.ToIconString(), new Vector2( 60, 0 ) ) ) { + _plugin.ModManager.ChangeModPriority( _selectedMod ); + _selectedModIndex -= 1; } } else { ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f ); - ImGui.Button( FontAwesomeIcon.ArrowUp.ToIconString(), new Vector2( 45, 0 ) ); + ImGui.Button( FontAwesomeIcon.ArrowUp.ToIconString(), new Vector2( 60, 0 ) ); ImGui.PopStyleVar(); } @@ -192,16 +232,18 @@ namespace Penumbra ImGui.SameLine(); - if( _selectedModIndex != _plugin.ModManager.AvailableMods.Count - 1 ) + if( _selectedModIndex != _plugin.ModManager.Mods?.ModSettings.Count - 1 ) { - if( ImGui.Button( FontAwesomeIcon.ArrowDown.ToIconString(), new Vector2( 45, 0 ) ) ) + if( ImGui.Button( FontAwesomeIcon.ArrowDown.ToIconString(), new Vector2( 60, 0 ) ) ) { + _plugin.ModManager.ChangeModPriority( _selectedMod, true ); + _selectedModIndex += 1; } } else { ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f ); - ImGui.Button( FontAwesomeIcon.ArrowDown.ToIconString(), new Vector2( 45, 0 ) ); + ImGui.Button( FontAwesomeIcon.ArrowDown.ToIconString(), new Vector2( 60, 0 ) ); ImGui.PopStyleVar(); } @@ -215,8 +257,9 @@ namespace Penumbra ImGui.SameLine(); - if( ImGui.Button( FontAwesomeIcon.Trash.ToIconString(), new Vector2( 45, 0 ) ) ) + if( ImGui.Button( FontAwesomeIcon.Trash.ToIconString(), new Vector2( 60, 0 ) ) ) { + _selectedModDeleteIndex = _selectedModIndex; } ImGui.PopFont(); @@ -228,7 +271,7 @@ namespace Penumbra ImGui.SameLine(); - if( ImGui.Button( FontAwesomeIcon.Plus.ToIconString(), new Vector2( 45, 0 ) ) ) + if( ImGui.Button( FontAwesomeIcon.Plus.ToIconString(), new Vector2( 60, 0 ) ) ) { } @@ -242,14 +285,67 @@ namespace Penumbra ImGui.EndGroup(); } - void DrawResourceMods() + void DrawDeleteModal() { - var ret = ImGui.BeginTabItem( "Resource Mods" ); + if( _selectedModDeleteIndex != null ) + ImGui.OpenPopup( DialogDeleteMod ); + + var ret = ImGui.BeginPopupModal( DialogDeleteMod ); if( !ret ) { return; } + if( _selectedMod?.Mod == null ) + { + ImGui.CloseCurrentPopup(); + ImGui.EndPopup(); + } + + ImGui.Text( "Are you sure you want to delete the following mod:" ); + // todo: why the fuck does this become null?????? + ImGui.Text( _selectedMod?.Mod?.Meta?.Name ); + + if( ImGui.Button( "Yes, delete it" ) ) + { + ImGui.CloseCurrentPopup(); + _plugin.ModManager.DeleteMod( _selectedMod.Mod ); + _selectedMod = null; + _selectedModIndex = 0; + _selectedModDeleteIndex = null; + } + + ImGui.SameLine(); + + if( ImGui.Button( "No, keep it" ) ) + { + ImGui.CloseCurrentPopup(); + _selectedModDeleteIndex = null; + } + + ImGui.EndPopup(); + } + + void DrawResourceMods() + { + var ret = ImGui.BeginTabItem( "Mods" ); + if( !ret ) + { + return; + } + + if( _plugin.ModManager.Mods == null ) + { + ImGui.Text( "You don't have any mods :(" ); + ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 20 ); + ImGui.Text( "You'll need to install them first by creating a folder close to the root of your drive (preferably an SSD)." ); + ImGui.Text( "For example: D:/ffxiv/mods/" ); + ImGui.Text( "And pasting that path into the settings tab and clicking the 'Rediscover Mods' button." ); + ImGui.Text( "You can return to this tab once you've done that." ); + ImGui.EndTabItem(); + return; + } + DrawModsSelector(); ImGui.SameLine(); @@ -260,29 +356,78 @@ namespace Penumbra { ImGui.BeginChild( "selectedModInfo", AutoFillSize, true ); - ImGui.Text( _selectedMod.Meta.Name ); + ImGui.Text( _selectedMod.Mod.Meta.Name ); ImGui.SameLine(); ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "by" ); ImGui.SameLine(); - ImGui.Text( _selectedMod.Meta.Author ); + ImGui.Text( _selectedMod.Mod.Meta.Author ); - ImGui.TextWrapped( _selectedMod.Meta.Description ?? "" ); + ImGui.TextWrapped( _selectedMod.Mod.Meta.Description ?? "" ); - ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 12 ); + ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 10 ); - // list files - ImGui.Text( "Files:" ); - ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( "##", AutoFillSize ) ) + var enabled = _selectedMod.Enabled; + if( ImGui.Checkbox( "Enabled", ref enabled ) ) { - foreach( var file in _selectedMod.ModFiles ) + _selectedMod.Enabled = enabled; + _plugin.ModManager.Mods.Save(); + _plugin.ModManager.CalculateEffectiveFileList(); + } + + if( ImGui.Button( "Open Mod Folder" ) ) + { + Process.Start( _selectedMod.Mod.ModBasePath.FullName ); + } + + ImGui.BeginTabBar( "PenumbraPluginDetails" ); + if( ImGui.BeginTabItem( "Files" ) ) + { + ImGui.SetNextItemWidth( -1 ); + if( ImGui.ListBoxHeader( "##", AutoFillSize ) ) { - ImGui.Selectable( file.FullName ); + foreach( var file in _selectedMod.Mod.ModFiles ) + { + ImGui.Selectable( file.FullName ); + } + } + + ImGui.ListBoxFooter(); + ImGui.EndTabItem(); + } + + if( _selectedMod.Mod.FileConflicts.Any() ) + { + if( ImGui.BeginTabItem( "File Conflicts" ) ) + { + ImGui.SetNextItemWidth( -1 ); + if( ImGui.ListBoxHeader( "##", AutoFillSize ) ) + { + foreach( var kv in _selectedMod.Mod.FileConflicts ) + { + var mod = kv.Key; + var files = kv.Value; + + if( ImGui.Selectable( mod ) ) + { + SelectModByName( mod ); + } + + ImGui.Indent( 15 ); + foreach( var file in files ) + { + ImGui.Selectable( file ); + } + + ImGui.Unindent( 15 ); + } + } + + ImGui.ListBoxFooter(); + ImGui.EndTabItem(); } } - ImGui.ListBoxFooter(); - + ImGui.EndTabBar(); ImGui.EndChild(); } catch( Exception ex ) @@ -294,6 +439,23 @@ namespace Penumbra ImGui.EndTabItem(); } + void SelectModByName( string name ) + { + for( var modIndex = 0; modIndex < _plugin.ModManager.Mods.ModSettings.Count; modIndex++ ) + { + var mod = _plugin.ModManager.Mods.ModSettings[ modIndex ]; + + if( mod.Mod.Meta.Name != name ) + { + continue; + } + + _selectedMod = mod; + _selectedModIndex = modIndex; + return; + } + } + void DrawEffectiveFileList() { var ret = ImGui.BeginTabItem( "Effective File List" ); @@ -319,10 +481,7 @@ namespace Penumbra private void ReloadMods() { _selectedMod = null; - - // haha yikes - _plugin.ModManager = new ModManager( new DirectoryInfo( _plugin.Configuration.BaseFolder ) ); - _plugin.ModManager.DiscoverMods(); + _plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection ); } } } \ No newline at end of file