mod reordering, deleting, conflict resolution, some other fixes

This commit is contained in:
Adam 2020-12-25 22:50:31 +11:00
parent fbb39a8626
commit 7f1fd95a78
10 changed files with 605 additions and 203 deletions

View file

@ -1,6 +1,7 @@
using Dalamud.Configuration; using Dalamud.Configuration;
using Dalamud.Plugin; using Dalamud.Plugin;
using System; using System;
using System.Collections.Generic;
namespace Penumbra namespace Penumbra
{ {
@ -11,7 +12,9 @@ namespace Penumbra
public bool IsEnabled { get; set; } = true; 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 // the below exist just to make saving less cumbersome

View file

@ -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;
}
}
}

View file

@ -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; }
}
}

View file

@ -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 );
}
}
}

104
Penumbra/Mods/ModManager.cs Normal file
View file

@ -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;
}
}
}

View file

@ -1,10 +1,9 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Dalamud.Plugin; using Dalamud.Plugin;
using Penumbra.Models; using Penumbra.Models;
namespace Penumbra namespace Penumbra.Mods
{ {
public class ResourceMod public class ResourceMod
{ {
@ -12,7 +11,9 @@ namespace Penumbra
public DirectoryInfo ModBasePath { get; set; } 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() 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
};
}
} }
} }

View file

@ -1,56 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<AssemblyTitle>Penumbra</AssemblyTitle> <AssemblyTitle>Penumbra</AssemblyTitle>
<Company>absolute gangstas</Company> <Company>absolute gangstas</Company>
<Product>Penumbra</Product> <Product>Penumbra</Product>
<Copyright>Copyright © 2020</Copyright> <Copyright>Copyright © 2020</Copyright>
<FileVersion>1.0.0.0</FileVersion> <FileVersion>1.0.0.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion> <AssemblyVersion>1.0.0.0</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath> <OutputPath>bin\$(Configuration)\</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;TRACE</DefineConstants> <DebugType>full</DebugType>
</PropertyGroup> <DefineConstants>DEBUG;TRACE</DefineConstants>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup>
<DebugType>pdbonly</DebugType>
</PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<ItemGroup> <DebugType>pdbonly</DebugType>
<Reference Include="Dalamud"> </PropertyGroup>
<HintPath>..\libs\Dalamud.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath> <ItemGroup>
<HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath> <Reference Include="Dalamud">
<Private>False</Private> <HintPath>..\libs\Dalamud.dll</HintPath>
</Reference> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath>
<Reference Include="ImGui.NET"> <HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath>
<HintPath>..\libs\ImGui.NET.dll</HintPath> <Private>False</Private>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll</HintPath> </Reference>
<HintPath>$(DALAMUD_ROOT)\ImGui.NET.dll</HintPath> <Reference Include="ImGui.NET">
<Private>False</Private> <HintPath>..\libs\ImGui.NET.dll</HintPath>
</Reference> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll</HintPath>
<Reference Include="ImGuiScene"> <HintPath>$(DALAMUD_ROOT)\ImGui.NET.dll</HintPath>
<HintPath>..\libs\ImGuiScene.dll</HintPath> <Private>False</Private>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll</HintPath> </Reference>
<HintPath>$(DALAMUD_ROOT)\ImGuiScene.dll</HintPath> <Reference Include="ImGuiScene">
<Private>False</Private> <HintPath>..\libs\ImGuiScene.dll</HintPath>
</Reference> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll</HintPath>
<Reference Include="Lumina"> <HintPath>$(DALAMUD_ROOT)\ImGuiScene.dll</HintPath>
<HintPath>..\libs\Lumina.dll</HintPath> <Private>False</Private>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath> </Reference>
<HintPath>$(DALAMUD_ROOT)\Lumina.dll</HintPath> <Reference Include="Lumina">
<Private>False</Private> <HintPath>..\libs\Lumina.dll</HintPath>
</Reference> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath>
<Reference Include="System.Windows.Forms" /> <HintPath>$(DALAMUD_ROOT)\Lumina.dll</HintPath>
<Reference Include="System.Data.DataSetExtensions" /> <Private>False</Private>
<Reference Include="Microsoft.CSharp" /> </Reference>
<Reference Include="System.Net.Http" /> </ItemGroup>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DotNetZip" Version="1.13.8" /> <Reference Include="System.Windows.Forms" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <Reference Include="System.Data.DataSetExtensions" />
<PackageReference Include="Reloaded.Hooks" Version="2.4.1" /> <Reference Include="Microsoft.CSharp" />
</ItemGroup> <Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNetZip" Version="1.13.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Reloaded.Hooks" Version="2.4.1" />
</ItemGroup>
</Project> </Project>

View file

@ -1,6 +1,13 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Plugin; using Dalamud.Plugin;
using Penumbra.Extensions;
using Penumbra.Mods;
using Penumbra.UI;
namespace Penumbra namespace Penumbra
{ {
@ -19,6 +26,8 @@ namespace Penumbra
public SettingsInterface SettingsInterface { get; set; } public SettingsInterface SettingsInterface { get; set; }
public string PluginDebugTitleStr { get; private set; }
public void Initialize( DalamudPluginInterface pluginInterface ) public void Initialize( DalamudPluginInterface pluginInterface )
{ {
PluginInterface = pluginInterface; PluginInterface = pluginInterface;
@ -26,8 +35,8 @@ namespace Penumbra
Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); Configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
Configuration.Initialize( PluginInterface ); Configuration.Initialize( PluginInterface );
ModManager = new ModManager( new DirectoryInfo( Configuration.BaseFolder ) ); ModManager = new ModManager();
ModManager.DiscoverMods(); ModManager.DiscoverMods( Configuration.CurrentCollection );
ResourceLoader = new ResourceLoader( this ); ResourceLoader = new ResourceLoader( this );
@ -39,9 +48,11 @@ namespace Penumbra
ResourceLoader.Init(); ResourceLoader.Init();
ResourceLoader.Enable(); ResourceLoader.Enable();
SettingsInterface = new SettingsInterface( this ); SettingsInterface = new SettingsInterface( this );
PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw; PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw;
PluginDebugTitleStr = $"{Name} - Debug Build";
} }
public void Dispose() public void Dispose()

View file

@ -4,6 +4,8 @@
{ {
LoadUnpackedResource = 0, LoadUnpackedResource = 0,
LoadFileResource = 1, // Shit in My Games uses this 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
} }
} }

View file

@ -3,15 +3,15 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.Remoting.Messaging;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Plugin; using Dalamud.Plugin;
using ImGuiNET; using ImGuiNET;
using Penumbra.Importer; using Penumbra.Importer;
using Penumbra.Models;
namespace Penumbra namespace Penumbra.UI
{ {
public class SettingsInterface public class SettingsInterface
{ {
@ -22,11 +22,14 @@ namespace Penumbra
private static readonly Vector2 AutoFillSize = new Vector2( -1, -1 ); private static readonly Vector2 AutoFillSize = new Vector2( -1, -1 );
private static readonly Vector2 ModListSize = new Vector2( 200, -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 static readonly Vector2 MaxSettingsSize = new Vector2( 69420, 42069 );
private const string DialogDeleteMod = "PenumbraDeleteMod";
private int _selectedModIndex; private int _selectedModIndex;
private ResourceMod _selectedMod; private int? _selectedModDeleteIndex;
private ModInfo _selectedMod;
private bool _isImportRunning = false; private bool _isImportRunning = false;
@ -38,7 +41,11 @@ namespace Penumbra
public void Draw() public void Draw()
{ {
ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize ); ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize );
#if DEBUG
var ret = ImGui.Begin( _plugin.PluginDebugTitleStr );
#else
var ret = ImGui.Begin( _plugin.Name ); var ret = ImGui.Begin( _plugin.Name );
#endif
if( !ret ) if( !ret )
{ {
return; return;
@ -50,6 +57,8 @@ namespace Penumbra
DrawResourceMods(); DrawResourceMods();
DrawEffectiveFileList(); DrawEffectiveFileList();
DrawDeleteModal();
ImGui.EndTabBar(); ImGui.EndTabBar();
ImGui.End(); ImGui.End();
@ -64,17 +73,17 @@ namespace Penumbra
} }
// FUCKKKKK // FUCKKKKK
var basePath = _plugin.Configuration.BaseFolder; var basePath = _plugin.Configuration.CurrentCollection;
if( ImGui.InputText( "Root Folder", ref basePath, 255 ) ) if( ImGui.InputText( "Root Folder", ref basePath, 255 ) )
{ {
_plugin.Configuration.BaseFolder = basePath; _plugin.Configuration.CurrentCollection = basePath;
} }
if( ImGui.Button( "Rediscover Mods" ) ) if( ImGui.Button( "Rediscover Mods" ) )
{ {
ReloadMods(); ReloadMods();
} }
if( ImGui.Button( "Reload Player Resource" ) ) if( ImGui.Button( "Reload Player Resource" ) )
{ {
_plugin.ResourceLoader.ReloadPlayerResource(); _plugin.ResourceLoader.ReloadPlayerResource();
@ -103,7 +112,7 @@ namespace Penumbra
try try
{ {
var importer = var importer =
new TexToolsImport( new DirectoryInfo( _plugin.Configuration.BaseFolder ) ); new TexToolsImport( new DirectoryInfo( _plugin.Configuration.CurrentCollection ) );
foreach( var fileName in picker.FileNames ) foreach( var fileName in picker.FileNames )
{ {
@ -135,7 +144,7 @@ namespace Penumbra
{ {
_plugin.Configuration.Save(); _plugin.Configuration.Save();
} }
if( _plugin.ResourceLoader != null ) if( _plugin.ResourceLoader != null )
{ {
ImGui.Checkbox( "DEBUG Log all loaded files", ref _plugin.ResourceLoader.LogAllFiles ); 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 ) ); ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, new Vector2( 0, 0 ) );
// Inlay selector list // 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 ); for( var modIndex = 0; modIndex < _plugin.ModManager.Mods.ModSettings.Count; modIndex++ )
if( ImGui.Selectable( mod.Value.Meta.Name, modIndex == _selectedModIndex ) )
{ {
_selectedModIndex = modIndex; var settings = _plugin.ModManager.Mods.ModSettings[ modIndex ];
_selectedMod = mod.Value;
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 ); ImGui.PushFont( UiBuilder.IconFont );
if( _selectedModIndex != 0 ) 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 else
{ {
ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f ); 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(); ImGui.PopStyleVar();
} }
@ -192,16 +232,18 @@ namespace Penumbra
ImGui.SameLine(); 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 else
{ {
ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f ); 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(); ImGui.PopStyleVar();
} }
@ -215,8 +257,9 @@ namespace Penumbra
ImGui.SameLine(); 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(); ImGui.PopFont();
@ -228,7 +271,7 @@ namespace Penumbra
ImGui.SameLine(); 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(); 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 ) if( !ret )
{ {
return; 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(); DrawModsSelector();
ImGui.SameLine(); ImGui.SameLine();
@ -260,29 +356,78 @@ namespace Penumbra
{ {
ImGui.BeginChild( "selectedModInfo", AutoFillSize, true ); ImGui.BeginChild( "selectedModInfo", AutoFillSize, true );
ImGui.Text( _selectedMod.Meta.Name ); ImGui.Text( _selectedMod.Mod.Meta.Name );
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "by" ); ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "by" );
ImGui.SameLine(); 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 var enabled = _selectedMod.Enabled;
ImGui.Text( "Files:" ); if( ImGui.Checkbox( "Enabled", ref enabled ) )
ImGui.SetNextItemWidth( -1 );
if( ImGui.ListBoxHeader( "##", AutoFillSize ) )
{ {
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(); ImGui.EndChild();
} }
catch( Exception ex ) catch( Exception ex )
@ -294,6 +439,23 @@ namespace Penumbra
ImGui.EndTabItem(); 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() void DrawEffectiveFileList()
{ {
var ret = ImGui.BeginTabItem( "Effective File List" ); var ret = ImGui.BeginTabItem( "Effective File List" );
@ -319,10 +481,7 @@ namespace Penumbra
private void ReloadMods() private void ReloadMods()
{ {
_selectedMod = null; _selectedMod = null;
_plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection );
// haha yikes
_plugin.ModManager = new ModManager( new DirectoryInfo( _plugin.Configuration.BaseFolder ) );
_plugin.ModManager.DiscoverMods();
} }
} }
} }