mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Applied a slightly expanded .editorconfig to all files, checked the changes and did some simple refactoring-suggestions.
This commit is contained in:
parent
b307a787db
commit
801d9e24cf
38 changed files with 1438 additions and 1055 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
[*]
|
[*]
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
end_of_line=lf
|
end_of_line=lf
|
||||||
trim_trailing_whitespace=false
|
trim_trailing_whitespace=true
|
||||||
insert_final_newline=false
|
insert_final_newline=false
|
||||||
indent_style=space
|
indent_style=space
|
||||||
indent_size=4
|
indent_size=4
|
||||||
|
|
@ -10,6 +10,7 @@ indent_size=4
|
||||||
# Microsoft .NET properties
|
# Microsoft .NET properties
|
||||||
csharp_new_line_before_members_in_object_initializers=false
|
csharp_new_line_before_members_in_object_initializers=false
|
||||||
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||||
|
csharp_prefer_braces=true:none
|
||||||
csharp_space_after_cast=false
|
csharp_space_after_cast=false
|
||||||
csharp_space_after_keywords_in_control_flow_statements=false
|
csharp_space_after_keywords_in_control_flow_statements=false
|
||||||
csharp_space_between_method_call_parameter_list_parentheses=true
|
csharp_space_between_method_call_parameter_list_parentheses=true
|
||||||
|
|
@ -30,9 +31,26 @@ dotnet_style_qualification_for_property=false:suggestion
|
||||||
dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
|
dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
|
||||||
|
|
||||||
# ReSharper properties
|
# ReSharper properties
|
||||||
|
resharper_align_multiline_binary_expressions_chain=false
|
||||||
|
resharper_align_multiline_calls_chain=false
|
||||||
resharper_autodetect_indent_settings=true
|
resharper_autodetect_indent_settings=true
|
||||||
|
resharper_braces_redundant=true
|
||||||
|
resharper_constructor_or_destructor_body=expression_body
|
||||||
|
resharper_csharp_empty_block_style=together
|
||||||
|
resharper_csharp_max_line_length=144
|
||||||
resharper_csharp_space_within_array_access_brackets=true
|
resharper_csharp_space_within_array_access_brackets=true
|
||||||
resharper_enforce_line_ending_style=true
|
resharper_enforce_line_ending_style=true
|
||||||
|
resharper_int_align_assignments=true
|
||||||
|
resharper_int_align_comments=true
|
||||||
|
resharper_int_align_fields=true
|
||||||
|
resharper_int_align_invocations=false
|
||||||
|
resharper_int_align_nested_ternary=true
|
||||||
|
resharper_int_align_properties=false
|
||||||
|
resharper_int_align_switch_expressions=true
|
||||||
|
resharper_int_align_switch_sections=true
|
||||||
|
resharper_int_align_variables=true
|
||||||
|
resharper_local_function_body=expression_body
|
||||||
|
resharper_method_or_operator_body=expression_body
|
||||||
resharper_place_attribute_on_same_line=false
|
resharper_place_attribute_on_same_line=false
|
||||||
resharper_space_after_cast=false
|
resharper_space_after_cast=false
|
||||||
resharper_space_within_checked_parentheses=true
|
resharper_space_within_checked_parentheses=true
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,7 @@ namespace Penumbra.API
|
||||||
{
|
{
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
|
|
||||||
public ModsController( Plugin plugin )
|
public ModsController( Plugin plugin ) => _plugin = plugin;
|
||||||
{
|
|
||||||
_plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route( HttpVerbs.Get, "/mods" )]
|
[Route( HttpVerbs.Get, "/mods" )]
|
||||||
public object GetMods()
|
public object GetMods()
|
||||||
|
|
@ -24,7 +21,7 @@ namespace Penumbra.API
|
||||||
x.FolderName,
|
x.FolderName,
|
||||||
x.Mod.Meta,
|
x.Mod.Meta,
|
||||||
BasePath = x.Mod.ModBasePath.FullName,
|
BasePath = x.Mod.ModBasePath.FullName,
|
||||||
Files = x.Mod.ModFiles.Select( fi => fi.FullName )
|
Files = x.Mod.ModFiles.Select( fi => fi.FullName )
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Penumbra
|
||||||
public static Task< DialogResult > ShowDialogAsync( this CommonDialog form, IWin32Window owner )
|
public static Task< DialogResult > ShowDialogAsync( this CommonDialog form, IWin32Window owner )
|
||||||
{
|
{
|
||||||
var taskSource = new TaskCompletionSource< DialogResult >();
|
var taskSource = new TaskCompletionSource< DialogResult >();
|
||||||
var th = new Thread( () => DialogThread( form, owner, taskSource ) );
|
var th = new Thread( () => DialogThread( form, owner, taskSource ) );
|
||||||
th.Start();
|
th.Start();
|
||||||
return taskSource.Task;
|
return taskSource.Task;
|
||||||
}
|
}
|
||||||
|
|
@ -38,28 +38,25 @@ namespace Penumbra
|
||||||
{
|
{
|
||||||
public IntPtr Handle { get; set; }
|
public IntPtr Handle { get; set; }
|
||||||
|
|
||||||
public DialogHandle( IntPtr handle )
|
public DialogHandle( IntPtr handle ) => Handle = handle;
|
||||||
{
|
|
||||||
Handle = handle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HiddenForm : Form
|
public class HiddenForm : Form
|
||||||
{
|
{
|
||||||
private readonly CommonDialog _form;
|
private readonly CommonDialog _form;
|
||||||
private readonly IWin32Window _owner;
|
private readonly IWin32Window _owner;
|
||||||
private readonly TaskCompletionSource< DialogResult > _taskSource;
|
private readonly TaskCompletionSource< DialogResult > _taskSource;
|
||||||
|
|
||||||
public HiddenForm( CommonDialog form, IWin32Window owner, TaskCompletionSource< DialogResult > taskSource )
|
public HiddenForm( CommonDialog form, IWin32Window owner, TaskCompletionSource< DialogResult > taskSource )
|
||||||
{
|
{
|
||||||
this._form = form;
|
_form = form;
|
||||||
this._owner = owner;
|
_owner = owner;
|
||||||
this._taskSource = taskSource;
|
_taskSource = taskSource;
|
||||||
|
|
||||||
Opacity = 0;
|
Opacity = 0;
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
FormBorderStyle = FormBorderStyle.None;
|
||||||
ShowInTaskbar = false;
|
ShowInTaskbar = false;
|
||||||
Size = new Size( 0, 0 );
|
Size = new Size( 0, 0 );
|
||||||
|
|
||||||
Shown += HiddenForm_Shown;
|
Shown += HiddenForm_Shown;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ namespace Penumbra.Extensions
|
||||||
/// <typeparam name="TField">The type of the underlying field</typeparam>
|
/// <typeparam name="TField">The type of the underlying field</typeparam>
|
||||||
/// <returns>A delegate that will return a reference to a particular field - zero copy</returns>
|
/// <returns>A delegate that will return a reference to a particular field - zero copy</returns>
|
||||||
/// <exception cref="MissingFieldException"></exception>
|
/// <exception cref="MissingFieldException"></exception>
|
||||||
private static RefGet< TObject, TField > CreateRefGetter< TObject, TField >( string fieldName ) where TField : unmanaged
|
private static RefGet< TObject, TField > CreateRefGetter< TObject, TField >( string fieldName )
|
||||||
|
where TField : unmanaged
|
||||||
{
|
{
|
||||||
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ namespace Penumbra.Game
|
||||||
public unsafe delegate void* UnloadPlayerResourcesPrototype( IntPtr pResourceManager );
|
public unsafe delegate void* UnloadPlayerResourcesPrototype( IntPtr pResourceManager );
|
||||||
|
|
||||||
|
|
||||||
public LoadPlayerResourcesPrototype LoadPlayerResources { get; private set; }
|
public LoadPlayerResourcesPrototype LoadPlayerResources { get; }
|
||||||
public UnloadPlayerResourcesPrototype UnloadPlayerResources { get; private set; }
|
public UnloadPlayerResourcesPrototype UnloadPlayerResources { get; }
|
||||||
|
|
||||||
// Object addresses
|
// Object addresses
|
||||||
private readonly IntPtr _playerResourceManagerAddress;
|
private readonly IntPtr _playerResourceManagerAddress;
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Penumbra.Game
|
||||||
|
|
||||||
_playerResourceManagerAddress = scanner.GetStaticAddressFromSig( "0F 44 FE 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 05" );
|
_playerResourceManagerAddress = scanner.GetStaticAddressFromSig( "0F 44 FE 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 05" );
|
||||||
|
|
||||||
LoadPlayerResources = Marshal.GetDelegateForFunctionPointer< LoadPlayerResourcesPrototype >( loadPlayerResourcesAddress );
|
LoadPlayerResources = Marshal.GetDelegateForFunctionPointer< LoadPlayerResourcesPrototype >( loadPlayerResourcesAddress );
|
||||||
UnloadPlayerResources = Marshal.GetDelegateForFunctionPointer< UnloadPlayerResourcesPrototype >( unloadPlayerResourcesAddress );
|
UnloadPlayerResources = Marshal.GetDelegateForFunctionPointer< UnloadPlayerResourcesPrototype >( unloadPlayerResourcesAddress );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Game.ClientState.Actors;
|
using Dalamud.Game.ClientState.Actors;
|
||||||
using Dalamud.Game.ClientState.Actors.Types;
|
using Dalamud.Game.ClientState.Actors.Types;
|
||||||
|
|
||||||
namespace Penumbra
|
namespace Penumbra.Game
|
||||||
{
|
{
|
||||||
public static class RefreshActors
|
public static class RefreshActors
|
||||||
{
|
{
|
||||||
|
|
@ -12,43 +13,49 @@ namespace Penumbra
|
||||||
private const int RenderTaskOtherDelay = 25;
|
private const int RenderTaskOtherDelay = 25;
|
||||||
private const int ModelInvisibilityFlag = 0b10;
|
private const int ModelInvisibilityFlag = 0b10;
|
||||||
|
|
||||||
private static async void Redraw(Actor actor)
|
private static async void Redraw( Actor actor )
|
||||||
{
|
{
|
||||||
var ptr = actor.Address;
|
var ptr = actor.Address;
|
||||||
var renderModePtr = ptr + RenderModeOffset;
|
var renderModePtr = ptr + RenderModeOffset;
|
||||||
var renderStatus = Marshal.ReadInt32(renderModePtr);
|
var renderStatus = Marshal.ReadInt32( renderModePtr );
|
||||||
|
|
||||||
async void DrawObject(int delay)
|
async void DrawObject( int delay )
|
||||||
{
|
{
|
||||||
Marshal.WriteInt32(renderModePtr, renderStatus | ModelInvisibilityFlag);
|
Marshal.WriteInt32( renderModePtr, renderStatus | ModelInvisibilityFlag );
|
||||||
await Task.Delay(delay);
|
await Task.Delay( delay );
|
||||||
Marshal.WriteInt32(renderModePtr, renderStatus & ~ModelInvisibilityFlag);
|
Marshal.WriteInt32( renderModePtr, renderStatus & ~ModelInvisibilityFlag );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor.ObjectKind == Dalamud.Game.ClientState.Actors.ObjectKind.Player)
|
if( actor.ObjectKind == ObjectKind.Player )
|
||||||
{
|
{
|
||||||
DrawObject(RenderTaskPlayerDelay);
|
DrawObject( RenderTaskPlayerDelay );
|
||||||
await Task.Delay(RenderTaskPlayerDelay);
|
await Task.Delay( RenderTaskPlayerDelay );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
DrawObject(RenderTaskOtherDelay);
|
{
|
||||||
|
DrawObject( RenderTaskOtherDelay );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RedrawSpecific(ActorTable actors, string name)
|
public static void RedrawSpecific( ActorTable actors, string name )
|
||||||
{
|
{
|
||||||
if (name?.Length == 0)
|
if( name?.Length == 0 )
|
||||||
RedrawAll(actors);
|
{
|
||||||
|
RedrawAll( actors );
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var actor in actors)
|
foreach( var actor in actors.Where( A => A.Name == name ) )
|
||||||
if (actor.Name == name)
|
{
|
||||||
Redraw(actor);
|
Redraw( actor );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RedrawAll(ActorTable actors)
|
public static void RedrawAll( ActorTable actors )
|
||||||
{
|
{
|
||||||
foreach (var actor in actors)
|
foreach( var actor in actors )
|
||||||
Redraw(actor);
|
{
|
||||||
|
Redraw( actor );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,10 +8,7 @@ namespace Penumbra.Importer
|
||||||
{
|
{
|
||||||
private readonly FileStream _fileStream;
|
private readonly FileStream _fileStream;
|
||||||
|
|
||||||
public MagicTempFileStreamManagerAndDeleterFuckery( FileStream stream ) : base( stream )
|
public MagicTempFileStreamManagerAndDeleterFuckery( FileStream stream ) : base( stream ) => _fileStream = stream;
|
||||||
{
|
|
||||||
_fileStream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new void Dispose()
|
public new void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Penumbra.Importer
|
||||||
{
|
{
|
||||||
private readonly DirectoryInfo _outDirectory;
|
private readonly DirectoryInfo _outDirectory;
|
||||||
|
|
||||||
private const string TempFileName = "textools-import";
|
private const string TempFileName = "textools-import";
|
||||||
private readonly string _resolvedTempFilePath;
|
private readonly string _resolvedTempFilePath;
|
||||||
|
|
||||||
public ImporterState State { get; private set; }
|
public ImporterState State { get; private set; }
|
||||||
|
|
@ -42,7 +42,7 @@ namespace Penumbra.Importer
|
||||||
|
|
||||||
public TexToolsImport( DirectoryInfo outDirectory )
|
public TexToolsImport( DirectoryInfo outDirectory )
|
||||||
{
|
{
|
||||||
_outDirectory = outDirectory;
|
_outDirectory = outDirectory;
|
||||||
_resolvedTempFilePath = Path.Combine( _outDirectory.FullName, TempFileName );
|
_resolvedTempFilePath = Path.Combine( _outDirectory.FullName, TempFileName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Penumbra.Importer
|
||||||
{
|
{
|
||||||
CurrentModPack = modPackFile.Name;
|
CurrentModPack = modPackFile.Name;
|
||||||
|
|
||||||
VerifyVersionAndImport(modPackFile);
|
VerifyVersionAndImport( modPackFile );
|
||||||
|
|
||||||
State = ImporterState.Done;
|
State = ImporterState.Done;
|
||||||
}
|
}
|
||||||
|
|
@ -67,8 +67,8 @@ namespace Penumbra.Importer
|
||||||
State = ImporterState.WritingPackToDisk;
|
State = ImporterState.WritingPackToDisk;
|
||||||
|
|
||||||
// write shitty zip garbage to disk
|
// write shitty zip garbage to disk
|
||||||
var entry = file.GetEntry( entryName );
|
var entry = file.GetEntry( entryName );
|
||||||
using var s = file.GetInputStream( entry );
|
using var s = file.GetInputStream( entry );
|
||||||
|
|
||||||
WriteZipEntryToTempFile( s );
|
WriteZipEntryToTempFile( s );
|
||||||
|
|
||||||
|
|
@ -76,25 +76,31 @@ namespace Penumbra.Importer
|
||||||
return new MagicTempFileStreamManagerAndDeleterFuckery( fs );
|
return new MagicTempFileStreamManagerAndDeleterFuckery( fs );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VerifyVersionAndImport(FileInfo modPackFile)
|
private void VerifyVersionAndImport( FileInfo modPackFile )
|
||||||
{
|
{
|
||||||
using var zfs = modPackFile.OpenRead();
|
using var zfs = modPackFile.OpenRead();
|
||||||
using var extractedModPack = new ZipFile( zfs );
|
using var extractedModPack = new ZipFile( zfs );
|
||||||
var mpl = extractedModPack.GetEntry( "TTMPL.mpl" );
|
var mpl = extractedModPack.GetEntry( "TTMPL.mpl" );
|
||||||
var modRaw = GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 );
|
var modRaw = GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 );
|
||||||
|
|
||||||
// At least a better validation than going by the extension.
|
// At least a better validation than going by the extension.
|
||||||
if (modRaw.Contains("\"TTMPVersion\":"))
|
if( modRaw.Contains( "\"TTMPVersion\":" ) )
|
||||||
{
|
{
|
||||||
if (modPackFile.Extension != ".ttmp2")
|
if( modPackFile.Extension != ".ttmp2" )
|
||||||
PluginLog.Warning($"File {modPackFile.FullName} seems to be a V2 TTMP, but has the wrong extension.");
|
{
|
||||||
ImportV2ModPack(modPackFile, extractedModPack, modRaw);
|
PluginLog.Warning( $"File {modPackFile.FullName} seems to be a V2 TTMP, but has the wrong extension." );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportV2ModPack( modPackFile, extractedModPack, modRaw );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (modPackFile.Extension != ".ttmp")
|
if( modPackFile.Extension != ".ttmp" )
|
||||||
PluginLog.Warning($"File {modPackFile.FullName} seems to be a V1 TTMP, but has the wrong extension.");
|
{
|
||||||
ImportV1ModPack(modPackFile, extractedModPack, modRaw);
|
PluginLog.Warning( $"File {modPackFile.FullName} seems to be a V1 TTMP, but has the wrong extension." );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportV1ModPack( modPackFile, extractedModPack, modRaw );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,8 +118,8 @@ namespace Penumbra.Importer
|
||||||
// Create a new ModMeta from the TTMP modlist info
|
// Create a new ModMeta from the TTMP modlist info
|
||||||
var modMeta = new ModMeta
|
var modMeta = new ModMeta
|
||||||
{
|
{
|
||||||
Author = "Unknown",
|
Author = "Unknown",
|
||||||
Name = modPackFile.Name,
|
Name = modPackFile.Name,
|
||||||
Description = "Mod imported from TexTools mod pack"
|
Description = "Mod imported from TexTools mod pack"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -135,7 +141,7 @@ namespace Penumbra.Importer
|
||||||
ExtractSimpleModList( newModFolder, modList, modData );
|
ExtractSimpleModList( newModFolder, modList, modData );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImportV2ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw )
|
private void ImportV2ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw )
|
||||||
{
|
{
|
||||||
var modList = JsonConvert.DeserializeObject< SimpleModPack >( modRaw );
|
var modList = JsonConvert.DeserializeObject< SimpleModPack >( modRaw );
|
||||||
|
|
||||||
|
|
@ -159,7 +165,7 @@ namespace Penumbra.Importer
|
||||||
var modMeta = new ModMeta
|
var modMeta = new ModMeta
|
||||||
{
|
{
|
||||||
Author = modList.Author,
|
Author = modList.Author,
|
||||||
Name = modList.Name,
|
Name = modList.Name,
|
||||||
Description = string.IsNullOrEmpty( modList.Description )
|
Description = string.IsNullOrEmpty( modList.Description )
|
||||||
? "Mod imported from TexTools mod pack"
|
? "Mod imported from TexTools mod pack"
|
||||||
: modList.Description
|
: modList.Description
|
||||||
|
|
@ -188,7 +194,7 @@ namespace Penumbra.Importer
|
||||||
var modMeta = new ModMeta
|
var modMeta = new ModMeta
|
||||||
{
|
{
|
||||||
Author = modList.Author,
|
Author = modList.Author,
|
||||||
Name = modList.Name,
|
Name = modList.Name,
|
||||||
Description = string.IsNullOrEmpty( modList.Description )
|
Description = string.IsNullOrEmpty( modList.Description )
|
||||||
? "Mod imported from TexTools mod pack"
|
? "Mod imported from TexTools mod pack"
|
||||||
: modList.Description,
|
: modList.Description,
|
||||||
|
|
@ -206,24 +212,26 @@ namespace Penumbra.Importer
|
||||||
newModFolder.Create();
|
newModFolder.Create();
|
||||||
|
|
||||||
if( modList.SimpleModsList != null )
|
if( modList.SimpleModsList != null )
|
||||||
|
{
|
||||||
ExtractSimpleModList( newModFolder, modList.SimpleModsList, modData );
|
ExtractSimpleModList( newModFolder, modList.SimpleModsList, modData );
|
||||||
|
}
|
||||||
|
|
||||||
if( modList.ModPackPages == null )
|
if( modList.ModPackPages == null )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate through all pages
|
// Iterate through all pages
|
||||||
foreach( var page in modList.ModPackPages)
|
foreach( var group in modList.ModPackPages.SelectMany( page => page.ModGroups ) )
|
||||||
{
|
{
|
||||||
foreach(var group in page.ModGroups)
|
var groupFolder = new DirectoryInfo( Path.Combine( newModFolder.FullName, group.GroupName.ReplaceInvalidPathSymbols() ) );
|
||||||
|
foreach( var option in group.OptionList )
|
||||||
{
|
{
|
||||||
var groupFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, group.GroupName.ReplaceInvalidPathSymbols()));
|
var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name.ReplaceInvalidPathSymbols() ) );
|
||||||
foreach(var option in group.OptionList)
|
ExtractSimpleModList( optionFolder, option.ModsJsons, modData );
|
||||||
{
|
|
||||||
var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name.ReplaceInvalidPathSymbols()) );
|
|
||||||
ExtractSimpleModList( optionFolder, option.ModsJsons, modData );
|
|
||||||
}
|
|
||||||
AddMeta(newModFolder, groupFolder, group, modMeta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddMeta( newModFolder, groupFolder, group, modMeta );
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(
|
File.WriteAllText(
|
||||||
|
|
@ -232,32 +240,35 @@ namespace Penumbra.Importer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddMeta( DirectoryInfo baseFolder, DirectoryInfo groupFolder,ModGroup group, ModMeta meta)
|
private void AddMeta( DirectoryInfo baseFolder, DirectoryInfo groupFolder, ModGroup group, ModMeta meta )
|
||||||
{
|
{
|
||||||
var Inf = new InstallerInfo
|
var Inf = new InstallerInfo
|
||||||
{
|
{
|
||||||
SelectionType = group.SelectionType,
|
SelectionType = group.SelectionType,
|
||||||
GroupName = group.GroupName,
|
GroupName = group.GroupName,
|
||||||
Options = new List<Option>(),
|
Options = new List< Option >()
|
||||||
};
|
};
|
||||||
foreach( var opt in group.OptionList )
|
foreach( var opt in group.OptionList )
|
||||||
{
|
{
|
||||||
var optio = new Option
|
var option = new Option
|
||||||
{
|
{
|
||||||
OptionName = opt.Name,
|
OptionName = opt.Name,
|
||||||
OptionDesc = String.IsNullOrEmpty(opt.Description) ? "" : opt.Description,
|
OptionDesc = string.IsNullOrEmpty( opt.Description ) ? "" : opt.Description,
|
||||||
OptionFiles = new Dictionary<string, HashSet<string>>()
|
OptionFiles = new Dictionary< string, HashSet< string > >()
|
||||||
};
|
};
|
||||||
var optDir = new DirectoryInfo(Path.Combine( groupFolder.FullName, opt.Name.ReplaceInvalidPathSymbols()));
|
var optDir = new DirectoryInfo( Path.Combine( groupFolder.FullName, opt.Name.ReplaceInvalidPathSymbols() ) );
|
||||||
if (optDir.Exists)
|
if( !optDir.Exists )
|
||||||
{
|
{
|
||||||
foreach ( var file in optDir.EnumerateFiles("*.*", SearchOption.AllDirectories) )
|
foreach( var file in optDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
||||||
{
|
{
|
||||||
optio.AddFile(file.FullName.Substring(baseFolder.FullName.Length).TrimStart('\\'), file.FullName.Substring(optDir.FullName.Length).TrimStart('\\').Replace('\\','/'));
|
option.AddFile( file.FullName.Substring( baseFolder.FullName.Length ).TrimStart( '\\' ),
|
||||||
|
file.FullName.Substring( optDir.FullName.Length ).TrimStart( '\\' ).Replace( '\\', '/' ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Inf.Options.Add( optio );
|
|
||||||
|
Inf.Options.Add( option );
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.Groups.Add( group.GroupName, Inf );
|
meta.Groups.Add( group.GroupName, Inf );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,14 +287,8 @@ namespace Penumbra.Importer
|
||||||
TotalProgress += wtf.LongCount();
|
TotalProgress += wtf.LongCount();
|
||||||
|
|
||||||
// Extract each SimpleMod into the new mod folder
|
// Extract each SimpleMod into the new mod folder
|
||||||
foreach( var simpleMod in wtf )
|
foreach( var simpleMod in wtf.Where( M => M != null ) )
|
||||||
{
|
{
|
||||||
if( simpleMod == null )
|
|
||||||
{
|
|
||||||
// do we increment here too???? can this even happen?????
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtractMod( outDirectory, simpleMod, dataStream );
|
ExtractMod( outDirectory, simpleMod, dataStream );
|
||||||
CurrentProgress++;
|
CurrentProgress++;
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +306,9 @@ namespace Penumbra.Importer
|
||||||
extractedFile.Directory?.Create();
|
extractedFile.Directory?.Create();
|
||||||
|
|
||||||
if( extractedFile.FullName.EndsWith( "mdl" ) )
|
if( extractedFile.FullName.EndsWith( "mdl" ) )
|
||||||
|
{
|
||||||
ProcessMdl( data.Data );
|
ProcessMdl( data.Data );
|
||||||
|
}
|
||||||
|
|
||||||
File.WriteAllBytes( extractedFile.FullName, data.Data );
|
File.WriteAllBytes( extractedFile.FullName, data.Data );
|
||||||
}
|
}
|
||||||
|
|
@ -317,24 +324,21 @@ namespace Penumbra.Importer
|
||||||
mdl[ 64 ] = 1;
|
mdl[ 64 ] = 1;
|
||||||
|
|
||||||
// Model header LOD num
|
// Model header LOD num
|
||||||
var stackSize = BitConverter.ToUInt32( mdl, 4 );
|
var stackSize = BitConverter.ToUInt32( mdl, 4 );
|
||||||
var runtimeBegin = stackSize + 0x44;
|
var runtimeBegin = stackSize + 0x44;
|
||||||
var stringsLengthOffset = runtimeBegin + 4;
|
var stringsLengthOffset = runtimeBegin + 4;
|
||||||
var stringsLength = BitConverter.ToUInt32( mdl, ( int )stringsLengthOffset );
|
var stringsLength = BitConverter.ToUInt32( mdl, ( int )stringsLengthOffset );
|
||||||
var modelHeaderStart = stringsLengthOffset + stringsLength + 4;
|
var modelHeaderStart = stringsLengthOffset + stringsLength + 4;
|
||||||
var modelHeaderLodOffset = 22;
|
var modelHeaderLodOffset = 22;
|
||||||
mdl[ modelHeaderStart + modelHeaderLodOffset ] = 1;
|
mdl[ modelHeaderStart + modelHeaderLodOffset ] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream GetStreamFromZipEntry( ZipFile file, ZipEntry entry )
|
private static Stream GetStreamFromZipEntry( ZipFile file, ZipEntry entry ) => file.GetInputStream( entry );
|
||||||
{
|
|
||||||
return file.GetInputStream( entry );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetStringFromZipEntry( ZipFile file, ZipEntry entry, Encoding encoding )
|
private static string GetStringFromZipEntry( ZipFile file, ZipEntry entry, Encoding encoding )
|
||||||
{
|
{
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
using var s = GetStreamFromZipEntry( file, entry );
|
using var s = GetStreamFromZipEntry( file, entry );
|
||||||
s.CopyTo( ms );
|
s.CopyTo( ms );
|
||||||
return encoding.GetString( ms.ToArray() );
|
return encoding.GetString( ms.ToArray() );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,17 @@ namespace Penumbra.Models
|
||||||
private readonly DirectoryInfo _baseDir;
|
private readonly DirectoryInfo _baseDir;
|
||||||
private readonly int _baseDirLength;
|
private readonly int _baseDirLength;
|
||||||
private readonly ModMeta _mod;
|
private readonly ModMeta _mod;
|
||||||
private SHA256 _hasher = null;
|
private SHA256 _hasher;
|
||||||
|
|
||||||
private readonly Dictionary<long, List<FileInfo>> _filesBySize = new();
|
private readonly Dictionary< long, List< FileInfo > > _filesBySize = new();
|
||||||
|
|
||||||
private ref SHA256 Sha()
|
private ref SHA256 Sha()
|
||||||
{
|
{
|
||||||
if (_hasher == null)
|
_hasher ??= SHA256.Create();
|
||||||
_hasher = SHA256.Create();
|
|
||||||
return ref _hasher;
|
return ref _hasher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Deduplicator(DirectoryInfo baseDir, ModMeta mod)
|
public Deduplicator( DirectoryInfo baseDir, ModMeta mod )
|
||||||
{
|
{
|
||||||
_baseDir = baseDir;
|
_baseDir = baseDir;
|
||||||
_baseDirLength = baseDir.FullName.Length;
|
_baseDirLength = baseDir.FullName.Length;
|
||||||
|
|
@ -37,125 +36,134 @@ namespace Penumbra.Models
|
||||||
foreach( var file in _baseDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
foreach( var file in _baseDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
||||||
{
|
{
|
||||||
var fileLength = file.Length;
|
var fileLength = file.Length;
|
||||||
if (_filesBySize.TryGetValue(fileLength, out var files))
|
if( _filesBySize.TryGetValue( fileLength, out var files ) )
|
||||||
files.Add(file);
|
{
|
||||||
|
files.Add( file );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
_filesBySize[fileLength] = new(){ file };
|
{
|
||||||
|
_filesBySize[ fileLength ] = new List< FileInfo >() { file };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
foreach (var pair in _filesBySize)
|
foreach( var pair in _filesBySize.Where( pair => pair.Value.Count >= 2 ) )
|
||||||
{
|
{
|
||||||
if (pair.Value.Count < 2)
|
if( pair.Value.Count == 2 )
|
||||||
continue;
|
|
||||||
|
|
||||||
if (pair.Value.Count == 2)
|
|
||||||
{
|
{
|
||||||
if (CompareFilesDirectly(pair.Value[0], pair.Value[1]))
|
if( CompareFilesDirectly( pair.Value[ 0 ], pair.Value[ 1 ] ) )
|
||||||
ReplaceFile(pair.Value[0], pair.Value[1]);
|
{
|
||||||
|
ReplaceFile( pair.Value[ 0 ], pair.Value[ 1 ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var deleted = Enumerable.Repeat(false, pair.Value.Count).ToArray();
|
var deleted = Enumerable.Repeat( false, pair.Value.Count ).ToArray();
|
||||||
var hashes = pair.Value.Select( F => ComputeHash(F)).ToArray();
|
var hashes = pair.Value.Select( ComputeHash ).ToArray();
|
||||||
|
|
||||||
for (var i = 0; i < pair.Value.Count; ++i)
|
for( var i = 0; i < pair.Value.Count; ++i )
|
||||||
{
|
{
|
||||||
if (deleted[i])
|
if( deleted[ i ] )
|
||||||
continue;
|
|
||||||
|
|
||||||
for (var j = i + 1; j < pair.Value.Count; ++j)
|
|
||||||
{
|
{
|
||||||
if (deleted[j])
|
continue;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
if (!CompareHashes(hashes[i], hashes[j]))
|
for( var j = i + 1; j < pair.Value.Count; ++j )
|
||||||
|
{
|
||||||
|
if( deleted[ j ] || !CompareHashes( hashes[ i ], hashes[ j ] ) )
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ReplaceFile(pair.Value[i], pair.Value[j]);
|
ReplaceFile( pair.Value[ i ], pair.Value[ j ] );
|
||||||
deleted[j] = true;
|
deleted[ j ] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClearEmptySubDirectories(_baseDir);
|
|
||||||
|
ClearEmptySubDirectories( _baseDir );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReplaceFile(FileInfo f1, FileInfo f2)
|
private void ReplaceFile( FileInfo f1, FileInfo f2 )
|
||||||
{
|
{
|
||||||
var relName1 = f1.FullName.Substring(_baseDirLength).TrimStart('\\');
|
var relName1 = f1.FullName.Substring( _baseDirLength ).TrimStart( '\\' );
|
||||||
var relName2 = f2.FullName.Substring(_baseDirLength).TrimStart('\\');
|
var relName2 = f2.FullName.Substring( _baseDirLength ).TrimStart( '\\' );
|
||||||
|
|
||||||
var inOption = false;
|
var inOption = false;
|
||||||
foreach (var group in _mod.Groups.Select( g => g.Value.Options))
|
foreach( var group in _mod.Groups.Select( g => g.Value.Options ) )
|
||||||
{
|
{
|
||||||
foreach (var option in group)
|
foreach( var option in group )
|
||||||
{
|
{
|
||||||
if (option.OptionFiles.TryGetValue(relName2, out var values))
|
if( option.OptionFiles.TryGetValue( relName2, out var values ) )
|
||||||
{
|
{
|
||||||
inOption = true;
|
inOption = true;
|
||||||
foreach (var value in values)
|
foreach( var value in values )
|
||||||
option.AddFile(relName1, value);
|
{
|
||||||
option.OptionFiles.Remove(relName2);
|
option.AddFile( relName1, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
option.OptionFiles.Remove( relName2 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!inOption)
|
|
||||||
|
if( !inOption )
|
||||||
{
|
{
|
||||||
const string duplicates = "Duplicates";
|
const string duplicates = "Duplicates";
|
||||||
if (!_mod.Groups.ContainsKey(duplicates))
|
if( !_mod.Groups.ContainsKey( duplicates ) )
|
||||||
{
|
{
|
||||||
InstallerInfo info = new()
|
InstallerInfo info = new()
|
||||||
{
|
{
|
||||||
GroupName = duplicates,
|
GroupName = duplicates,
|
||||||
SelectionType = SelectType.Single,
|
SelectionType = SelectType.Single,
|
||||||
Options = new()
|
Options = new List< Option >()
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
OptionName = "Required",
|
OptionName = "Required",
|
||||||
OptionDesc = "",
|
OptionDesc = "",
|
||||||
OptionFiles = new()
|
OptionFiles = new Dictionary< string, HashSet< string > >()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
_mod.Groups.Add(duplicates, info);
|
_mod.Groups.Add( duplicates, info );
|
||||||
}
|
}
|
||||||
_mod.Groups[duplicates].Options[0].AddFile(relName1, relName2.Replace('\\', '/'));
|
|
||||||
_mod.Groups[duplicates].Options[0].AddFile(relName1, relName1.Replace('\\', '/'));
|
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName2.Replace( '\\', '/' ) );
|
||||||
|
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName1.Replace( '\\', '/' ) );
|
||||||
}
|
}
|
||||||
PluginLog.Information($"File {relName1} and {relName2} are identical. Deleting the second.");
|
|
||||||
|
PluginLog.Information( $"File {relName1} and {relName2} are identical. Deleting the second." );
|
||||||
f2.Delete();
|
f2.Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CompareFilesDirectly(FileInfo f1, FileInfo f2)
|
public static bool CompareFilesDirectly( FileInfo f1, FileInfo f2 )
|
||||||
{
|
=> File.ReadAllBytes( f1.FullName ).SequenceEqual( File.ReadAllBytes( f2.FullName ) );
|
||||||
return File.ReadAllBytes(f1.FullName).SequenceEqual(File.ReadAllBytes(f2.FullName));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CompareHashes(byte[] f1, byte[] f2)
|
public static bool CompareHashes( byte[] f1, byte[] f2 )
|
||||||
{
|
=> StructuralComparisons.StructuralEqualityComparer.Equals( f1, f2 );
|
||||||
return StructuralComparisons.StructuralEqualityComparer.Equals(f1, f2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ComputeHash(FileInfo f)
|
public byte[] ComputeHash( FileInfo f )
|
||||||
{
|
{
|
||||||
var stream = File.OpenRead( f.FullName );
|
var stream = File.OpenRead( f.FullName );
|
||||||
var ret = Sha().ComputeHash(stream);
|
var ret = Sha().ComputeHash( stream );
|
||||||
stream.Dispose();
|
stream.Dispose();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not delete the base directory itself even if it is completely empty at the end.
|
// Does not delete the base directory itself even if it is completely empty at the end.
|
||||||
public static void ClearEmptySubDirectories(DirectoryInfo baseDir)
|
public static void ClearEmptySubDirectories( DirectoryInfo baseDir )
|
||||||
{
|
{
|
||||||
foreach (var subDir in baseDir.GetDirectories())
|
foreach( var subDir in baseDir.GetDirectories() )
|
||||||
{
|
{
|
||||||
ClearEmptySubDirectories(subDir);
|
ClearEmptySubDirectories( subDir );
|
||||||
if (subDir.GetFiles().Length == 0 && subDir.GetDirectories().Length == 0)
|
if( subDir.GetFiles().Length == 0 && subDir.GetDirectories().Length == 0 )
|
||||||
|
{
|
||||||
subDir.Delete();
|
subDir.Delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,30 +5,37 @@ namespace Penumbra.Models
|
||||||
{
|
{
|
||||||
public enum SelectType
|
public enum SelectType
|
||||||
{
|
{
|
||||||
Single, Multi
|
Single,
|
||||||
|
Multi
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Option
|
public struct Option
|
||||||
{
|
{
|
||||||
public string OptionName;
|
public string OptionName;
|
||||||
public string OptionDesc;
|
public string OptionDesc;
|
||||||
|
|
||||||
[JsonProperty(ItemConverterType = typeof(SingleOrArrayConverter<string>))]
|
[JsonProperty( ItemConverterType = typeof( SingleOrArrayConverter< string > ) )]
|
||||||
public Dictionary<string, HashSet<string>> OptionFiles;
|
public Dictionary< string, HashSet< string > > OptionFiles;
|
||||||
|
|
||||||
public bool AddFile(string filePath, string gamePath)
|
public bool AddFile( string filePath, string gamePath )
|
||||||
{
|
{
|
||||||
if (OptionFiles.TryGetValue(filePath, out var set))
|
if( OptionFiles.TryGetValue( filePath, out var set ) )
|
||||||
return set.Add(gamePath);
|
{
|
||||||
else
|
return set.Add( gamePath );
|
||||||
OptionFiles[filePath] = new(){ gamePath };
|
}
|
||||||
|
|
||||||
|
OptionFiles[ filePath ] = new HashSet< string >() { gamePath };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct InstallerInfo {
|
public struct InstallerInfo
|
||||||
|
{
|
||||||
public string GroupName;
|
public string GroupName;
|
||||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
|
||||||
|
[JsonConverter( typeof( Newtonsoft.Json.Converters.StringEnumConverter ) )]
|
||||||
public SelectType SelectionType;
|
public SelectType SelectionType;
|
||||||
public List<Option> Options;
|
|
||||||
|
public List< Option > Options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,9 +7,9 @@ namespace Penumbra.Models
|
||||||
public class ModInfo
|
public class ModInfo
|
||||||
{
|
{
|
||||||
public string FolderName { get; set; }
|
public string FolderName { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public int Priority { get; set; }
|
public int Priority { get; set; }
|
||||||
public Dictionary<string, int> Conf {get;set;}
|
public Dictionary< string, int > Conf { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public ResourceMod Mod { get; set; }
|
public ResourceMod Mod { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -21,21 +21,24 @@ namespace Penumbra.Models
|
||||||
|
|
||||||
public Dictionary< string, string > FileSwaps { get; } = new();
|
public Dictionary< string, string > FileSwaps { get; } = new();
|
||||||
|
|
||||||
public Dictionary<string, InstallerInfo> Groups { get; set; } = new();
|
public Dictionary< string, InstallerInfo > Groups { get; set; } = new();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool HasGroupWithConfig { get; set; } = false;
|
public bool HasGroupWithConfig { get; set; } = false;
|
||||||
|
|
||||||
public static ModMeta LoadFromFile(string filePath)
|
public static ModMeta LoadFromFile( string filePath )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var meta = JsonConvert.DeserializeObject< ModMeta >( File.ReadAllText( filePath ) );
|
var meta = JsonConvert.DeserializeObject< ModMeta >( File.ReadAllText( filePath ) );
|
||||||
meta.HasGroupWithConfig = meta.Groups != null && meta.Groups.Count > 0
|
meta.HasGroupWithConfig =
|
||||||
&& meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1);
|
meta.Groups != null
|
||||||
|
&& meta.Groups.Count > 0
|
||||||
|
&& meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1 );
|
||||||
|
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
catch( Exception)
|
catch( Exception )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
// todo: handle broken mods properly
|
// todo: handle broken mods properly
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,7 @@ namespace Penumbra.Mods
|
||||||
public ResourceMod[] EnabledMods { get; set; }
|
public ResourceMod[] EnabledMods { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public ModCollection( DirectoryInfo basePath )
|
public ModCollection( DirectoryInfo basePath ) => _basePath = basePath;
|
||||||
{
|
|
||||||
_basePath = basePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load( bool invertOrder = false )
|
public void Load( bool invertOrder = false )
|
||||||
{
|
{
|
||||||
|
|
@ -51,7 +48,7 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ModSettings ??= new();
|
ModSettings ??= new List< ModInfo >();
|
||||||
var foundMods = new List< string >();
|
var foundMods = new List< string >();
|
||||||
|
|
||||||
foreach( var modDir in _basePath.EnumerateDirectories() )
|
foreach( var modDir in _basePath.EnumerateDirectories() )
|
||||||
|
|
@ -73,7 +70,7 @@ namespace Penumbra.Mods
|
||||||
|
|
||||||
var mod = new ResourceMod
|
var mod = new ResourceMod
|
||||||
{
|
{
|
||||||
Meta = meta,
|
Meta = meta,
|
||||||
ModBasePath = modDir
|
ModBasePath = modDir
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -124,7 +121,7 @@ namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
// todo: certified fucked tier
|
// todo: certified fucked tier
|
||||||
|
|
||||||
var prio = info.Priority;
|
var prio = info.Priority;
|
||||||
var swapPrio = up ? prio + 1 : prio - 1;
|
var swapPrio = up ? prio + 1 : prio - 1;
|
||||||
var swapMeta = ModSettings.FirstOrDefault( x => x.Priority == swapPrio );
|
var swapMeta = ModSettings.FirstOrDefault( x => x.Priority == swapPrio );
|
||||||
|
|
||||||
|
|
@ -133,7 +130,7 @@ namespace Penumbra.Mods
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.Priority = swapPrio;
|
info.Priority = swapPrio;
|
||||||
swapMeta.Priority = prio;
|
swapMeta.Priority = prio;
|
||||||
|
|
||||||
// reorder mods list
|
// reorder mods list
|
||||||
|
|
@ -160,10 +157,10 @@ namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
var entry = new ModInfo
|
var entry = new ModInfo
|
||||||
{
|
{
|
||||||
Priority = ModSettings.Count,
|
Priority = ModSettings.Count,
|
||||||
FolderName = mod.ModBasePath.Name,
|
FolderName = mod.ModBasePath.Name,
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
Mod = mod
|
Mod = mod
|
||||||
};
|
};
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,15 @@ namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
public class ModManager : IDisposable
|
public class ModManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
|
public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
|
||||||
public readonly Dictionary< string, string > SwappedFiles = new();
|
public readonly Dictionary< string, string > SwappedFiles = new();
|
||||||
|
|
||||||
public ModCollection Mods { get; set; }
|
public ModCollection Mods { get; set; }
|
||||||
|
|
||||||
private DirectoryInfo _basePath;
|
private DirectoryInfo _basePath;
|
||||||
|
|
||||||
public ModManager( Plugin plugin )
|
public ModManager( Plugin plugin ) => _plugin = plugin;
|
||||||
{
|
|
||||||
_plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DiscoverMods()
|
public void DiscoverMods()
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +111,7 @@ namespace Penumbra.Mods
|
||||||
mod.FileConflicts?.Clear();
|
mod.FileConflicts?.Clear();
|
||||||
if( settings.Conf == null )
|
if( settings.Conf == null )
|
||||||
{
|
{
|
||||||
settings.Conf = new();
|
settings.Conf = new Dictionary< string, int >();
|
||||||
_plugin.ModManager.Mods.Save();
|
_plugin.ModManager.Mods.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +135,7 @@ namespace Penumbra.Mods
|
||||||
if( !SwappedFiles.ContainsKey( swap.Value ) )
|
if( !SwappedFiles.ContainsKey( swap.Value ) )
|
||||||
{
|
{
|
||||||
SwappedFiles[ swap.Key.ToLowerInvariant() ] = swap.Value;
|
SwappedFiles[ swap.Key.ToLowerInvariant() ] = swap.Value;
|
||||||
registeredFiles[ swap.Key ] = mod.Meta.Name;
|
registeredFiles[ swap.Key ] = mod.Meta.Name;
|
||||||
}
|
}
|
||||||
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) )
|
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) )
|
||||||
{
|
{
|
||||||
|
|
@ -155,13 +152,14 @@ namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' );
|
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' );
|
||||||
|
|
||||||
bool doNotAdd = false;
|
var doNotAdd = false;
|
||||||
|
|
||||||
HashSet< string > paths;
|
HashSet< string > paths;
|
||||||
foreach( var group in mod.Meta.Groups.Select( G => G.Value ) )
|
foreach( var group in mod.Meta.Groups.Select( G => G.Value ) )
|
||||||
{
|
{
|
||||||
if( !settings.Conf.TryGetValue( group.GroupName, out var setting )
|
if( !settings.Conf.TryGetValue( group.GroupName, out var setting )
|
||||||
|| ( group.SelectionType == SelectType.Single && settings.Conf[ group.GroupName ] >= group.Options.Count ) )
|
|| group.SelectionType == SelectType.Single
|
||||||
|
&& settings.Conf[ group.GroupName ] >= group.Options.Count )
|
||||||
{
|
{
|
||||||
settings.Conf[ group.GroupName ] = 0;
|
settings.Conf[ group.GroupName ] = 0;
|
||||||
_plugin.ModManager.Mods.Save();
|
_plugin.ModManager.Mods.Save();
|
||||||
|
|
@ -169,10 +167,14 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
|
|
||||||
if( group.Options.Count == 0 )
|
if( group.Options.Count == 0 )
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if( group.SelectionType == SelectType.Multi )
|
if( group.SelectionType == SelectType.Multi )
|
||||||
settings.Conf[ group.GroupName ] &= ( ( 1 << group.Options.Count ) - 1 );
|
{
|
||||||
|
settings.Conf[ group.GroupName ] &= ( 1 << group.Options.Count ) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
switch( group.SelectionType )
|
switch( group.SelectionType )
|
||||||
{
|
{
|
||||||
|
|
@ -183,15 +185,10 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for( var i = 0; i < group.Options.Count; ++i )
|
if( group.Options.Where( ( o, i ) => i != setting )
|
||||||
|
.Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) ) )
|
||||||
{
|
{
|
||||||
if( i == setting )
|
doNotAdd = true;
|
||||||
continue;
|
|
||||||
if( group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath ) )
|
|
||||||
{
|
|
||||||
doNotAdd = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +204,9 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath ) )
|
else if( group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath ) )
|
||||||
|
{
|
||||||
doNotAdd = true;
|
doNotAdd = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -216,7 +215,7 @@ namespace Penumbra.Mods
|
||||||
|
|
||||||
if( !doNotAdd )
|
if( !doNotAdd )
|
||||||
{
|
{
|
||||||
AddFiles( new() { relativeFilePath.Replace( '\\', '/' ) }, out doNotAdd, file, registeredFiles, mod );
|
AddFiles( new HashSet< string > { relativeFilePath.Replace( '\\', '/' ) }, out doNotAdd, file, registeredFiles, mod );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +229,7 @@ namespace Penumbra.Mods
|
||||||
if( !ResolvedFiles.ContainsKey( gamePath ) )
|
if( !ResolvedFiles.ContainsKey( gamePath ) )
|
||||||
{
|
{
|
||||||
ResolvedFiles[ gamePath.ToLowerInvariant() ] = file;
|
ResolvedFiles[ gamePath.ToLowerInvariant() ] = file;
|
||||||
registeredFiles[ gamePath ] = mod.Meta.Name;
|
registeredFiles[ gamePath ] = mod.Meta.Name;
|
||||||
}
|
}
|
||||||
else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
|
else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
|
||||||
{
|
{
|
||||||
|
|
@ -247,17 +246,18 @@ namespace Penumbra.Mods
|
||||||
|
|
||||||
public void DeleteMod( ResourceMod mod )
|
public void DeleteMod( ResourceMod mod )
|
||||||
{
|
{
|
||||||
if (mod?.ModBasePath?.Exists ?? false)
|
if( mod?.ModBasePath?.Exists ?? false )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.Delete(mod.ModBasePath.FullName, true);
|
Directory.Delete( mod.ModBasePath.FullName, true );
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception e )
|
||||||
{
|
{
|
||||||
PluginLog.Error($"Could not delete the mod {mod.ModBasePath.Name}:\n{e}");
|
PluginLog.Error( $"Could not delete the mod {mod.ModBasePath.Name}:\n{e}" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscoverMods();
|
DiscoverMods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,9 +278,7 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetSwappedFilePath( string gameResourcePath )
|
public string GetSwappedFilePath( string gameResourcePath )
|
||||||
{
|
=> SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
|
||||||
return SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ResolveSwappedOrReplacementFilePath( string gameResourcePath )
|
public string ResolveSwappedOrReplacementFilePath( string gameResourcePath )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ namespace Penumbra
|
||||||
GameUtils.ReloadPlayerResources();
|
GameUtils.ReloadPlayerResources();
|
||||||
|
|
||||||
SettingsInterface = new SettingsInterface( this );
|
SettingsInterface = new SettingsInterface( this );
|
||||||
|
|
||||||
PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw;
|
PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw;
|
||||||
|
|
||||||
PluginDebugTitleStr = $"{Name} - Debug Build";
|
PluginDebugTitleStr = $"{Name} - Debug Build";
|
||||||
|
|
@ -122,10 +123,15 @@ namespace Penumbra
|
||||||
}
|
}
|
||||||
case "redraw":
|
case "redraw":
|
||||||
{
|
{
|
||||||
if (args.Length > 1)
|
if( args.Length > 1 )
|
||||||
RefreshActors.RedrawSpecific(PluginInterface.ClientState.Actors, string.Join(" ", args.Skip(1)));
|
{
|
||||||
|
RefreshActors.RedrawSpecific( PluginInterface.ClientState.Actors, string.Join( " ", args.Skip( 1 ) ) );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
RefreshActors.RedrawAll(PluginInterface.ClientState.Actors);
|
{
|
||||||
|
RefreshActors.RedrawAll( PluginInterface.ClientState.Actors );
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ namespace Penumbra
|
||||||
public ResourceLoader( Plugin plugin )
|
public ResourceLoader( Plugin plugin )
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
Crc32 = new Crc32();
|
Crc32 = new Crc32();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Init()
|
public unsafe void Init()
|
||||||
|
|
@ -71,8 +71,8 @@ namespace Penumbra
|
||||||
scanner.ScanText( "E8 ?? ?? ?? 00 48 8B D8 EB ?? F0 FF 83 ?? ?? 00 00" );
|
scanner.ScanText( "E8 ?? ?? ?? 00 48 8B D8 EB ?? F0 FF 83 ?? ?? 00 00" );
|
||||||
|
|
||||||
|
|
||||||
ReadSqpackHook = new Hook< ReadSqpackPrototype >( ReadSqpackHandler, ( long )readSqpackAddress );
|
ReadSqpackHook = new Hook< ReadSqpackPrototype >( ReadSqpackHandler, ( long )readSqpackAddress );
|
||||||
GetResourceSyncHook = new Hook< GetResourceSyncPrototype >( GetResourceSyncHandler, ( long )getResourceSyncAddress );
|
GetResourceSyncHook = new Hook< GetResourceSyncPrototype >( GetResourceSyncHandler, ( long )getResourceSyncAddress );
|
||||||
GetResourceAsyncHook = new Hook< GetResourceAsyncPrototype >( GetResourceAsyncHandler, ( long )getResourceAsyncAddress );
|
GetResourceAsyncHook = new Hook< GetResourceAsyncPrototype >( GetResourceAsyncHandler, ( long )getResourceAsyncAddress );
|
||||||
|
|
||||||
ReadFile = Marshal.GetDelegateForFunctionPointer< ReadFilePrototype >( readFileAddress );
|
ReadFile = Marshal.GetDelegateForFunctionPointer< ReadFilePrototype >( readFileAddress );
|
||||||
|
|
@ -129,7 +129,7 @@ namespace Penumbra
|
||||||
PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath );
|
PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Plugin.Configuration.IsEnabled)
|
if( Plugin.Configuration.IsEnabled )
|
||||||
{
|
{
|
||||||
var replacementPath = Plugin.ModManager.ResolveSwappedOrReplacementFilePath( gameFsPath );
|
var replacementPath = Plugin.ModManager.ResolveSwappedOrReplacementFilePath( gameFsPath );
|
||||||
|
|
||||||
|
|
@ -140,7 +140,7 @@ namespace Penumbra
|
||||||
}
|
}
|
||||||
|
|
||||||
var cleanPath = replacementPath.Replace( '\\', '/' );
|
var cleanPath = replacementPath.Replace( '\\', '/' );
|
||||||
var path = Encoding.ASCII.GetBytes( cleanPath );
|
var path = Encoding.ASCII.GetBytes( cleanPath );
|
||||||
|
|
||||||
var bPath = stackalloc byte[path.Length + 1];
|
var bPath = stackalloc byte[path.Length + 1];
|
||||||
Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length );
|
Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length );
|
||||||
|
|
@ -192,7 +192,9 @@ namespace Penumbra
|
||||||
public void Enable()
|
public void Enable()
|
||||||
{
|
{
|
||||||
if( IsEnabled )
|
if( IsEnabled )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ReadSqpackHook.Activate();
|
ReadSqpackHook.Activate();
|
||||||
GetResourceSyncHook.Activate();
|
GetResourceSyncHook.Activate();
|
||||||
|
|
@ -208,7 +210,9 @@ namespace Penumbra
|
||||||
public void Disable()
|
public void Disable()
|
||||||
{
|
{
|
||||||
if( !IsEnabled )
|
if( !IsEnabled )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ReadSqpackHook.Disable();
|
ReadSqpackHook.Disable();
|
||||||
GetResourceSyncHook.Disable();
|
GetResourceSyncHook.Disable();
|
||||||
|
|
@ -220,7 +224,9 @@ namespace Penumbra
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if( IsEnabled )
|
if( IsEnabled )
|
||||||
|
{
|
||||||
Disable();
|
Disable();
|
||||||
|
}
|
||||||
|
|
||||||
// ReadSqpackHook.Disable();
|
// ReadSqpackHook.Disable();
|
||||||
// GetResourceSyncHook.Disable();
|
// GetResourceSyncHook.Disable();
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ namespace Penumbra.Structs
|
||||||
public enum FileMode : uint
|
public enum FileMode : uint
|
||||||
{
|
{
|
||||||
LoadUnpackedResource = 0,
|
LoadUnpackedResource = 0,
|
||||||
LoadFileResource = 1, // Shit in My Games uses this
|
LoadFileResource = 1, // Shit in My Games uses this
|
||||||
|
|
||||||
// some shit here, the game does some jump if its < 0xA for other files for some reason but there's no impl, probs debug?
|
// 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
|
LoadIndexResource = 0xA, // load index/index2
|
||||||
LoadSqPackResource = 0xB
|
LoadSqPackResource = 0xB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,124 +7,134 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
public static partial class ImGuiCustom
|
public static partial class ImGuiCustom
|
||||||
{
|
{
|
||||||
public static void BeginFramedGroup(string label) => BeginFramedGroupInternal(ref label, ZeroVector, false);
|
public static void BeginFramedGroup( string label ) => BeginFramedGroupInternal( ref label, ZeroVector, false );
|
||||||
public static void BeginFramedGroup(string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, false);
|
public static void BeginFramedGroup( string label, Vector2 minSize ) => BeginFramedGroupInternal( ref label, minSize, false );
|
||||||
|
|
||||||
public static bool BeginFramedGroupEdit(ref string label) => BeginFramedGroupInternal(ref label, ZeroVector, true);
|
public static bool BeginFramedGroupEdit( ref string label ) => BeginFramedGroupInternal( ref label, ZeroVector, true );
|
||||||
public static bool BeginFramedGroupEdit(ref string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, true);
|
public static bool BeginFramedGroupEdit( ref string label, Vector2 minSize ) => BeginFramedGroupInternal( ref label, minSize, true );
|
||||||
|
|
||||||
private static bool BeginFramedGroupInternal(ref string label, Vector2 minSize, bool edit)
|
private static bool BeginFramedGroupInternal( ref string label, Vector2 minSize, bool edit )
|
||||||
{
|
{
|
||||||
var itemSpacing = ImGui.GetStyle().ItemSpacing;
|
var itemSpacing = ImGui.GetStyle().ItemSpacing;
|
||||||
var frameHeight = ImGui.GetFrameHeight();
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
var halfFrameHeight = new Vector2(ImGui.GetFrameHeight() / 2, 0);
|
var halfFrameHeight = new Vector2( ImGui.GetFrameHeight() / 2, 0 );
|
||||||
|
|
||||||
ImGui.BeginGroup(); // First group
|
ImGui.BeginGroup(); // First group
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector);
|
ImGui.PushStyleVar( ImGuiStyleVar.FramePadding, ZeroVector );
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
|
ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, ZeroVector );
|
||||||
|
|
||||||
ImGui.BeginGroup(); // Second group
|
ImGui.BeginGroup(); // Second group
|
||||||
|
|
||||||
var effectiveSize = minSize;
|
var effectiveSize = minSize;
|
||||||
if (effectiveSize.X < 0)
|
if( effectiveSize.X < 0 )
|
||||||
|
{
|
||||||
effectiveSize.X = ImGui.GetContentRegionAvail().X;
|
effectiveSize.X = ImGui.GetContentRegionAvail().X;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure width.
|
// Ensure width.
|
||||||
ImGui.Dummy(new(effectiveSize.X, 0));
|
ImGui.Dummy( new Vector2( effectiveSize.X, 0 ) );
|
||||||
// Ensure left half boundary width/distance.
|
// Ensure left half boundary width/distance.
|
||||||
ImGui.Dummy(halfFrameHeight);
|
ImGui.Dummy( halfFrameHeight );
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.BeginGroup(); // Third group.
|
ImGui.BeginGroup(); // Third group.
|
||||||
// Ensure right half of boundary width/distance
|
// Ensure right half of boundary width/distance
|
||||||
ImGui.Dummy(halfFrameHeight);
|
ImGui.Dummy( halfFrameHeight );
|
||||||
|
|
||||||
// Label block
|
// Label block
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var ret = false;
|
var ret = false;
|
||||||
if (edit)
|
if( edit )
|
||||||
ret = ImGuiCustom.ResizingTextInput(ref label, 1024);
|
{
|
||||||
|
ret = ResizingTextInput( ref label, 1024 );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ImGui.TextUnformatted(label);
|
{
|
||||||
|
ImGui.TextUnformatted( label );
|
||||||
|
}
|
||||||
|
|
||||||
var labelMin = ImGui.GetItemRectMin();
|
var labelMin = ImGui.GetItemRectMin();
|
||||||
var labelMax = ImGui.GetItemRectMax();
|
var labelMax = ImGui.GetItemRectMax();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
// Ensure height and distance to label.
|
// Ensure height and distance to label.
|
||||||
ImGui.Dummy(new Vector2(0, frameHeight + itemSpacing.Y));
|
ImGui.Dummy( new Vector2( 0, frameHeight + itemSpacing.Y ) );
|
||||||
|
|
||||||
ImGui.BeginGroup(); // Fourth Group.
|
ImGui.BeginGroup(); // Fourth Group.
|
||||||
|
|
||||||
ImGui.PopStyleVar(2);
|
ImGui.PopStyleVar( 2 );
|
||||||
|
|
||||||
ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y));
|
ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y ) );
|
||||||
|
|
||||||
var itemWidth = ImGui.CalcItemWidth();
|
var itemWidth = ImGui.CalcItemWidth();
|
||||||
ImGui.PushItemWidth(Math.Max(0f, itemWidth - frameHeight));
|
ImGui.PushItemWidth( Math.Max( 0f, itemWidth - frameHeight ) );
|
||||||
|
|
||||||
labelStack.Add((labelMin, labelMax));
|
labelStack.Add( ( labelMin, labelMax ) );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawClippedRect(Vector2 clipMin, Vector2 clipMax, Vector2 drawMin, Vector2 drawMax, uint color, float thickness)
|
private static void DrawClippedRect( Vector2 clipMin, Vector2 clipMax, Vector2 drawMin, Vector2 drawMax, uint color, float thickness )
|
||||||
{
|
{
|
||||||
ImGui.PushClipRect(clipMin, clipMax, true);
|
ImGui.PushClipRect( clipMin, clipMax, true );
|
||||||
ImGui.GetWindowDrawList().AddRect(drawMin, drawMax, color, thickness);
|
ImGui.GetWindowDrawList().AddRect( drawMin, drawMax, color, thickness );
|
||||||
ImGui.PopClipRect();
|
ImGui.PopClipRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EndFramedGroup()
|
public static void EndFramedGroup()
|
||||||
{
|
{
|
||||||
uint borderColor = ImGui.ColorConvertFloat4ToU32(ImGui.GetStyle().Colors[(int)ImGuiCol.Border]);
|
var borderColor = ImGui.ColorConvertFloat4ToU32( ImGui.GetStyle().Colors[ ( int )ImGuiCol.Border ] );
|
||||||
Vector2 itemSpacing = ImGui.GetStyle().ItemSpacing;
|
var itemSpacing = ImGui.GetStyle().ItemSpacing;
|
||||||
float frameHeight = ImGui.GetFrameHeight();
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
Vector2 halfFrameHeight = new(ImGui.GetFrameHeight() / 2, 0);
|
var halfFrameHeight = new Vector2( ImGui.GetFrameHeight() / 2, 0 );
|
||||||
|
|
||||||
ImGui.PopItemWidth();
|
ImGui.PopItemWidth();
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector);
|
ImGui.PushStyleVar( ImGuiStyleVar.FramePadding, ZeroVector );
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
|
ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, ZeroVector );
|
||||||
|
|
||||||
ImGui.EndGroup(); // Close fourth group
|
ImGui.EndGroup(); // Close fourth group
|
||||||
ImGui.EndGroup(); // Close third group
|
ImGui.EndGroup(); // Close third group
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
// Ensure right distance.
|
// Ensure right distance.
|
||||||
ImGui.Dummy(halfFrameHeight);
|
ImGui.Dummy( halfFrameHeight );
|
||||||
// Ensure bottom distance
|
// Ensure bottom distance
|
||||||
ImGui.Dummy(new Vector2(0, frameHeight/2 - itemSpacing.Y));
|
ImGui.Dummy( new Vector2( 0, frameHeight / 2 - itemSpacing.Y ) );
|
||||||
ImGui.EndGroup(); // Close second group
|
ImGui.EndGroup(); // Close second group
|
||||||
|
|
||||||
var itemMin = ImGui.GetItemRectMin();
|
var itemMin = ImGui.GetItemRectMin();
|
||||||
var itemMax = ImGui.GetItemRectMax();
|
var itemMax = ImGui.GetItemRectMax();
|
||||||
var (currentLabelMin, currentLabelMax) = labelStack[labelStack.Count - 1];
|
var (currentLabelMin, currentLabelMax) = labelStack[ labelStack.Count - 1 ];
|
||||||
labelStack.RemoveAt(labelStack.Count - 1);
|
labelStack.RemoveAt( labelStack.Count - 1 );
|
||||||
|
|
||||||
var halfFrame = new Vector2(frameHeight / 8, frameHeight / 2);
|
var halfFrame = new Vector2( frameHeight / 8, frameHeight / 2 );
|
||||||
currentLabelMin.X -= itemSpacing.X;
|
currentLabelMin.X -= itemSpacing.X;
|
||||||
currentLabelMax.X += itemSpacing.X;
|
currentLabelMax.X += itemSpacing.X;
|
||||||
var frameMin = itemMin + halfFrame;
|
var frameMin = itemMin + halfFrame;
|
||||||
var frameMax = itemMax - new Vector2(halfFrame.X, 0);
|
var frameMax = itemMax - new Vector2( halfFrame.X, 0 );
|
||||||
|
|
||||||
// Left
|
// Left
|
||||||
DrawClippedRect(new(-float.MaxValue , -float.MaxValue ), new(currentLabelMin.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
|
DrawClippedRect( new Vector2( -float.MaxValue, -float.MaxValue ), new Vector2( currentLabelMin.X, float.MaxValue ), frameMin,
|
||||||
|
frameMax, borderColor, halfFrame.X );
|
||||||
// Right
|
// Right
|
||||||
DrawClippedRect(new(currentLabelMax.X, -float.MaxValue ), new(float.MaxValue , float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
|
DrawClippedRect( new Vector2( currentLabelMax.X, -float.MaxValue ), new Vector2( float.MaxValue, float.MaxValue ), frameMin,
|
||||||
|
frameMax, borderColor, halfFrame.X );
|
||||||
// Top
|
// Top
|
||||||
DrawClippedRect(new(currentLabelMin.X, -float.MaxValue ), new(currentLabelMax.X, currentLabelMin.Y), frameMin, frameMax, borderColor, halfFrame.X);
|
DrawClippedRect( new Vector2( currentLabelMin.X, -float.MaxValue ), new Vector2( currentLabelMax.X, currentLabelMin.Y ), frameMin,
|
||||||
|
frameMax, borderColor, halfFrame.X );
|
||||||
// Bottom
|
// Bottom
|
||||||
DrawClippedRect(new(currentLabelMin.X, currentLabelMax.Y), new(currentLabelMax.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
|
DrawClippedRect( new Vector2( currentLabelMin.X, currentLabelMax.Y ), new Vector2( currentLabelMax.X, float.MaxValue ), frameMin,
|
||||||
|
frameMax, borderColor, halfFrame.X );
|
||||||
|
|
||||||
ImGui.PopStyleVar(2);
|
ImGui.PopStyleVar( 2 );
|
||||||
ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y));
|
ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y ) );
|
||||||
ImGui.Dummy(ZeroVector);
|
ImGui.Dummy( ZeroVector );
|
||||||
|
|
||||||
ImGui.EndGroup(); // Close first group
|
ImGui.EndGroup(); // Close first group
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Vector2 ZeroVector = new(0, 0);
|
private static readonly Vector2 ZeroVector = new( 0, 0 );
|
||||||
|
|
||||||
private static readonly List<(Vector2, Vector2)> labelStack = new();
|
private static readonly List< (Vector2, Vector2) > labelStack = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,39 +4,50 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
public static partial class ImGuiCustom
|
public static partial class ImGuiCustom
|
||||||
{
|
{
|
||||||
public static bool RenameableCombo(string label, ref int currentItem, ref string newName, string[] items, int numItems)
|
public static bool RenameableCombo( string label, ref int currentItem, out string newName, string[] items, int numItems )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
newName = "";
|
newName = "";
|
||||||
var newOption = "";
|
var newOption = "";
|
||||||
if (ImGui.BeginCombo(label, (numItems > 0) ? items[currentItem] : newOption))
|
if( !ImGui.BeginCombo( label, numItems > 0 ? items[ currentItem ] : newOption ) )
|
||||||
{
|
{
|
||||||
for (var i = 0; i < numItems; ++i)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( var i = 0; i < numItems; ++i )
|
||||||
|
{
|
||||||
|
var isSelected = i == currentItem;
|
||||||
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.InputText( $"##{label}_{i}", ref items[ i ], 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
{
|
{
|
||||||
var isSelected = i == currentItem;
|
currentItem = i;
|
||||||
ImGui.SetNextItemWidth(-1);
|
newName = items[ i ];
|
||||||
if (ImGui.InputText($"##{label}_{i}", ref items[i], 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
ret = true;
|
||||||
{
|
|
||||||
currentItem = i;
|
|
||||||
newName = items[i];
|
|
||||||
ret = true;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
if (isSelected)
|
|
||||||
ImGui.SetItemDefaultFocus();
|
|
||||||
}
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
if (ImGui.InputText($"##{label}_new", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
|
||||||
{
|
|
||||||
currentItem = numItems;
|
|
||||||
newName = newOption;
|
|
||||||
ret = true;
|
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
if (numItems == 0)
|
|
||||||
|
if( isSelected )
|
||||||
|
{
|
||||||
ImGui.SetItemDefaultFocus();
|
ImGui.SetItemDefaultFocus();
|
||||||
ImGui.EndCombo();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.InputText( $"##{label}_new", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
|
{
|
||||||
|
currentItem = numItems;
|
||||||
|
newName = newOption;
|
||||||
|
ret = true;
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( numItems == 0 )
|
||||||
|
{
|
||||||
|
ImGui.SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndCombo();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,36 +5,45 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
public static partial class ImGuiCustom
|
public static partial class ImGuiCustom
|
||||||
{
|
{
|
||||||
public static bool InputOrText(bool editable, string label, ref string text, uint maxLength)
|
public static bool InputOrText( bool editable, string label, ref string text, uint maxLength )
|
||||||
{
|
{
|
||||||
if (editable)
|
if( editable )
|
||||||
return ResizingTextInput(label, ref text, maxLength);
|
{
|
||||||
|
return ResizingTextInput( label, ref text, maxLength );
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Text(text);
|
ImGui.Text( text );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool ResizingTextInput(string label, ref string input, uint maxLength) => ResizingTextInputIntern(label, ref input, maxLength).Item1;
|
public static bool ResizingTextInput( string label, ref string input, uint maxLength ) =>
|
||||||
public static bool ResizingTextInput(ref string input, uint maxLength)
|
ResizingTextInputIntern( label, ref input, maxLength ).Item1;
|
||||||
|
|
||||||
|
public static bool ResizingTextInput( ref string input, uint maxLength )
|
||||||
{
|
{
|
||||||
var (ret, id) = ResizingTextInputIntern($"##{input}", ref input, maxLength);
|
var (ret, id) = ResizingTextInputIntern( $"##{input}", ref input, maxLength );
|
||||||
if (ret)
|
if( ret )
|
||||||
_textInputWidths.Remove(id);
|
{
|
||||||
|
TextInputWidths.Remove( id );
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (bool, uint) ResizingTextInputIntern(string label, ref string input, uint maxLength)
|
private static (bool, uint) ResizingTextInputIntern( string label, ref string input, uint maxLength )
|
||||||
{
|
{
|
||||||
var id = ImGui.GetID(label);
|
var id = ImGui.GetID( label );
|
||||||
if (!_textInputWidths.TryGetValue(id, out var width))
|
if( !TextInputWidths.TryGetValue( id, out var width ) )
|
||||||
width = ImGui.CalcTextSize(input).X + 10;
|
{
|
||||||
|
width = ImGui.CalcTextSize( input ).X + 10;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(width);
|
ImGui.SetNextItemWidth( width );
|
||||||
var ret = ImGui.InputText(label, ref input, maxLength, ImGuiInputTextFlags.EnterReturnsTrue);
|
var ret = ImGui.InputText( label, ref input, maxLength, ImGuiInputTextFlags.EnterReturnsTrue );
|
||||||
_textInputWidths[id] = ImGui.CalcTextSize(input).X + 10;
|
TextInputWidths[ id ] = ImGui.CalcTextSize( input ).X + 10;
|
||||||
return (ret, id);
|
return ( ret, id );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<uint, float> _textInputWidths = new();
|
private static readonly Dictionary< uint, float > TextInputWidths = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,23 +4,22 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
public static partial class ImGuiCustom
|
public static partial class ImGuiCustom
|
||||||
{
|
{
|
||||||
public static void VerticalDistance(float distance)
|
public static void VerticalDistance( float distance )
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + distance);
|
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + distance );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RightJustifiedText(float pos, string text)
|
public static void RightJustifiedText( float pos, string text )
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - 2 * ImGui.GetStyle().ItemSpacing.X);
|
ImGui.SetCursorPosX( pos - ImGui.CalcTextSize( text ).X - 2 * ImGui.GetStyle().ItemSpacing.X );
|
||||||
ImGui.Text(text);
|
ImGui.Text( text );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RightJustifiedLabel(float pos, string text)
|
public static void RightJustifiedLabel( float pos, string text )
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - ImGui.GetStyle().ItemSpacing.X / 2);
|
ImGui.SetCursorPosX( pos - ImGui.CalcTextSize( text ).X - ImGui.GetStyle().ItemSpacing.X / 2 );
|
||||||
ImGui.Text(text);
|
ImGui.Text( text );
|
||||||
ImGui.SameLine(pos);
|
ImGui.SameLine( pos );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,16 +8,17 @@ namespace Penumbra.UI
|
||||||
private class LaunchButton
|
private class LaunchButton
|
||||||
{
|
{
|
||||||
// magic numbers
|
// magic numbers
|
||||||
private const int Padding = 50;
|
private const int Padding = 50;
|
||||||
private const int Width = 200;
|
private const int Width = 200;
|
||||||
private const int Height = 45;
|
private const int Height = 45;
|
||||||
private const string MenuButtonsName = "Penumbra Menu Buttons";
|
private const string MenuButtonsName = "Penumbra Menu Buttons";
|
||||||
private const string MenuButtonLabel = "Manage Mods";
|
private const string MenuButtonLabel = "Manage Mods";
|
||||||
|
|
||||||
private static readonly Vector2 WindowSize = new(Width, Height);
|
private static readonly Vector2 WindowSize = new( Width, Height );
|
||||||
private static readonly Vector2 WindowPosOffset = new(Padding + Width, Padding + Height);
|
private static readonly Vector2 WindowPosOffset = new( Padding + Width, Padding + Height );
|
||||||
|
|
||||||
private readonly ImGuiWindowFlags ButtonFlags = ImGuiWindowFlags.AlwaysAutoResize
|
private const ImGuiWindowFlags ButtonFlags =
|
||||||
|
ImGuiWindowFlags.AlwaysAutoResize
|
||||||
| ImGuiWindowFlags.NoBackground
|
| ImGuiWindowFlags.NoBackground
|
||||||
| ImGuiWindowFlags.NoDecoration
|
| ImGuiWindowFlags.NoDecoration
|
||||||
| ImGuiWindowFlags.NoMove
|
| ImGuiWindowFlags.NoMove
|
||||||
|
|
@ -28,7 +29,7 @@ namespace Penumbra.UI
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
private readonly Dalamud.Game.ClientState.Condition _condition;
|
private readonly Dalamud.Game.ClientState.Condition _condition;
|
||||||
|
|
||||||
public LaunchButton(SettingsInterface ui)
|
public LaunchButton( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_condition = ui._plugin.PluginInterface.ClientState.Condition;
|
_condition = ui._plugin.PluginInterface.ClientState.Condition;
|
||||||
|
|
@ -36,20 +37,26 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if( !_condition.Any() && !_base._menu.Visible )
|
if( _condition.Any() || _base._menu.Visible )
|
||||||
{
|
{
|
||||||
var ss = ImGui.GetIO().DisplaySize;
|
return;
|
||||||
|
|
||||||
ImGui.SetNextWindowPos( ss - WindowPosOffset, ImGuiCond.Always );
|
|
||||||
|
|
||||||
if( ImGui.Begin(MenuButtonsName, ButtonFlags) )
|
|
||||||
{
|
|
||||||
if( ImGui.Button( MenuButtonLabel, WindowSize ) )
|
|
||||||
_base.FlipVisibility();
|
|
||||||
|
|
||||||
ImGui.End();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ss = ImGui.GetIO().DisplaySize;
|
||||||
|
|
||||||
|
ImGui.SetNextWindowPos( ss - WindowPosOffset, ImGuiCond.Always );
|
||||||
|
|
||||||
|
if( !ImGui.Begin( MenuButtonsName, ButtonFlags ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.Button( MenuButtonLabel, WindowSize ) )
|
||||||
|
{
|
||||||
|
_base.FlipVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,29 +19,37 @@ namespace Penumbra.UI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
public MenuBar(SettingsInterface ui) => _base = ui;
|
public MenuBar( SettingsInterface ui ) => _base = ui;
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if( _showDebugBar && ImGui.BeginMainMenuBar() )
|
if( !_showDebugBar || !ImGui.BeginMainMenuBar() )
|
||||||
{
|
{
|
||||||
if( ImGui.BeginMenu( MenuLabel ) )
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.BeginMenu( MenuLabel ) )
|
||||||
|
{
|
||||||
|
if( ImGui.MenuItem( MenuItemToggle, SlashCommand, _base._menu.Visible ) )
|
||||||
{
|
{
|
||||||
if( ImGui.MenuItem( MenuItemToggle, SlashCommand, _base._menu.Visible ) )
|
_base.FlipVisibility();
|
||||||
_base.FlipVisibility();
|
|
||||||
|
|
||||||
if( ImGui.MenuItem( MenuItemRediscover ) )
|
|
||||||
_base.ReloadMods();
|
|
||||||
#if DEBUG
|
|
||||||
if ( ImGui.MenuItem( MenuItemHide) )
|
|
||||||
_showDebugBar = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ImGui.EndMenu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMainMenuBar();
|
if( ImGui.MenuItem( MenuItemRediscover ) )
|
||||||
|
{
|
||||||
|
_base.ReloadMods();
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
if( ImGui.MenuItem( MenuItemHide ) )
|
||||||
|
{
|
||||||
|
_showDebugBar = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.EndMainMenuBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
private const float DefaultVerticalSpace = 20f;
|
private const float DefaultVerticalSpace = 20f;
|
||||||
|
|
||||||
private static readonly Vector2 AutoFillSize = new(-1, -1);
|
private static readonly Vector2 AutoFillSize = new( -1, -1 );
|
||||||
private static readonly Vector2 ZeroVector = new( 0, 0);
|
private static readonly Vector2 ZeroVector = new( 0, 0 );
|
||||||
|
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
|
|
||||||
|
|
@ -19,9 +19,9 @@ namespace Penumbra.UI
|
||||||
public SettingsInterface( Plugin plugin )
|
public SettingsInterface( Plugin plugin )
|
||||||
{
|
{
|
||||||
_plugin = plugin;
|
_plugin = plugin;
|
||||||
_launchButton = new(this);
|
_launchButton = new LaunchButton( this );
|
||||||
_menuBar = new(this);
|
_menuBar = new MenuBar( this );
|
||||||
_menu = new(this);
|
_menu = new SettingsMenu( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FlipVisibility() => _menu.Visible = !_menu.Visible;
|
public void FlipVisibility() => _menu.Visible = !_menu.Visible;
|
||||||
|
|
@ -35,14 +35,14 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
private void ReloadMods()
|
private void ReloadMods()
|
||||||
{
|
{
|
||||||
_menu._installedTab._selector.ResetModNamesLower();
|
_menu.InstalledTab.Selector.ResetModNamesLower();
|
||||||
_menu._installedTab._selector.ClearSelection();
|
_menu.InstalledTab.Selector.ClearSelection();
|
||||||
// create the directory if it doesn't exist
|
// create the directory if it doesn't exist
|
||||||
Directory.CreateDirectory( _plugin.Configuration.CurrentCollection );
|
Directory.CreateDirectory( _plugin.Configuration.CurrentCollection );
|
||||||
|
|
||||||
_plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection );
|
_plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection );
|
||||||
_menu._effectiveTab.RebuildFileList(_plugin.Configuration.ShowAdvanced);
|
_menu.EffectiveTab.RebuildFileList( _plugin.Configuration.ShowAdvanced );
|
||||||
_menu._installedTab._selector.ResetModNamesLower();
|
_menu.InstalledTab.Selector.ResetModNamesLower();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,20 +13,20 @@ namespace Penumbra.UI
|
||||||
private static readonly Vector2 MaxSettingsSize = new( 69420, 42069 );
|
private static readonly Vector2 MaxSettingsSize = new( 69420, 42069 );
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
public readonly TabSettings _settingsTab;
|
private readonly TabSettings _settingsTab;
|
||||||
public readonly TabImport _importTab;
|
private readonly TabImport _importTab;
|
||||||
public readonly TabBrowser _browserTab;
|
private readonly TabBrowser _browserTab;
|
||||||
public readonly TabInstalled _installedTab;
|
public readonly TabInstalled InstalledTab;
|
||||||
public readonly TabEffective _effectiveTab;
|
public readonly TabEffective EffectiveTab;
|
||||||
|
|
||||||
public SettingsMenu(SettingsInterface ui)
|
public SettingsMenu( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_settingsTab = new(_base);
|
_settingsTab = new TabSettings( _base );
|
||||||
_importTab = new(_base);
|
_importTab = new TabImport( _base );
|
||||||
_browserTab = new();
|
_browserTab = new TabBrowser();
|
||||||
_installedTab = new(_base);
|
InstalledTab = new TabInstalled( _base );
|
||||||
_effectiveTab = new(_base);
|
EffectiveTab = new TabEffective( _base );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
@ -39,7 +39,9 @@ namespace Penumbra.UI
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if( !Visible )
|
if( !Visible )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize );
|
ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize );
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
@ -48,7 +50,9 @@ namespace Penumbra.UI
|
||||||
var ret = ImGui.Begin( _base._plugin.Name, ref Visible );
|
var ret = ImGui.Begin( _base._plugin.Name, ref Visible );
|
||||||
#endif
|
#endif
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.BeginTabBar( PenumbraSettingsLabel );
|
ImGui.BeginTabBar( PenumbraSettingsLabel );
|
||||||
|
|
||||||
|
|
@ -58,10 +62,12 @@ namespace Penumbra.UI
|
||||||
if( !_importTab.IsImporting() )
|
if( !_importTab.IsImporting() )
|
||||||
{
|
{
|
||||||
_browserTab.Draw();
|
_browserTab.Draw();
|
||||||
_installedTab.Draw();
|
InstalledTab.Draw();
|
||||||
|
|
||||||
if( _base._plugin.Configuration.ShowAdvanced )
|
if( _base._plugin.Configuration.ShowAdvanced )
|
||||||
_effectiveTab.Draw();
|
{
|
||||||
|
EffectiveTab.Draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndTabBar();
|
ImGui.EndTabBar();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginTabItem( "Available Mods" );
|
var ret = ImGui.BeginTabItem( "Available Mods" );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Text( "woah" );
|
ImGui.Text( "woah" );
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
|
|
|
||||||
|
|
@ -11,22 +11,22 @@ namespace Penumbra.UI
|
||||||
private const string LabelTab = "Effective File List";
|
private const string LabelTab = "Effective File List";
|
||||||
private const float TextSizePadding = 5f;
|
private const float TextSizePadding = 5f;
|
||||||
|
|
||||||
private readonly ModManager _mods;
|
private readonly ModManager _mods;
|
||||||
private (string, string)[] _fileList = null;
|
private (string, string)[] _fileList;
|
||||||
private float _maxGamePath = 0f;
|
private float _maxGamePath;
|
||||||
|
|
||||||
public TabEffective(SettingsInterface ui)
|
public TabEffective( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_mods = ui._plugin.ModManager;
|
_mods = ui._plugin.ModManager;
|
||||||
RebuildFileList(ui._plugin.Configuration.ShowAdvanced);
|
RebuildFileList( ui._plugin.Configuration.ShowAdvanced );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RebuildFileList(bool advanced)
|
public void RebuildFileList( bool advanced )
|
||||||
{
|
{
|
||||||
if (advanced)
|
if( advanced )
|
||||||
{
|
{
|
||||||
_fileList = _mods.ResolvedFiles.Select( P => (P.Value.FullName, P.Key) ).ToArray();
|
_fileList = _mods.ResolvedFiles.Select( P => ( P.Value.FullName, P.Key ) ).ToArray();
|
||||||
_maxGamePath = ((_fileList.Length > 0) ? _fileList.Max( P => ImGui.CalcTextSize(P.Item2).X ) : 0f) + TextSizePadding;
|
_maxGamePath = ( _fileList.Length > 0 ? _fileList.Max( P => ImGui.CalcTextSize( P.Item2 ).X ) : 0f ) + TextSizePadding;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -35,26 +35,30 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFileLine((string, string) file)
|
private void DrawFileLine( (string, string) file )
|
||||||
{
|
{
|
||||||
ImGui.Selectable(file.Item2);
|
ImGui.Selectable( file.Item2 );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetCursorPosX(_maxGamePath);
|
ImGui.SetCursorPosX( _maxGamePath );
|
||||||
ImGui.TextUnformatted(" <-- ");
|
ImGui.TextUnformatted( " <-- " );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.Selectable(file.Item1);
|
ImGui.Selectable( file.Item1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginTabItem( LabelTab );
|
var ret = ImGui.BeginTabItem( LabelTab );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) )
|
if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) )
|
||||||
{
|
{
|
||||||
foreach( var file in _fileList )
|
foreach( var file in _fileList )
|
||||||
DrawFileLine(file);
|
{
|
||||||
|
DrawFileLine( file );
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.ListBoxFooter();
|
ImGui.ListBoxFooter();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,22 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
private const string LabelTab = "Import Mods";
|
private const string LabelTab = "Import Mods";
|
||||||
private const string LabelImportButton = "Import TexTools Modpacks";
|
private const string LabelImportButton = "Import TexTools Modpacks";
|
||||||
private const string FileTypeFilter = "TexTools TTMP Modpack (*.ttmp2)|*.ttmp*|All files (*.*)|*.*";
|
|
||||||
private const string LabelFileDialog = "Pick one or more modpacks.";
|
private const string LabelFileDialog = "Pick one or more modpacks.";
|
||||||
private const string LabelFileImportRunning = "Import in progress...";
|
private const string LabelFileImportRunning = "Import in progress...";
|
||||||
|
private const string FileTypeFilter = "TexTools TTMP Modpack (*.ttmp2)|*.ttmp*|All files (*.*)|*.*";
|
||||||
private const string TooltipModpack1 = "Writing modpack to disk before extracting...";
|
private const string TooltipModpack1 = "Writing modpack to disk before extracting...";
|
||||||
private const string FailedImport = "One or more of your modpacks failed to import.\nPlease submit a bug report.";
|
private const string FailedImport = "One or more of your modpacks failed to import.\nPlease submit a bug report.";
|
||||||
|
|
||||||
private const uint ColorRed = 0xFF0000C8;
|
private const uint ColorRed = 0xFF0000C8;
|
||||||
|
|
||||||
private static readonly Vector2 ImportBarSize = new( -1, 0 );
|
private static readonly Vector2 ImportBarSize = new( -1, 0 );
|
||||||
|
|
||||||
private bool _isImportRunning = false;
|
private bool _isImportRunning = false;
|
||||||
private bool _hasError = false;
|
private bool _hasError = false;
|
||||||
private TexToolsImport _texToolsImport = null!;
|
private TexToolsImport _texToolsImport = null!;
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
|
|
||||||
public TabImport(SettingsInterface ui) => _base = ui;
|
public TabImport( SettingsInterface ui ) => _base = ui;
|
||||||
|
|
||||||
public bool IsImporting() => _isImportRunning;
|
public bool IsImporting() => _isImportRunning;
|
||||||
|
|
||||||
|
|
@ -41,10 +41,10 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
var picker = new OpenFileDialog
|
var picker = new OpenFileDialog
|
||||||
{
|
{
|
||||||
Multiselect = true,
|
Multiselect = true,
|
||||||
Filter = FileTypeFilter,
|
Filter = FileTypeFilter,
|
||||||
CheckFileExists = true,
|
CheckFileExists = true,
|
||||||
Title = LabelFileDialog
|
Title = LabelFileDialog
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await picker.ShowDialogAsync();
|
var result = await picker.ShowDialogAsync();
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
foreach( var fileName in picker.FileNames )
|
foreach( var fileName in picker.FileNames )
|
||||||
{
|
{
|
||||||
PluginLog.Log( $"-> {fileName} START");
|
PluginLog.Log( $"-> {fileName} START" );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -74,6 +74,7 @@ namespace Penumbra.UI
|
||||||
_texToolsImport = null;
|
_texToolsImport = null;
|
||||||
_base.ReloadMods();
|
_base.ReloadMods();
|
||||||
}
|
}
|
||||||
|
|
||||||
_isImportRunning = false;
|
_isImportRunning = false;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
@ -90,32 +91,34 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
ImGui.Button( LabelFileImportRunning );
|
ImGui.Button( LabelFileImportRunning );
|
||||||
|
|
||||||
if( _texToolsImport != null )
|
if( _texToolsImport == null )
|
||||||
{
|
{
|
||||||
switch( _texToolsImport.State )
|
return;
|
||||||
{
|
}
|
||||||
case ImporterState.None:
|
|
||||||
break;
|
|
||||||
case ImporterState.WritingPackToDisk:
|
|
||||||
ImGui.Text( TooltipModpack1 );
|
|
||||||
break;
|
|
||||||
case ImporterState.ExtractingModFiles:
|
|
||||||
{
|
|
||||||
var str =
|
|
||||||
$"{_texToolsImport.CurrentModPack} - {_texToolsImport.CurrentProgress} of {_texToolsImport.TotalProgress} files";
|
|
||||||
|
|
||||||
ImGui.ProgressBar( _texToolsImport.Progress, ImportBarSize, str );
|
switch( _texToolsImport.State )
|
||||||
break;
|
{
|
||||||
}
|
case ImporterState.None:
|
||||||
case ImporterState.Done:
|
break;
|
||||||
break;
|
case ImporterState.WritingPackToDisk:
|
||||||
default:
|
ImGui.Text( TooltipModpack1 );
|
||||||
throw new ArgumentOutOfRangeException();
|
break;
|
||||||
|
case ImporterState.ExtractingModFiles:
|
||||||
|
{
|
||||||
|
var str =
|
||||||
|
$"{_texToolsImport.CurrentModPack} - {_texToolsImport.CurrentProgress} of {_texToolsImport.TotalProgress} files";
|
||||||
|
|
||||||
|
ImGui.ProgressBar( _texToolsImport.Progress, ImportBarSize, str );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case ImporterState.Done:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFailedImportMessage()
|
private static void DrawFailedImportMessage()
|
||||||
{
|
{
|
||||||
ImGui.PushStyleColor( ImGuiCol.Text, ColorRed );
|
ImGui.PushStyleColor( ImGuiCol.Text, ColorRed );
|
||||||
ImGui.Text( FailedImport );
|
ImGui.Text( FailedImport );
|
||||||
|
|
@ -126,15 +129,23 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginTabItem( LabelTab );
|
var ret = ImGui.BeginTabItem( LabelTab );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( !_isImportRunning )
|
if( !_isImportRunning )
|
||||||
|
{
|
||||||
DrawImportButton();
|
DrawImportButton();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
DrawImportProgress();
|
DrawImportProgress();
|
||||||
|
}
|
||||||
|
|
||||||
if (_hasError)
|
if( _hasError )
|
||||||
|
{
|
||||||
DrawFailedImportMessage();
|
DrawFailedImportMessage();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,25 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
public partial class SettingsInterface
|
public partial class SettingsInterface
|
||||||
{
|
{
|
||||||
private partial class TabInstalled
|
private class TabInstalled
|
||||||
{
|
{
|
||||||
private const string LabelTab = "Installed Mods";
|
private const string LabelTab = "Installed Mods";
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
public readonly Selector _selector;
|
public readonly Selector Selector;
|
||||||
public readonly ModPanel _modPanel;
|
public readonly ModPanel ModPanel;
|
||||||
|
|
||||||
public TabInstalled(SettingsInterface ui)
|
public TabInstalled( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_selector = new(_base);
|
Selector = new Selector( _base );
|
||||||
_modPanel = new(_base, _selector);
|
ModPanel = new ModPanel( _base, Selector );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawNoModsAvailable()
|
private static void DrawNoModsAvailable()
|
||||||
{
|
{
|
||||||
ImGui.Text( "You don't have any mods :(" );
|
ImGui.Text( "You don't have any mods :(" );
|
||||||
ImGuiCustom.VerticalDistance(20f);
|
ImGuiCustom.VerticalDistance( 20f );
|
||||||
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( "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( "For example: D:/ffxiv/mods/" );
|
||||||
ImGui.Text( "And pasting that path into the settings tab and clicking the 'Rediscover Mods' button." );
|
ImGui.Text( "And pasting that path into the settings tab and clicking the 'Rediscover Mods' button." );
|
||||||
|
|
@ -33,20 +33,22 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginTabItem( LabelTab );
|
var ret = ImGui.BeginTabItem( LabelTab );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
return;
|
|
||||||
|
|
||||||
if (_base._plugin.ModManager.Mods != null)
|
|
||||||
{
|
{
|
||||||
_selector.Draw();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _base._plugin.ModManager.Mods != null )
|
||||||
|
{
|
||||||
|
Selector.Draw();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
_modPanel.Draw();
|
ModPanel.Draw();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
DrawNoModsAvailable();
|
DrawNoModsAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,16 @@ namespace Penumbra.UI
|
||||||
internal static class Extension
|
internal static class Extension
|
||||||
{
|
{
|
||||||
// Remove the entry at idx from the list if the new string is empty, otherwise replace it.
|
// Remove the entry at idx from the list if the new string is empty, otherwise replace it.
|
||||||
public static void RemoveOrChange(this List<string> list, string newString, int idx)
|
public static void RemoveOrChange( this List< string > list, string newString, int idx )
|
||||||
{
|
{
|
||||||
if (newString?.Length == 0)
|
if( newString?.Length == 0 )
|
||||||
list.RemoveAt(idx);
|
{
|
||||||
|
list.RemoveAt( idx );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
list[idx] = newString;
|
{
|
||||||
|
list[ idx ] = newString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,117 +37,147 @@ namespace Penumbra.UI
|
||||||
private const string LabelFileSwapHeader = "##fileSwaps";
|
private const string LabelFileSwapHeader = "##fileSwaps";
|
||||||
private const string LabelFileListTab = "Files";
|
private const string LabelFileListTab = "Files";
|
||||||
private const string LabelFileListHeader = "##fileList";
|
private const string LabelFileListHeader = "##fileList";
|
||||||
private const string TooltipFilesTab = "Green files replace their standard game path counterpart (not in any option) or are in all options of a Single-Select option.\nYellow files are restricted to some options.";
|
|
||||||
private const string LabelGroupSelect = "##groupSelect";
|
private const string LabelGroupSelect = "##groupSelect";
|
||||||
private const string LabelOptionSelect = "##optionSelect";
|
private const string LabelOptionSelect = "##optionSelect";
|
||||||
private const string LabelConfigurationTab = "Configuration";
|
private const string LabelConfigurationTab = "Configuration";
|
||||||
|
|
||||||
private const float TextSizePadding = 5f;
|
private const string TooltipFilesTab =
|
||||||
private const float OptionSelectionWidth = 140f;
|
"Green files replace their standard game path counterpart (not in any option) or are in all options of a Single-Select option.\n" +
|
||||||
private const float CheckMarkSize = 50f;
|
"Yellow files are restricted to some options.";
|
||||||
private const uint ColorGreen = 0xFF00C800;
|
|
||||||
private const uint ColorYellow = 0xFF00C8C8;
|
|
||||||
private const uint ColorRed = 0xFF0000C8;
|
|
||||||
|
|
||||||
private bool _editMode = false;
|
private const float TextSizePadding = 5f;
|
||||||
private int _selectedGroupIndex = 0;
|
private const float OptionSelectionWidth = 140f;
|
||||||
private InstallerInfo? _selectedGroup = null;
|
private const float CheckMarkSize = 50f;
|
||||||
private int _selectedOptionIndex = 0;
|
private const uint ColorGreen = 0xFF00C800;
|
||||||
private Option? _selectedOption = null;
|
private const uint ColorYellow = 0xFF00C8C8;
|
||||||
private (string label, string name)[] _changedItemsList = null;
|
private const uint ColorRed = 0xFF0000C8;
|
||||||
private float? _fileSwapOffset = null;
|
|
||||||
private string _currentGamePaths = "";
|
private bool _editMode = false;
|
||||||
|
private int _selectedGroupIndex = 0;
|
||||||
|
private InstallerInfo? _selectedGroup = null;
|
||||||
|
private int _selectedOptionIndex = 0;
|
||||||
|
private Option? _selectedOption = null;
|
||||||
|
private (string label, string name)[] _changedItemsList = null;
|
||||||
|
private float? _fileSwapOffset = null;
|
||||||
|
private string _currentGamePaths = "";
|
||||||
|
|
||||||
private (string name, bool selected, uint color, string relName)[] _fullFilenameList = null;
|
private (string name, bool selected, uint color, string relName)[] _fullFilenameList = null;
|
||||||
|
|
||||||
public void SelectGroup(int idx)
|
private readonly Selector _selector;
|
||||||
|
private readonly SettingsInterface _base;
|
||||||
|
|
||||||
|
private void SelectGroup( int idx )
|
||||||
{
|
{
|
||||||
_selectedGroupIndex = idx;
|
_selectedGroupIndex = idx;
|
||||||
if (_selectedGroupIndex >= Meta?.Groups?.Count)
|
if( _selectedGroupIndex >= Meta?.Groups?.Count )
|
||||||
|
{
|
||||||
_selectedGroupIndex = 0;
|
_selectedGroupIndex = 0;
|
||||||
if (Meta?.Groups?.Count > 0)
|
}
|
||||||
_selectedGroup = Meta.Groups.ElementAt(_selectedGroupIndex).Value;
|
|
||||||
else
|
|
||||||
_selectedGroup = null;
|
|
||||||
}
|
|
||||||
public void SelectGroup() => SelectGroup(_selectedGroupIndex);
|
|
||||||
|
|
||||||
public void SelectOption(int idx)
|
if( Meta?.Groups?.Count > 0 )
|
||||||
|
{
|
||||||
|
_selectedGroup = Meta.Groups.ElementAt( _selectedGroupIndex ).Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedGroup = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectGroup() => SelectGroup( _selectedGroupIndex );
|
||||||
|
|
||||||
|
private void SelectOption( int idx )
|
||||||
{
|
{
|
||||||
_selectedOptionIndex = idx;
|
_selectedOptionIndex = idx;
|
||||||
if (_selectedOptionIndex >= _selectedGroup?.Options.Count)
|
if( _selectedOptionIndex >= _selectedGroup?.Options.Count )
|
||||||
|
{
|
||||||
_selectedOptionIndex = 0;
|
_selectedOptionIndex = 0;
|
||||||
if (_selectedGroup?.Options.Count > 0)
|
}
|
||||||
_selectedOption = ((InstallerInfo) _selectedGroup).Options[_selectedOptionIndex];
|
|
||||||
|
if( _selectedGroup?.Options.Count > 0 )
|
||||||
|
{
|
||||||
|
_selectedOption = ( ( InstallerInfo )_selectedGroup ).Options[ _selectedOptionIndex ];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_selectedOption = null;
|
_selectedOption = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public void SelectOption() => SelectOption(_selectedOptionIndex);
|
|
||||||
|
private void SelectOption() => SelectOption( _selectedOptionIndex );
|
||||||
|
|
||||||
public void ResetState()
|
public void ResetState()
|
||||||
{
|
{
|
||||||
_changedItemsList = null;
|
_changedItemsList = null;
|
||||||
_fileSwapOffset = null;
|
_fileSwapOffset = null;
|
||||||
_fullFilenameList = null;
|
_fullFilenameList = null;
|
||||||
SelectGroup();
|
SelectGroup();
|
||||||
SelectOption();
|
SelectOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PluginDetails( SettingsInterface ui, Selector s )
|
||||||
private readonly Selector _selector;
|
|
||||||
private readonly SettingsInterface _base;
|
|
||||||
public PluginDetails(SettingsInterface ui, Selector s)
|
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_selector = s;
|
_selector = s;
|
||||||
ResetState();
|
ResetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModInfo Mod { get{ return _selector.Mod(); } }
|
private ModInfo Mod => _selector.Mod();
|
||||||
private ModMeta Meta { get{ return Mod?.Mod?.Meta; } }
|
private ModMeta Meta => Mod?.Mod?.Meta;
|
||||||
|
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
_base._plugin.ModManager.Mods.Save();
|
_base._plugin.ModManager.Mods.Save();
|
||||||
_base._plugin.ModManager.CalculateEffectiveFileList();
|
_base._plugin.ModManager.CalculateEffectiveFileList();
|
||||||
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
|
_base._menu.EffectiveTab.RebuildFileList( _base._plugin.Configuration.ShowAdvanced );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAboutTab()
|
private void DrawAboutTab()
|
||||||
{
|
{
|
||||||
if (!_editMode && Meta.Description?.Length == 0)
|
if( !_editMode && Meta.Description?.Length == 0 )
|
||||||
return;
|
|
||||||
|
|
||||||
if(ImGui.BeginTabItem( LabelAboutTab ) )
|
|
||||||
{
|
{
|
||||||
var desc = Meta.Description;
|
return;
|
||||||
var flags = _editMode
|
|
||||||
? ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CtrlEnterForNewLine
|
|
||||||
: ImGuiInputTextFlags.ReadOnly;
|
|
||||||
|
|
||||||
if( _editMode )
|
|
||||||
{
|
|
||||||
if (ImGui.InputTextMultiline(LabelDescEdit, ref desc, 1 << 16, AutoFillSize, flags))
|
|
||||||
{
|
|
||||||
Meta.Description = desc;
|
|
||||||
_selector.SaveCurrentMod();
|
|
||||||
}
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip( TooltipAboutEdit );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped( desc );
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !ImGui.BeginTabItem( LabelAboutTab ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var desc = Meta.Description;
|
||||||
|
var flags = _editMode
|
||||||
|
? ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CtrlEnterForNewLine
|
||||||
|
: ImGuiInputTextFlags.ReadOnly;
|
||||||
|
|
||||||
|
if( _editMode )
|
||||||
|
{
|
||||||
|
if( ImGui.InputTextMultiline( LabelDescEdit, ref desc, 1 << 16, AutoFillSize, flags ) )
|
||||||
|
{
|
||||||
|
Meta.Description = desc;
|
||||||
|
_selector.SaveCurrentMod();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip( TooltipAboutEdit );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextWrapped( desc );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawChangedItemsTab()
|
private void DrawChangedItemsTab()
|
||||||
{
|
{
|
||||||
if (!_editMode && Meta.ChangedItems?.Count == 0)
|
if( !_editMode && ( Meta.ChangedItems?.Count ?? 0 ) == 0 )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Meta.ChangedItems ??= new List< string >();
|
||||||
|
|
||||||
var flags = _editMode
|
var flags = _editMode
|
||||||
? ImGuiInputTextFlags.EnterReturnsTrue
|
? ImGuiInputTextFlags.EnterReturnsTrue
|
||||||
|
|
@ -154,122 +188,164 @@ namespace Penumbra.UI
|
||||||
ImGui.SetNextItemWidth( -1 );
|
ImGui.SetNextItemWidth( -1 );
|
||||||
if( ImGui.ListBoxHeader( LabelChangedItemsHeader, AutoFillSize ) )
|
if( ImGui.ListBoxHeader( LabelChangedItemsHeader, AutoFillSize ) )
|
||||||
{
|
{
|
||||||
if (_changedItemsList == null)
|
_changedItemsList ??= Meta.ChangedItems.Select( ( I, index ) => ( $"{LabelChangedItemIdx}{index}", I ) ).ToArray();
|
||||||
_changedItemsList = Meta.ChangedItems.Select( (I, index) => ($"{LabelChangedItemIdx}{index}", I) ).ToArray();
|
|
||||||
for (var i = 0; i < Meta.ChangedItems.Count; ++i)
|
for( var i = 0; i < Meta.ChangedItems.Count; ++i )
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(-1);
|
ImGui.SetNextItemWidth( -1 );
|
||||||
if ( ImGui.InputText(_changedItemsList[i].label, ref _changedItemsList[i].name, 128, flags) )
|
if( ImGui.InputText( _changedItemsList[ i ].label, ref _changedItemsList[ i ].name, 128, flags ) )
|
||||||
{
|
{
|
||||||
Meta.ChangedItems.RemoveOrChange(_changedItemsList[i].name, i);
|
Meta.ChangedItems.RemoveOrChange( _changedItemsList[ i ].name, i );
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newItem = "";
|
var newItem = "";
|
||||||
if ( _editMode )
|
if( _editMode )
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(-1);
|
ImGui.SetNextItemWidth( -1 );
|
||||||
if ( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags) )
|
if( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags ) )
|
||||||
{
|
{
|
||||||
if (newItem.Length > 0)
|
if( newItem.Length > 0 )
|
||||||
{
|
{
|
||||||
if (Meta.ChangedItems == null)
|
if( Meta.ChangedItems == null )
|
||||||
Meta.ChangedItems = new(){ newItem };
|
{
|
||||||
|
Meta.ChangedItems = new List< string >() { newItem };
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Meta.ChangedItems.Add(newItem);
|
{
|
||||||
|
Meta.ChangedItems.Add( newItem );
|
||||||
|
}
|
||||||
|
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.ListBoxFooter();
|
ImGui.ListBoxFooter();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_changedItemsList = null;
|
_changedItemsList = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawConflictTab()
|
private void DrawConflictTab()
|
||||||
{
|
{
|
||||||
if( Mod.Mod.FileConflicts.Any() )
|
if( !Mod.Mod.FileConflicts.Any() )
|
||||||
{
|
{
|
||||||
if( ImGui.BeginTabItem( LabelConflictsTab ) )
|
return;
|
||||||
{
|
}
|
||||||
ImGui.SetNextItemWidth( -1 );
|
|
||||||
if( ImGui.ListBoxHeader( LabelConflictsHeader, AutoFillSize ) )
|
|
||||||
{
|
|
||||||
foreach( var kv in Mod.Mod.FileConflicts )
|
|
||||||
{
|
|
||||||
var mod = kv.Key;
|
|
||||||
if( ImGui.Selectable( mod ) )
|
|
||||||
_selector.SelectModByName( mod );
|
|
||||||
|
|
||||||
ImGui.Indent( 15 );
|
if( !ImGui.BeginTabItem( LabelConflictsTab ) )
|
||||||
foreach( var file in kv.Value )
|
{
|
||||||
ImGui.Selectable( file );
|
return;
|
||||||
ImGui.Unindent( 15 );
|
}
|
||||||
}
|
|
||||||
ImGui.ListBoxFooter();
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.ListBoxHeader( LabelConflictsHeader, AutoFillSize ) )
|
||||||
|
{
|
||||||
|
foreach( var kv in Mod.Mod.FileConflicts )
|
||||||
|
{
|
||||||
|
var mod = kv.Key;
|
||||||
|
if( ImGui.Selectable( mod ) )
|
||||||
|
{
|
||||||
|
_selector.SelectModByName( mod );
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.Indent( 15 );
|
||||||
|
foreach( var file in kv.Value )
|
||||||
|
{
|
||||||
|
ImGui.Selectable( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent( 15 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.ListBoxFooter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFileSwapTab()
|
private void DrawFileSwapTab()
|
||||||
{
|
{
|
||||||
if( Meta.FileSwaps.Any() )
|
if( !Meta.FileSwaps.Any() )
|
||||||
{
|
{
|
||||||
if( ImGui.BeginTabItem( LabelFileSwapTab ) )
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.BeginTabItem( LabelFileSwapTab ) )
|
||||||
|
{
|
||||||
|
_fileSwapOffset ??= Meta.FileSwaps.Max( P => ImGui.CalcTextSize( P.Key ).X ) + TextSizePadding;
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) )
|
||||||
{
|
{
|
||||||
if (_fileSwapOffset == null)
|
foreach( var file in Meta.FileSwaps )
|
||||||
_fileSwapOffset = Meta.FileSwaps.Max( P => ImGui.CalcTextSize(P.Key).X) + TextSizePadding;
|
|
||||||
ImGui.SetNextItemWidth( -1 );
|
|
||||||
if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) )
|
|
||||||
{
|
{
|
||||||
foreach( var file in Meta.FileSwaps )
|
ImGui.Selectable( file.Key );
|
||||||
{
|
ImGui.SameLine( _fileSwapOffset ?? 0 );
|
||||||
ImGui.Selectable(file.Key);
|
ImGui.TextUnformatted( " -> " );
|
||||||
ImGui.SameLine(_fileSwapOffset ?? 0);
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(" -> ");
|
ImGui.Selectable( file.Value );
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.Selectable(file.Value);
|
|
||||||
}
|
|
||||||
ImGui.ListBoxFooter();
|
|
||||||
}
|
}
|
||||||
ImGui.EndTabItem();
|
|
||||||
|
ImGui.ListBoxFooter();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
_fileSwapOffset = null;
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_fileSwapOffset = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateFilenameList()
|
private void UpdateFilenameList()
|
||||||
{
|
{
|
||||||
if (_fullFilenameList == null)
|
if( _fullFilenameList != null )
|
||||||
{
|
{
|
||||||
var len = Mod.Mod.ModBasePath.FullName.Length;
|
return;
|
||||||
_fullFilenameList = Mod.Mod.ModFiles.Select( F => (F.FullName, false, ColorGreen, "") ).ToArray();
|
}
|
||||||
|
|
||||||
if(Meta.Groups?.Count == 0)
|
var len = Mod.Mod.ModBasePath.FullName.Length;
|
||||||
return;
|
_fullFilenameList = Mod.Mod.ModFiles.Select( F => ( F.FullName, false, ColorGreen, "" ) ).ToArray();
|
||||||
|
|
||||||
for (var i = 0; i < Mod.Mod.ModFiles.Count; ++i)
|
if( Meta.Groups?.Count == 0 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( var i = 0; i < Mod.Mod.ModFiles.Count; ++i )
|
||||||
|
{
|
||||||
|
_fullFilenameList[ i ].relName = _fullFilenameList[ i ].name.Substring( len ).TrimStart( '\\' );
|
||||||
|
if( Meta.Groups == null )
|
||||||
{
|
{
|
||||||
_fullFilenameList[i].relName = _fullFilenameList[i].name.Substring(len).TrimStart('\\');
|
continue;
|
||||||
foreach (var Group in Meta.Groups.Values)
|
}
|
||||||
|
|
||||||
|
foreach( var group in Meta.Groups.Values )
|
||||||
|
{
|
||||||
|
var inAll = true;
|
||||||
|
foreach( var option in group.Options )
|
||||||
{
|
{
|
||||||
var inAll = true;
|
if( option.OptionFiles.ContainsKey( _fullFilenameList[ i ].relName ) )
|
||||||
foreach (var Option in Group.Options)
|
|
||||||
{
|
{
|
||||||
if (Option.OptionFiles.ContainsKey(_fullFilenameList[i].relName))
|
_fullFilenameList[ i ].color = ColorYellow;
|
||||||
_fullFilenameList[i].color = ColorYellow;
|
|
||||||
else
|
|
||||||
inAll = false;
|
|
||||||
}
|
}
|
||||||
if (inAll && Group.SelectionType == SelectType.Single)
|
else
|
||||||
_fullFilenameList[i].color = ColorGreen;
|
{
|
||||||
|
inAll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( inAll && group.SelectionType == SelectType.Single )
|
||||||
|
{
|
||||||
|
_fullFilenameList[ i ].color = ColorGreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,111 +353,142 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
private void DrawFileListTab()
|
private void DrawFileListTab()
|
||||||
{
|
{
|
||||||
if( ImGui.BeginTabItem( LabelFileListTab ) )
|
if( !ImGui.BeginTabItem( LabelFileListTab ) )
|
||||||
{
|
{
|
||||||
if (ImGui.IsItemHovered())
|
return;
|
||||||
ImGui.SetTooltip( TooltipFilesTab );
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth( -1 );
|
|
||||||
if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize ) )
|
|
||||||
{
|
|
||||||
UpdateFilenameList();
|
|
||||||
foreach(var file in _fullFilenameList)
|
|
||||||
{
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, file.color);
|
|
||||||
ImGui.Selectable(file.name);
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
ImGui.ListBoxFooter();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_fullFilenameList = null;
|
|
||||||
ImGui.EndTabItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip( TooltipFilesTab );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize ) )
|
||||||
|
{
|
||||||
|
UpdateFilenameList();
|
||||||
|
foreach( var file in _fullFilenameList )
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor( ImGuiCol.Text, file.color );
|
||||||
|
ImGui.Selectable( file.name );
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.ListBoxFooter();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_fullFilenameList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSelectedFilesButton(bool remove)
|
private void HandleSelectedFilesButton( bool remove )
|
||||||
{
|
{
|
||||||
if (_selectedOption == null)
|
if( _selectedOption == null )
|
||||||
return;
|
|
||||||
var option = (Option) _selectedOption;
|
|
||||||
|
|
||||||
var gamePaths = _currentGamePaths.Split(';');
|
|
||||||
if (gamePaths.Length == 0 || gamePaths[0].Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int? defaultIndex = null;
|
|
||||||
for (var i = 0; i < gamePaths.Length; ++i)
|
|
||||||
{
|
{
|
||||||
if (gamePaths[i] == TextDefaultGamePath )
|
return;
|
||||||
{
|
|
||||||
defaultIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseLength = Mod.Mod.ModBasePath.FullName.Length;
|
var option = ( Option )_selectedOption;
|
||||||
var changed = false;
|
|
||||||
for (var i = 0; i < Mod.Mod.ModFiles.Count; ++i)
|
var gamePaths = _currentGamePaths.Split( ';' );
|
||||||
|
if( gamePaths.Length == 0 || gamePaths[ 0 ].Length == 0 )
|
||||||
{
|
{
|
||||||
if (!_fullFilenameList[i].selected)
|
return;
|
||||||
continue;
|
}
|
||||||
|
|
||||||
var fileName = _fullFilenameList[i].relName;
|
var defaultIndex = gamePaths.IndexOf( p => p == TextDefaultGamePath );
|
||||||
if (defaultIndex != null)
|
var changed = false;
|
||||||
gamePaths[(int)defaultIndex] = fileName.Replace('\\', '/');
|
for( var i = 0; i < Mod.Mod.ModFiles.Count; ++i )
|
||||||
|
{
|
||||||
if (remove && option.OptionFiles.TryGetValue(fileName, out var setPaths))
|
if( !_fullFilenameList[ i ].selected )
|
||||||
{
|
{
|
||||||
if (setPaths.RemoveWhere( P => gamePaths.Contains(P)) > 0)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = _fullFilenameList[ i ].relName;
|
||||||
|
if( defaultIndex >= 0 )
|
||||||
|
{
|
||||||
|
gamePaths[ ( int )defaultIndex ] = fileName.Replace( '\\', '/' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( remove && option.OptionFiles.TryGetValue( fileName, out var setPaths ) )
|
||||||
|
{
|
||||||
|
if( setPaths.RemoveWhere( P => gamePaths.Contains( P ) ) > 0 )
|
||||||
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
if (setPaths.Count == 0 && option.OptionFiles.Remove(fileName))
|
}
|
||||||
|
|
||||||
|
if( setPaths.Count == 0 && option.OptionFiles.Remove( fileName ) )
|
||||||
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach(var gamePath in gamePaths)
|
changed = gamePaths.Aggregate( changed, ( current, gamePath ) => current | option.AddFile( fileName, gamePath ) );
|
||||||
changed |= option.AddFile(fileName, gamePath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed)
|
|
||||||
|
if( changed )
|
||||||
|
{
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAddToGroupButton()
|
private void DrawAddToGroupButton()
|
||||||
{
|
{
|
||||||
if (ImGui.Button( ButtonAddToGroup ) )
|
if( ImGui.Button( ButtonAddToGroup ) )
|
||||||
HandleSelectedFilesButton(false);
|
{
|
||||||
|
HandleSelectedFilesButton( false );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawRemoveFromGroupButton()
|
private void DrawRemoveFromGroupButton()
|
||||||
{
|
{
|
||||||
if (ImGui.Button( ButtonRemoveFromGroup ) )
|
if( ImGui.Button( ButtonRemoveFromGroup ) )
|
||||||
HandleSelectedFilesButton(true);
|
{
|
||||||
|
HandleSelectedFilesButton( true );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGamePathInput()
|
private void DrawGamePathInput()
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted( LabelGamePathsEdit );
|
ImGui.TextUnformatted( LabelGamePathsEdit );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetNextItemWidth(-1);
|
ImGui.SetNextItemWidth( -1 );
|
||||||
ImGui.InputText(LabelGamePathsEditBox, ref _currentGamePaths, 128);
|
ImGui.InputText( LabelGamePathsEditBox, ref _currentGamePaths, 128 );
|
||||||
if (ImGui.IsItemHovered())
|
if( ImGui.IsItemHovered() )
|
||||||
ImGui.SetTooltip(TooltipGamePathsEdit);
|
{
|
||||||
|
ImGui.SetTooltip( TooltipGamePathsEdit );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupRow()
|
private void DrawGroupRow()
|
||||||
{
|
{
|
||||||
if (_selectedGroup == null)
|
if( _selectedGroup == null )
|
||||||
|
{
|
||||||
SelectGroup();
|
SelectGroup();
|
||||||
if (_selectedOption == null)
|
}
|
||||||
SelectOption();
|
|
||||||
|
|
||||||
if (!DrawEditGroupSelector())
|
if( _selectedOption == null )
|
||||||
|
{
|
||||||
|
SelectOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !DrawEditGroupSelector() )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (!DrawEditOptionSelector())
|
if( !DrawEditOptionSelector() )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawAddToGroupButton();
|
DrawAddToGroupButton();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -390,116 +497,142 @@ namespace Penumbra.UI
|
||||||
DrawGamePathInput();
|
DrawGamePathInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFileAndGamePaths(int idx)
|
private void DrawFileAndGamePaths( int idx )
|
||||||
{
|
{
|
||||||
void Selectable(uint colorNormal, uint colorReplace)
|
void Selectable( uint colorNormal, uint colorReplace )
|
||||||
{
|
{
|
||||||
var loc = _fullFilenameList[idx].color;
|
var loc = _fullFilenameList[ idx ].color;
|
||||||
if (loc == colorNormal)
|
if( loc == colorNormal )
|
||||||
|
{
|
||||||
loc = colorReplace;
|
loc = colorReplace;
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, loc);
|
}
|
||||||
ImGui.Selectable( _fullFilenameList[idx].name, ref _fullFilenameList[idx].selected );
|
|
||||||
|
ImGui.PushStyleColor( ImGuiCol.Text, loc );
|
||||||
|
ImGui.Selectable( _fullFilenameList[ idx ].name, ref _fullFilenameList[ idx ].selected );
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
const float indent = 30f;
|
const float indent = 30f;
|
||||||
if (_selectedOption == null)
|
if( _selectedOption == null )
|
||||||
{
|
{
|
||||||
Selectable(0, ColorGreen);
|
Selectable( 0, ColorGreen );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = _fullFilenameList[idx].relName;
|
var fileName = _fullFilenameList[ idx ].relName;
|
||||||
if (((Option) _selectedOption).OptionFiles.TryGetValue(fileName, out var gamePaths))
|
if( ( ( Option )_selectedOption ).OptionFiles.TryGetValue( fileName, out var gamePaths ) )
|
||||||
{
|
{
|
||||||
Selectable(0, ColorGreen);
|
Selectable( 0, ColorGreen );
|
||||||
|
|
||||||
ImGui.Indent(indent);
|
ImGui.Indent( indent );
|
||||||
foreach (var gamePath in gamePaths)
|
foreach( var gamePath in gamePaths )
|
||||||
{
|
{
|
||||||
string tmp = gamePath;
|
var tmp = gamePath;
|
||||||
if (ImGui.InputText($"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue))
|
if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue )
|
||||||
|
&& tmp != gamePath )
|
||||||
{
|
{
|
||||||
if (tmp != gamePath)
|
gamePaths.Remove( gamePath );
|
||||||
|
if( tmp.Length > 0 )
|
||||||
{
|
{
|
||||||
gamePaths.Remove(gamePath);
|
gamePaths.Add( tmp );
|
||||||
if (tmp.Length > 0)
|
|
||||||
gamePaths.Add(tmp);
|
|
||||||
_selector.SaveCurrentMod();
|
|
||||||
_selector.ReloadCurrentMod();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_selector.SaveCurrentMod();
|
||||||
|
_selector.ReloadCurrentMod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui.Unindent(indent);
|
|
||||||
|
ImGui.Unindent( indent );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Selectable(ColorYellow, ColorRed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawMultiSelectorCheckBox(InstallerInfo group, int idx, int flag, string label)
|
|
||||||
{
|
|
||||||
var opt = group.Options[idx];
|
|
||||||
var enabled = ( flag & (1 << idx)) != 0;
|
|
||||||
var oldEnabled = enabled;
|
|
||||||
if (ImGui.Checkbox(label, ref enabled))
|
|
||||||
{
|
{
|
||||||
if (oldEnabled != enabled)
|
Selectable( ColorYellow, ColorRed );
|
||||||
{
|
|
||||||
Mod.Conf[group.GroupName] ^= (1 << idx);
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawMultiSelector(InstallerInfo group)
|
private void DrawMultiSelectorCheckBox( InstallerInfo group, int idx, int flag, string label )
|
||||||
{
|
{
|
||||||
if (group.Options.Count == 0)
|
var opt = group.Options[ idx ];
|
||||||
return;
|
var enabled = ( flag & ( 1 << idx ) ) != 0;
|
||||||
|
var oldEnabled = enabled;
|
||||||
|
if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled )
|
||||||
|
{
|
||||||
|
Mod.Conf[ group.GroupName ] ^= 1 << idx;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGuiCustom.BeginFramedGroup(group.GroupName);
|
private void DrawMultiSelector( InstallerInfo group )
|
||||||
for(var i = 0; i < group.Options.Count; ++i)
|
{
|
||||||
DrawMultiSelectorCheckBox(group, i, Mod.Conf[group.GroupName], $"{group.Options[i].OptionName}##{group.GroupName}");
|
if( group.Options.Count == 0 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiCustom.BeginFramedGroup( group.GroupName );
|
||||||
|
for( var i = 0; i < group.Options.Count; ++i )
|
||||||
|
{
|
||||||
|
DrawMultiSelectorCheckBox( group, i, Mod.Conf[ group.GroupName ],
|
||||||
|
$"{group.Options[ i ].OptionName}##{group.GroupName}" );
|
||||||
|
}
|
||||||
|
|
||||||
ImGuiCustom.EndFramedGroup();
|
ImGuiCustom.EndFramedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawSingleSelector(InstallerInfo group)
|
private void DrawSingleSelector( InstallerInfo group )
|
||||||
{
|
{
|
||||||
if (group.Options.Count < 2)
|
if( group.Options.Count < 2 )
|
||||||
return;
|
|
||||||
var code = Mod.Conf[group.GroupName];
|
|
||||||
if( ImGui.Combo( group.GroupName, ref code, group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
|
|
||||||
{
|
{
|
||||||
Mod.Conf[group.GroupName] = code;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = Mod.Conf[ group.GroupName ];
|
||||||
|
if( ImGui.Combo( group.GroupName, ref code
|
||||||
|
, group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
|
||||||
|
{
|
||||||
|
Mod.Conf[ group.GroupName ] = code;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupSelectors()
|
private void DrawGroupSelectors()
|
||||||
{
|
{
|
||||||
foreach(var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Single ) )
|
foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Single ) )
|
||||||
DrawSingleSelector(g);
|
{
|
||||||
foreach(var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Multi ))
|
DrawSingleSelector( g );
|
||||||
DrawMultiSelector(g);
|
}
|
||||||
|
|
||||||
|
foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Multi ) )
|
||||||
|
{
|
||||||
|
DrawMultiSelector( g );
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawConfigurationTab()
|
private void DrawConfigurationTab()
|
||||||
{
|
{
|
||||||
if (!_editMode && !Meta.HasGroupWithConfig)
|
if( !_editMode && !Meta.HasGroupWithConfig )
|
||||||
return;
|
|
||||||
|
|
||||||
if(ImGui.BeginTabItem( LabelConfigurationTab ) )
|
|
||||||
{
|
{
|
||||||
if (_editMode)
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.BeginTabItem( LabelConfigurationTab ) )
|
||||||
|
{
|
||||||
|
if( _editMode )
|
||||||
|
{
|
||||||
DrawGroupSelectorsEdit();
|
DrawGroupSelectorsEdit();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
DrawGroupSelectors();
|
DrawGroupSelectors();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(bool editMode)
|
public void Draw( bool editMode )
|
||||||
{
|
{
|
||||||
_editMode = editMode;
|
_editMode = editMode;
|
||||||
ImGui.BeginTabBar( LabelPluginDetails );
|
ImGui.BeginTabBar( LabelPluginDetails );
|
||||||
|
|
@ -507,10 +640,15 @@ namespace Penumbra.UI
|
||||||
DrawAboutTab();
|
DrawAboutTab();
|
||||||
DrawChangedItemsTab();
|
DrawChangedItemsTab();
|
||||||
DrawConfigurationTab();
|
DrawConfigurationTab();
|
||||||
if (_editMode)
|
if( _editMode )
|
||||||
|
{
|
||||||
DrawFileListTabEdit();
|
DrawFileListTabEdit();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
DrawFileListTab();
|
DrawFileListTab();
|
||||||
|
}
|
||||||
|
|
||||||
DrawFileSwapTab();
|
DrawFileSwapTab();
|
||||||
DrawConflictTab();
|
DrawConflictTab();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
@ -15,34 +16,40 @@ namespace Penumbra.UI
|
||||||
private const string LabelNewMultiGroup = "New Multi Group";
|
private const string LabelNewMultiGroup = "New Multi Group";
|
||||||
private const string LabelGamePathsEdit = "Game Paths";
|
private const string LabelGamePathsEdit = "Game Paths";
|
||||||
private const string LabelGamePathsEditBox = "##gamePathsEdit";
|
private const string LabelGamePathsEditBox = "##gamePathsEdit";
|
||||||
private const string TextNoOptionAvailable = "[Not Available]";
|
|
||||||
private const string ButtonAddToGroup = "Add to Group";
|
private const string ButtonAddToGroup = "Add to Group";
|
||||||
private const string ButtonRemoveFromGroup = "Remove from Group";
|
private const string ButtonRemoveFromGroup = "Remove from Group";
|
||||||
private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines.";
|
private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines.";
|
||||||
|
private const string TextNoOptionAvailable = "[Not Available]";
|
||||||
private const string TextDefaultGamePath = "default";
|
private const string TextDefaultGamePath = "default";
|
||||||
private const char GamePathsSeparator = ';';
|
private const char GamePathsSeparator = ';';
|
||||||
private static readonly string TooltipFilesTabEdit = $"{TooltipFilesTab}\nRed Files are replaced in another group or a different option in this group, but not contained in the current option.";
|
|
||||||
private static readonly string TooltipGamePathsEdit = $"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\nUse '{TextDefaultGamePath}' to add the original file path.";
|
private static readonly string TooltipFilesTabEdit =
|
||||||
|
$"{TooltipFilesTab}\n" +
|
||||||
|
$"Red Files are replaced in another group or a different option in this group, but not contained in the current option.";
|
||||||
|
|
||||||
|
private static readonly string TooltipGamePathsEdit =
|
||||||
|
$"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n" +
|
||||||
|
$"Use '{TextDefaultGamePath}' to add the original file path.";
|
||||||
|
|
||||||
private const float MultiEditBoxWidth = 300f;
|
private const float MultiEditBoxWidth = 300f;
|
||||||
|
|
||||||
private bool DrawEditGroupSelector()
|
private bool DrawEditGroupSelector()
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth( OptionSelectionWidth );
|
ImGui.SetNextItemWidth( OptionSelectionWidth );
|
||||||
if (Meta.Groups.Count == 0)
|
if( Meta.Groups.Count == 0 )
|
||||||
{
|
{
|
||||||
ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, TextNoOptionAvailable, 1);
|
ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, TextNoOptionAvailable, 1 );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if( ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex
|
||||||
|
, Meta.Groups.Values.Select( G => G.GroupName ).ToArray()
|
||||||
|
, Meta.Groups.Count ) )
|
||||||
{
|
{
|
||||||
if (ImGui.Combo( LabelGroupSelect, ref _selectedGroupIndex, Meta.Groups.Values.Select( G => G.GroupName ).ToArray(), Meta.Groups.Count))
|
SelectGroup();
|
||||||
{
|
SelectOption( 0 );
|
||||||
SelectGroup();
|
|
||||||
SelectOption(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,15 +57,19 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetNextItemWidth( OptionSelectionWidth );
|
ImGui.SetNextItemWidth( OptionSelectionWidth );
|
||||||
if ((_selectedGroup?.Options.Count ?? 0) == 0)
|
if( ( _selectedGroup?.Options.Count ?? 0 ) == 0 )
|
||||||
{
|
{
|
||||||
ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, TextNoOptionAvailable, 1);
|
ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, TextNoOptionAvailable, 1 );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var group = (InstallerInfo) _selectedGroup;
|
var group = ( InstallerInfo )_selectedGroup;
|
||||||
if (ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, group.Options.Select(O => O.OptionName).ToArray(), group.Options.Count))
|
if( ImGui.Combo( LabelOptionSelect, ref _selectedOptionIndex, group.Options.Select( O => O.OptionName ).ToArray(),
|
||||||
|
group.Options.Count ) )
|
||||||
|
{
|
||||||
SelectOption();
|
SelectOption();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,13 +78,19 @@ namespace Penumbra.UI
|
||||||
if( ImGui.BeginTabItem( LabelFileListTab ) )
|
if( ImGui.BeginTabItem( LabelFileListTab ) )
|
||||||
{
|
{
|
||||||
UpdateFilenameList();
|
UpdateFilenameList();
|
||||||
if (ImGui.IsItemHovered())
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( _editMode ? TooltipFilesTabEdit : TooltipFilesTab );
|
ImGui.SetTooltip( _editMode ? TooltipFilesTabEdit : TooltipFilesTab );
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SetNextItemWidth( -1 );
|
ImGui.SetNextItemWidth( -1 );
|
||||||
if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize - new Vector2(0, 1.5f * ImGui.GetTextLineHeight()) ) )
|
if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize - new Vector2( 0, 1.5f * ImGui.GetTextLineHeight() ) ) )
|
||||||
for(var i = 0; i < Mod.Mod.ModFiles.Count; ++i)
|
{
|
||||||
DrawFileAndGamePaths(i);
|
for( var i = 0; i < Mod.Mod.ModFiles.Count; ++i )
|
||||||
|
{
|
||||||
|
DrawFileAndGamePaths( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.ListBoxFooter();
|
ImGui.ListBoxFooter();
|
||||||
|
|
||||||
|
|
@ -81,82 +98,90 @@ namespace Penumbra.UI
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_fullFilenameList = null;
|
_fullFilenameList = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawMultiSelectorEditBegin(InstallerInfo group)
|
private bool DrawMultiSelectorEditBegin( InstallerInfo group )
|
||||||
{
|
{
|
||||||
var groupName = group.GroupName;
|
var groupName = group.GroupName;
|
||||||
if (ImGuiCustom.BeginFramedGroupEdit(ref groupName)
|
if( ImGuiCustom.BeginFramedGroupEdit( ref groupName )
|
||||||
&& groupName != group.GroupName && !Meta.Groups.ContainsKey(groupName))
|
&& groupName != group.GroupName && !Meta.Groups.ContainsKey( groupName ) )
|
||||||
{
|
{
|
||||||
var oldConf = Mod.Conf[group.GroupName];
|
var oldConf = Mod.Conf[ group.GroupName ];
|
||||||
Meta.Groups.Remove(group.GroupName);
|
Meta.Groups.Remove( group.GroupName );
|
||||||
Mod.Conf.Remove(group.GroupName);
|
Mod.Conf.Remove( group.GroupName );
|
||||||
if (groupName.Length > 0)
|
if( groupName.Length > 0 )
|
||||||
{
|
{
|
||||||
Meta.Groups[groupName] = new(){ GroupName = groupName, SelectionType = SelectType.Multi, Options = group.Options };
|
Meta.Groups[ groupName ] = new InstallerInfo()
|
||||||
Mod.Conf[groupName] = oldConf;
|
{ GroupName = groupName, SelectionType = SelectType.Multi, Options = group.Options };
|
||||||
|
Mod.Conf[ groupName ] = oldConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
private void DrawMultiSelectorEditAdd(InstallerInfo group, float nameBoxStart)
|
|
||||||
|
private void DrawMultiSelectorEditAdd( InstallerInfo group, float nameBoxStart )
|
||||||
{
|
{
|
||||||
var newOption = "";
|
var newOption = "";
|
||||||
ImGui.SetCursorPosX(nameBoxStart);
|
ImGui.SetCursorPosX( nameBoxStart );
|
||||||
ImGui.SetNextItemWidth(MultiEditBoxWidth);
|
ImGui.SetNextItemWidth( MultiEditBoxWidth );
|
||||||
if (ImGui.InputText($"##new_{group.GroupName}_l", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
if( ImGui.InputText( $"##new_{group.GroupName}_l", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue )
|
||||||
|
&& newOption.Length != 0 )
|
||||||
{
|
{
|
||||||
if (newOption.Length != 0)
|
group.Options.Add( new Option()
|
||||||
{
|
{ OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< string, HashSet< string > >() } );
|
||||||
group.Options.Add(new(){ OptionName = newOption, OptionDesc = "", OptionFiles = new() });
|
_selector.SaveCurrentMod();
|
||||||
_selector.SaveCurrentMod();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawMultiSelectorEdit(InstallerInfo group)
|
private void DrawMultiSelectorEdit( InstallerInfo group )
|
||||||
{
|
{
|
||||||
var nameBoxStart = CheckMarkSize;
|
var nameBoxStart = CheckMarkSize;
|
||||||
var flag = Mod.Conf[group.GroupName];
|
var flag = Mod.Conf[ group.GroupName ];
|
||||||
|
|
||||||
var modChanged = DrawMultiSelectorEditBegin(group);
|
var modChanged = DrawMultiSelectorEditBegin( group );
|
||||||
|
|
||||||
for (var i = 0; i < group.Options.Count; ++i)
|
for( var i = 0; i < group.Options.Count; ++i )
|
||||||
{
|
{
|
||||||
var opt = group.Options[i];
|
var opt = group.Options[ i ];
|
||||||
var label = $"##{opt.OptionName}_{group.GroupName}";
|
var label = $"##{opt.OptionName}_{group.GroupName}";
|
||||||
DrawMultiSelectorCheckBox(group, i, flag, label);
|
DrawMultiSelectorCheckBox( group, i, flag, label );
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var newName = opt.OptionName;
|
var newName = opt.OptionName;
|
||||||
|
|
||||||
if (nameBoxStart == CheckMarkSize)
|
if( nameBoxStart == CheckMarkSize )
|
||||||
nameBoxStart = ImGui.GetCursorPosX();
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(MultiEditBoxWidth);
|
|
||||||
if (ImGui.InputText($"{label}_l", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
|
||||||
{
|
{
|
||||||
if (newName.Length == 0)
|
nameBoxStart = ImGui.GetCursorPosX();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth( MultiEditBoxWidth );
|
||||||
|
if( ImGui.InputText( $"{label}_l", ref newName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
|
{
|
||||||
|
if( newName.Length == 0 )
|
||||||
{
|
{
|
||||||
group.Options.RemoveAt(i);
|
group.Options.RemoveAt( i );
|
||||||
var bitmaskFront = (1 << i) - 1;
|
var bitmaskFront = ( 1 << i ) - 1;
|
||||||
Mod.Conf[group.GroupName] = (flag & bitmaskFront) | ((flag & ~bitmaskFront) >> 1);
|
Mod.Conf[ group.GroupName ] = ( flag & bitmaskFront ) | ( ( flag & ~bitmaskFront ) >> 1 );
|
||||||
modChanged = true;
|
modChanged = true;
|
||||||
}
|
}
|
||||||
else if (newName != opt.OptionName)
|
else if( newName != opt.OptionName )
|
||||||
{
|
{
|
||||||
group.Options[i] = new(){ OptionName = newName, OptionDesc = opt.OptionDesc, OptionFiles = opt.OptionFiles };
|
group.Options[ i ] = new Option()
|
||||||
|
{ OptionName = newName, OptionDesc = opt.OptionDesc, OptionFiles = opt.OptionFiles };
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawMultiSelectorEditAdd(group, nameBoxStart);
|
DrawMultiSelectorEditAdd( group, nameBoxStart );
|
||||||
|
|
||||||
if (modChanged)
|
if( modChanged )
|
||||||
{
|
{
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
Save();
|
Save();
|
||||||
|
|
@ -165,134 +190,164 @@ namespace Penumbra.UI
|
||||||
ImGuiCustom.EndFramedGroup();
|
ImGuiCustom.EndFramedGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawSingleSelectorEditGroup(InstallerInfo group)
|
private bool DrawSingleSelectorEditGroup( InstallerInfo group )
|
||||||
{
|
{
|
||||||
var groupName = group.GroupName;
|
var groupName = group.GroupName;
|
||||||
if (ImGui.InputText($"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue)
|
if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue )
|
||||||
&& !Meta.Groups.ContainsKey(groupName))
|
&& !Meta.Groups.ContainsKey( groupName ) )
|
||||||
{
|
{
|
||||||
var oldConf = Mod.Conf[group.GroupName];
|
var oldConf = Mod.Conf[ group.GroupName ];
|
||||||
if (groupName != group.GroupName)
|
if( groupName != group.GroupName )
|
||||||
{
|
{
|
||||||
Meta.Groups.Remove(group.GroupName);
|
Meta.Groups.Remove( group.GroupName );
|
||||||
Mod.Conf.Remove(group.GroupName);
|
Mod.Conf.Remove( group.GroupName );
|
||||||
}
|
}
|
||||||
if (groupName.Length > 0)
|
|
||||||
|
if( groupName.Length > 0 )
|
||||||
{
|
{
|
||||||
Meta.Groups.Add(groupName, new InstallerInfo(){ GroupName = groupName, Options = group.Options, SelectionType = SelectType.Single } );
|
Meta.Groups.Add( groupName,
|
||||||
Mod.Conf[groupName] = oldConf;
|
new InstallerInfo() { GroupName = groupName, Options = group.Options, SelectionType = SelectType.Single } );
|
||||||
|
Mod.Conf[ groupName ] = oldConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float DrawSingleSelectorEdit(InstallerInfo group)
|
private float DrawSingleSelectorEdit( InstallerInfo group )
|
||||||
{
|
{
|
||||||
var code = Mod.Conf[group.GroupName];
|
var code = Mod.Conf[ group.GroupName ];
|
||||||
var selectionChanged = false;
|
var selectionChanged = false;
|
||||||
var modChanged = false;
|
var modChanged = false;
|
||||||
var newName = "";
|
if( ImGuiCustom.RenameableCombo( $"##{group.GroupName}", ref code, out var newName,
|
||||||
if (ImGuiCustom.RenameableCombo($"##{group.GroupName}", ref code, ref newName, group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count))
|
group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
|
||||||
{
|
{
|
||||||
if (code == group.Options.Count)
|
if( code == group.Options.Count )
|
||||||
{
|
{
|
||||||
if (newName.Length > 0)
|
if( newName.Length > 0 )
|
||||||
{
|
{
|
||||||
selectionChanged = true;
|
selectionChanged = true;
|
||||||
modChanged = true;
|
modChanged = true;
|
||||||
Mod.Conf[group.GroupName] = code;
|
Mod.Conf[ group.GroupName ] = code;
|
||||||
group.Options.Add(new(){ OptionName = newName, OptionDesc = "", OptionFiles = new()});
|
group.Options.Add( new Option()
|
||||||
|
{ OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< string, HashSet< string > >() } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (newName.Length == 0)
|
if( newName.Length == 0 )
|
||||||
{
|
{
|
||||||
modChanged = true;
|
modChanged = true;
|
||||||
group.Options.RemoveAt(code);
|
group.Options.RemoveAt( code );
|
||||||
if (code >= group.Options.Count)
|
if( code >= group.Options.Count )
|
||||||
|
{
|
||||||
code = 0;
|
code = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (newName != group.Options[code].OptionName)
|
else if( newName != group.Options[ code ].OptionName )
|
||||||
{
|
{
|
||||||
modChanged = true;
|
modChanged = true;
|
||||||
group.Options[code] = new Option(){ OptionName = newName, OptionDesc = group.Options[code].OptionDesc, OptionFiles = group.Options[code].OptionFiles};
|
group.Options[ code ] = new Option()
|
||||||
|
{
|
||||||
|
OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc,
|
||||||
|
OptionFiles = group.Options[ code ].OptionFiles
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (Mod.Conf[group.GroupName] != code)
|
|
||||||
|
if( Mod.Conf[ group.GroupName ] != code )
|
||||||
{
|
{
|
||||||
selectionChanged = true;
|
selectionChanged = true;
|
||||||
Mod.Conf[group.GroupName] = code;
|
Mod.Conf[ group.GroupName ] = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var labelEditPos = ImGui.GetCursorPosX();
|
var labelEditPos = ImGui.GetCursorPosX();
|
||||||
modChanged |= DrawSingleSelectorEditGroup(group);
|
modChanged |= DrawSingleSelectorEditGroup( group );
|
||||||
|
|
||||||
if (modChanged)
|
if( modChanged )
|
||||||
|
{
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
|
}
|
||||||
|
|
||||||
if (selectionChanged)
|
if( selectionChanged )
|
||||||
|
{
|
||||||
Save();
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
return labelEditPos;
|
return labelEditPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddNewGroup(string newGroup, SelectType selectType)
|
private void AddNewGroup( string newGroup, SelectType selectType )
|
||||||
{
|
{
|
||||||
if (!Meta.Groups.ContainsKey(newGroup) && newGroup.Length > 0)
|
if( Meta.Groups.ContainsKey( newGroup ) || newGroup.Length <= 0 )
|
||||||
{
|
{
|
||||||
Meta.Groups[newGroup] = new ()
|
return;
|
||||||
{
|
|
||||||
GroupName = newGroup,
|
|
||||||
SelectionType = selectType,
|
|
||||||
Options = new()
|
|
||||||
} ;
|
|
||||||
|
|
||||||
Mod.Conf[newGroup] = 0;
|
|
||||||
_selector.SaveCurrentMod();
|
|
||||||
Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Meta.Groups[ newGroup ] = new InstallerInfo()
|
||||||
|
{
|
||||||
|
GroupName = newGroup,
|
||||||
|
SelectionType = selectType,
|
||||||
|
Options = new List< Option >()
|
||||||
|
};
|
||||||
|
|
||||||
|
Mod.Conf[ newGroup ] = 0;
|
||||||
|
_selector.SaveCurrentMod();
|
||||||
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAddSingleGroupField(float labelEditPos)
|
private void DrawAddSingleGroupField( float labelEditPos )
|
||||||
{
|
{
|
||||||
var newGroup = "";
|
var newGroup = "";
|
||||||
if(labelEditPos == CheckMarkSize)
|
if( labelEditPos == CheckMarkSize )
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosX(CheckMarkSize);
|
ImGui.SetCursorPosX( CheckMarkSize );
|
||||||
ImGui.SetNextItemWidth(MultiEditBoxWidth);
|
ImGui.SetNextItemWidth( MultiEditBoxWidth );
|
||||||
if (ImGui.InputText(LabelNewSingleGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
if( ImGui.InputText( LabelNewSingleGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
AddNewGroup(newGroup, SelectType.Single);
|
{
|
||||||
|
AddNewGroup( newGroup, SelectType.Single );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGuiCustom.RightJustifiedLabel(labelEditPos, LabelNewSingleGroup );
|
ImGuiCustom.RightJustifiedLabel( labelEditPos, LabelNewSingleGroup );
|
||||||
if (ImGui.InputText(LabelNewSingleGroupEdit, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
if( ImGui.InputText( LabelNewSingleGroupEdit, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
AddNewGroup(newGroup, SelectType.Single);
|
{
|
||||||
|
AddNewGroup( newGroup, SelectType.Single );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAddMultiGroupField()
|
private void DrawAddMultiGroupField()
|
||||||
{
|
{
|
||||||
var newGroup = "";
|
var newGroup = "";
|
||||||
ImGui.SetCursorPosX(CheckMarkSize);
|
ImGui.SetCursorPosX( CheckMarkSize );
|
||||||
ImGui.SetNextItemWidth(MultiEditBoxWidth);
|
ImGui.SetNextItemWidth( MultiEditBoxWidth );
|
||||||
if (ImGui.InputText(LabelNewMultiGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue))
|
if( ImGui.InputText( LabelNewMultiGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) )
|
||||||
AddNewGroup(newGroup, SelectType.Multi);
|
{
|
||||||
|
AddNewGroup( newGroup, SelectType.Multi );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupSelectorsEdit()
|
private void DrawGroupSelectorsEdit()
|
||||||
{
|
{
|
||||||
var labelEditPos = CheckMarkSize;
|
var labelEditPos = CheckMarkSize;
|
||||||
foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Single ) )
|
foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Single ) )
|
||||||
labelEditPos = DrawSingleSelectorEdit(g);
|
{
|
||||||
DrawAddSingleGroupField(labelEditPos);
|
labelEditPos = DrawSingleSelectorEdit( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawAddSingleGroupField( labelEditPos );
|
||||||
|
|
||||||
|
foreach( var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Multi ) )
|
||||||
|
{
|
||||||
|
DrawMultiSelectorEdit( g );
|
||||||
|
}
|
||||||
|
|
||||||
foreach(var g in Meta.Groups.Values.Where( g => g.SelectionType == SelectType.Multi ))
|
|
||||||
DrawMultiSelectorEdit(g);
|
|
||||||
DrawAddMultiGroupField();
|
DrawAddMultiGroupField();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,45 +16,48 @@ namespace Penumbra.UI
|
||||||
private const string LabelEditVersion = "##editVersion";
|
private const string LabelEditVersion = "##editVersion";
|
||||||
private const string LabelEditAuthor = "##editAuthor";
|
private const string LabelEditAuthor = "##editAuthor";
|
||||||
private const string LabelEditWebsite = "##editWebsite";
|
private const string LabelEditWebsite = "##editWebsite";
|
||||||
private const string ButtonOpenWebsite = "Open Website";
|
|
||||||
private const string LabelModEnabled = "Enabled";
|
private const string LabelModEnabled = "Enabled";
|
||||||
private const string LabelEditingEnabled = "Enable Editing";
|
private const string LabelEditingEnabled = "Enable Editing";
|
||||||
|
private const string ButtonOpenWebsite = "Open Website";
|
||||||
private const string ButtonOpenModFolder = "Open Mod Folder";
|
private const string ButtonOpenModFolder = "Open Mod Folder";
|
||||||
private const string TooltipOpenModFolder = "Open the directory containing this mod in your default file explorer.";
|
|
||||||
private const string ButtonEditJson = "Edit JSON";
|
private const string ButtonEditJson = "Edit JSON";
|
||||||
private const string TooltipEditJson = "Open the JSON configuration file in your default application for .json.";
|
|
||||||
private const string ButtonReloadJson = "Reload JSON";
|
private const string ButtonReloadJson = "Reload JSON";
|
||||||
private const string TooltipReloadJson = "Reload the configuration of all mods.";
|
|
||||||
private const string ButtonDeduplicate = "Deduplicate";
|
private const string ButtonDeduplicate = "Deduplicate";
|
||||||
private const string TooltipDeduplicate = "Try to find identical files and remove duplicate occurences to reduce the mods disk size. Introduces an invisible single-option Group \"Duplicates\".";
|
private const string TooltipOpenModFolder = "Open the directory containing this mod in your default file explorer.";
|
||||||
|
private const string TooltipEditJson = "Open the JSON configuration file in your default application for .json.";
|
||||||
|
private const string TooltipReloadJson = "Reload the configuration of all mods.";
|
||||||
|
|
||||||
private const float HeaderLineDistance = 10f;
|
private const string TooltipDeduplicate =
|
||||||
private static readonly Vector4 GreyColor = new( 1f, 1f, 1f, 0.66f );
|
"Try to find identical files and remove duplicate occurences to reduce the mods disk size.\n" +
|
||||||
|
"Introduces an invisible single-option Group \"Duplicates\".";
|
||||||
|
|
||||||
|
private const float HeaderLineDistance = 10f;
|
||||||
|
private static readonly Vector4 GreyColor = new( 1f, 1f, 1f, 0.66f );
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
private readonly Selector _selector;
|
private readonly Selector _selector;
|
||||||
public readonly PluginDetails _details;
|
public readonly PluginDetails Details;
|
||||||
|
|
||||||
private bool _editMode = false;
|
private bool _editMode;
|
||||||
private string _currentWebsite;
|
private string _currentWebsite;
|
||||||
private bool _validWebsite;
|
private bool _validWebsite;
|
||||||
|
|
||||||
public ModPanel(SettingsInterface ui, Selector s)
|
public ModPanel( SettingsInterface ui, Selector s )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_selector = s;
|
_selector = s;
|
||||||
_details = new(_base, _selector);
|
Details = new PluginDetails( _base, _selector );
|
||||||
_currentWebsite = Meta?.Website;
|
_currentWebsite = Meta?.Website;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModInfo Mod { get{ return _selector.Mod(); } }
|
private ModInfo Mod => _selector.Mod();
|
||||||
private ModMeta Meta { get{ return Mod?.Mod.Meta; } }
|
private ModMeta Meta => Mod?.Mod.Meta;
|
||||||
|
|
||||||
private void DrawName()
|
private void DrawName()
|
||||||
{
|
{
|
||||||
var name = Meta.Name;
|
var name = Meta.Name;
|
||||||
if (ImGuiCustom.InputOrText(_editMode, LabelEditName, ref name, 64)
|
if( ImGuiCustom.InputOrText( _editMode, LabelEditName, ref name, 64 )
|
||||||
&& name.Length > 0 && name != Meta.Name)
|
&& name.Length > 0 && name != Meta.Name )
|
||||||
{
|
{
|
||||||
Meta.Name = name;
|
Meta.Name = name;
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
|
|
@ -63,27 +66,27 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
private void DrawVersion()
|
private void DrawVersion()
|
||||||
{
|
{
|
||||||
if (_editMode)
|
if( _editMode )
|
||||||
{
|
{
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
ImGui.Text("(Version ");
|
ImGui.Text( "(Version " );
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
|
ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, ZeroVector );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var version = Meta.Version ?? "";
|
var version = Meta.Version ?? "";
|
||||||
if (ImGuiCustom.ResizingTextInput( LabelEditVersion, ref version, 16)
|
if( ImGuiCustom.ResizingTextInput( LabelEditVersion, ref version, 16 )
|
||||||
&& version != Meta.Version)
|
&& version != Meta.Version )
|
||||||
{
|
{
|
||||||
Meta.Version = version.Length > 0 ? version : null;
|
Meta.Version = version.Length > 0 ? version : null;
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.Text(")");
|
ImGui.Text( ")" );
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
ImGui.EndGroup();
|
ImGui.EndGroup();
|
||||||
}
|
}
|
||||||
else if ((Meta.Version?.Length ?? 0) > 0)
|
else if( ( Meta.Version?.Length ?? 0 ) > 0 )
|
||||||
{
|
{
|
||||||
ImGui.Text( $"(Version {Meta.Version})" );
|
ImGui.Text( $"(Version {Meta.Version})" );
|
||||||
}
|
}
|
||||||
|
|
@ -96,58 +99,62 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var author = Meta.Author ?? "";
|
var author = Meta.Author ?? "";
|
||||||
if (ImGuiCustom.InputOrText(_editMode, LabelEditAuthor, ref author, 64)
|
if( ImGuiCustom.InputOrText( _editMode, LabelEditAuthor, ref author, 64 )
|
||||||
&& author != Meta.Author)
|
&& author != Meta.Author )
|
||||||
{
|
{
|
||||||
Meta.Author = author.Length > 0 ? author : null;
|
Meta.Author = author.Length > 0 ? author : null;
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndGroup();
|
ImGui.EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawWebsite()
|
private void DrawWebsite()
|
||||||
{
|
{
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
if (_editMode)
|
if( _editMode )
|
||||||
{
|
{
|
||||||
ImGui.TextColored( GreyColor, "from" );
|
ImGui.TextColored( GreyColor, "from" );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var website = Meta.Website ?? "";
|
var website = Meta.Website ?? "";
|
||||||
if (ImGuiCustom.ResizingTextInput(LabelEditWebsite, ref website, 512)
|
if( ImGuiCustom.ResizingTextInput( LabelEditWebsite, ref website, 512 )
|
||||||
&& website != Meta.Website)
|
&& website != Meta.Website )
|
||||||
{
|
{
|
||||||
Meta.Website = website.Length > 0 ? website : null;
|
Meta.Website = website.Length > 0 ? website : null;
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (( Meta.Website?.Length ?? 0 ) > 0)
|
else if( ( Meta.Website?.Length ?? 0 ) > 0 )
|
||||||
{
|
{
|
||||||
if (_currentWebsite != Meta.Website)
|
if( _currentWebsite != Meta.Website )
|
||||||
{
|
{
|
||||||
_currentWebsite = Meta.Website;
|
_currentWebsite = Meta.Website;
|
||||||
_validWebsite = Uri.TryCreate( Meta.Website, UriKind.Absolute, out var uriResult )
|
_validWebsite = Uri.TryCreate( Meta.Website, UriKind.Absolute, out var uriResult )
|
||||||
&& ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp );
|
&& ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _validWebsite )
|
if( _validWebsite )
|
||||||
{
|
{
|
||||||
if( ImGui.SmallButton( ButtonOpenWebsite ) )
|
if( ImGui.SmallButton( ButtonOpenWebsite ) )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var process = new ProcessStartInfo(Meta.Website)
|
var process = new ProcessStartInfo( Meta.Website )
|
||||||
{
|
{
|
||||||
UseShellExecute = true
|
UseShellExecute = true
|
||||||
};
|
};
|
||||||
Process.Start(process);
|
Process.Start( process );
|
||||||
}
|
}
|
||||||
catch(System.ComponentModel.Win32Exception)
|
catch( System.ComponentModel.Win32Exception )
|
||||||
{
|
{
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( Meta.Website );
|
ImGui.SetTooltip( Meta.Website );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -156,6 +163,7 @@ namespace Penumbra.UI
|
||||||
ImGui.Text( Meta.Website );
|
ImGui.Text( Meta.Website );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndGroup();
|
ImGui.EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,13 +186,13 @@ namespace Penumbra.UI
|
||||||
Mod.Enabled = enabled;
|
Mod.Enabled = enabled;
|
||||||
_base._plugin.ModManager.Mods.Save();
|
_base._plugin.ModManager.Mods.Save();
|
||||||
_base._plugin.ModManager.CalculateEffectiveFileList();
|
_base._plugin.ModManager.CalculateEffectiveFileList();
|
||||||
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
|
_base._menu.EffectiveTab.RebuildFileList( _base._plugin.Configuration.ShowAdvanced );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawEditableMark()
|
private void DrawEditableMark()
|
||||||
{
|
{
|
||||||
ImGui.Checkbox( LabelEditingEnabled, ref _editMode);
|
ImGui.Checkbox( LabelEditingEnabled, ref _editMode );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawOpenModFolderButton()
|
private void DrawOpenModFolderButton()
|
||||||
|
|
@ -193,8 +201,11 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
Process.Start( Mod.Mod.ModBasePath.FullName );
|
Process.Start( Mod.Mod.ModBasePath.FullName );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipOpenModFolder );
|
ImGui.SetTooltip( TooltipOpenModFolder );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawEditJsonButton()
|
private void DrawEditJsonButton()
|
||||||
|
|
@ -203,8 +214,11 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
Process.Start( _selector.SaveCurrentMod() );
|
Process.Start( _selector.SaveCurrentMod() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipEditJson );
|
ImGui.SetTooltip( TooltipEditJson );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawReloadJsonButton()
|
private void DrawReloadJsonButton()
|
||||||
|
|
@ -213,22 +227,28 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
_selector.ReloadCurrentMod();
|
_selector.ReloadCurrentMod();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipReloadJson );
|
ImGui.SetTooltip( TooltipReloadJson );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawDeduplicateButton()
|
private void DrawDeduplicateButton()
|
||||||
{
|
{
|
||||||
if( ImGui.Button( ButtonDeduplicate ) )
|
if( ImGui.Button( ButtonDeduplicate ) )
|
||||||
{
|
{
|
||||||
new Deduplicator(Mod.Mod.ModBasePath, Meta).Run();
|
new Deduplicator( Mod.Mod.ModBasePath, Meta ).Run();
|
||||||
_selector.SaveCurrentMod();
|
_selector.SaveCurrentMod();
|
||||||
Mod.Mod.RefreshModFiles();
|
Mod.Mod.RefreshModFiles();
|
||||||
_base._plugin.ModManager.CalculateEffectiveFileList();
|
_base._plugin.ModManager.CalculateEffectiveFileList();
|
||||||
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
|
_base._menu.EffectiveTab.RebuildFileList( _base._plugin.Configuration.ShowAdvanced );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipDeduplicate );
|
ImGui.SetTooltip( TooltipDeduplicate );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawEditLine()
|
private void DrawEditLine()
|
||||||
|
|
@ -244,38 +264,44 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if( Mod != null )
|
if( Mod == null )
|
||||||
{
|
{
|
||||||
try
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true );
|
||||||
|
if( !ret )
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true );
|
return;
|
||||||
if (!ret)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DrawHeaderLine();
|
|
||||||
|
|
||||||
// Next line with fixed distance.
|
|
||||||
ImGuiCustom.VerticalDistance(HeaderLineDistance);
|
|
||||||
|
|
||||||
DrawEnabledMark();
|
|
||||||
if (_base._plugin.Configuration.ShowAdvanced)
|
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
|
||||||
DrawEditableMark();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next line, if editable.
|
|
||||||
if (_editMode)
|
|
||||||
DrawEditLine();
|
|
||||||
|
|
||||||
_details.Draw(_editMode);
|
|
||||||
|
|
||||||
ImGui.EndChild();
|
|
||||||
}
|
}
|
||||||
catch( Exception ex )
|
|
||||||
|
DrawHeaderLine();
|
||||||
|
|
||||||
|
// Next line with fixed distance.
|
||||||
|
ImGuiCustom.VerticalDistance( HeaderLineDistance );
|
||||||
|
|
||||||
|
DrawEnabledMark();
|
||||||
|
if( _base._plugin.Configuration.ShowAdvanced )
|
||||||
{
|
{
|
||||||
PluginLog.LogError( ex, "fuck" );
|
ImGui.SameLine();
|
||||||
|
DrawEditableMark();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next line, if editable.
|
||||||
|
if( _editMode )
|
||||||
|
{
|
||||||
|
DrawEditLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
Details.Draw( _editMode );
|
||||||
|
|
||||||
|
ImGui.EndChild();
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
PluginLog.LogError( ex, "fuck" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,21 +27,21 @@ namespace Penumbra.UI
|
||||||
private const uint DisabledModColor = 0xFF666666;
|
private const uint DisabledModColor = 0xFF666666;
|
||||||
private const uint ConflictingModColor = 0xFFAAAAFF;
|
private const uint ConflictingModColor = 0xFFAAAAFF;
|
||||||
|
|
||||||
private static readonly Vector2 SelectorButtonSizes = new(60, 0);
|
private static readonly Vector2 SelectorButtonSizes = new( 60, 0 );
|
||||||
private static readonly string ArrowUpString = FontAwesomeIcon.ArrowUp.ToIconString();
|
private static readonly string ArrowUpString = FontAwesomeIcon.ArrowUp.ToIconString();
|
||||||
private static readonly string ArrowDownString = FontAwesomeIcon.ArrowDown.ToIconString();
|
private static readonly string ArrowDownString = FontAwesomeIcon.ArrowDown.ToIconString();
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
private ModCollection Mods{ get{ return _base._plugin.ModManager.Mods; } }
|
private ModCollection Mods => _base._plugin.ModManager.Mods;
|
||||||
|
|
||||||
private ModInfo _mod = null;
|
private ModInfo _mod;
|
||||||
private int _index = 0;
|
private int _index;
|
||||||
private int? _deleteIndex = null;
|
private int? _deleteIndex;
|
||||||
private string _modFilter = "";
|
private string _modFilter = "";
|
||||||
private string[] _modNamesLower = null;
|
private string[] _modNamesLower;
|
||||||
|
|
||||||
|
|
||||||
public Selector(SettingsInterface ui)
|
public Selector( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
ResetModNamesLower();
|
ResetModNamesLower();
|
||||||
|
|
@ -52,16 +52,16 @@ namespace Penumbra.UI
|
||||||
_modNamesLower = Mods.ModSettings.Select( I => I.Mod.Meta.Name.ToLowerInvariant() ).ToArray();
|
_modNamesLower = Mods.ModSettings.Select( I => I.Mod.Meta.Name.ToLowerInvariant() ).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPriorityChangeButton(string iconString, bool up, int unavailableWhen)
|
private void DrawPriorityChangeButton( string iconString, bool up, int unavailableWhen )
|
||||||
{
|
{
|
||||||
ImGui.PushFont( UiBuilder.IconFont );
|
ImGui.PushFont( UiBuilder.IconFont );
|
||||||
if( _index != unavailableWhen )
|
if( _index != unavailableWhen )
|
||||||
{
|
{
|
||||||
if( ImGui.Button( iconString, SelectorButtonSizes ) )
|
if( ImGui.Button( iconString, SelectorButtonSizes ) )
|
||||||
{
|
{
|
||||||
SetSelection(_index);
|
SetSelection( _index );
|
||||||
_base._plugin.ModManager.ChangeModPriority( _mod, up );
|
_base._plugin.ModManager.ChangeModPriority( _mod, up );
|
||||||
_modNamesLower.Swap(_index, _index + (up ? 1 : -1));
|
_modNamesLower.Swap( _index, _index + ( up ? 1 : -1 ) );
|
||||||
_index += up ? 1 : -1;
|
_index += up ? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,10 +94,12 @@ namespace Penumbra.UI
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipDelete );
|
ImGui.SetTooltip( TooltipDelete );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawModAddButton()
|
private static void DrawModAddButton()
|
||||||
{
|
{
|
||||||
ImGui.PushFont( UiBuilder.IconFont );
|
ImGui.PushFont( UiBuilder.IconFont );
|
||||||
|
|
||||||
|
|
@ -109,19 +111,24 @@ namespace Penumbra.UI
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipAdd );
|
ImGui.SetTooltip( TooltipAdd );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawModsSelectorFilter()
|
private void DrawModsSelectorFilter()
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth( SelectorButtonSizes.X * 4 );
|
ImGui.SetNextItemWidth( SelectorButtonSizes.X * 4 );
|
||||||
string tmp = _modFilter;
|
var tmp = _modFilter;
|
||||||
if (ImGui.InputText(LabelModFilter, ref tmp, 256))
|
if( ImGui.InputText( LabelModFilter, ref tmp, 256 ) )
|
||||||
{
|
{
|
||||||
_modFilter = tmp.ToLowerInvariant();
|
_modFilter = tmp.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
ImGui.SetTooltip( TooltipModFilter );
|
ImGui.SetTooltip( TooltipModFilter );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawModsSelectorButtons()
|
private void DrawModsSelectorButtons()
|
||||||
|
|
@ -130,9 +137,9 @@ namespace Penumbra.UI
|
||||||
ImGui.PushStyleVar( ImGuiStyleVar.WindowPadding, ZeroVector );
|
ImGui.PushStyleVar( ImGuiStyleVar.WindowPadding, ZeroVector );
|
||||||
ImGui.PushStyleVar( ImGuiStyleVar.FrameRounding, 0 );
|
ImGui.PushStyleVar( ImGuiStyleVar.FrameRounding, 0 );
|
||||||
|
|
||||||
DrawPriorityChangeButton(ArrowUpString, false, 0);
|
DrawPriorityChangeButton( ArrowUpString, false, 0 );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawPriorityChangeButton(ArrowDownString, true, Mods?.ModSettings.Count - 1 ?? 0);
|
DrawPriorityChangeButton( ArrowDownString, true, Mods?.ModSettings.Count - 1 ?? 0 );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawModTrashButton();
|
DrawModTrashButton();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -141,16 +148,20 @@ namespace Penumbra.UI
|
||||||
ImGui.PopStyleVar( 3 );
|
ImGui.PopStyleVar( 3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawDeleteModal()
|
private void DrawDeleteModal()
|
||||||
{
|
{
|
||||||
if( _deleteIndex == null )
|
if( _deleteIndex == null )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.OpenPopup( DialogDeleteMod );
|
ImGui.OpenPopup( DialogDeleteMod );
|
||||||
|
|
||||||
var ret = ImGui.BeginPopupModal( DialogDeleteMod );
|
var ret = ImGui.BeginPopupModal( DialogDeleteMod );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( _mod?.Mod == null )
|
if( _mod?.Mod == null )
|
||||||
{
|
{
|
||||||
|
|
@ -185,8 +196,10 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
if (Mods == null)
|
if( Mods == null )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Selector pane
|
// Selector pane
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
|
|
@ -195,14 +208,16 @@ namespace Penumbra.UI
|
||||||
DrawModsSelectorFilter();
|
DrawModsSelectorFilter();
|
||||||
|
|
||||||
// Inlay selector list
|
// Inlay selector list
|
||||||
ImGui.BeginChild( LabelSelectorList, new Vector2(SelectorPanelWidth, -ImGui.GetFrameHeightWithSpacing() ), true );
|
ImGui.BeginChild( LabelSelectorList, new Vector2( SelectorPanelWidth, -ImGui.GetFrameHeightWithSpacing() ), true );
|
||||||
|
|
||||||
for( var modIndex = 0; modIndex < Mods.ModSettings.Count; modIndex++ )
|
for( var modIndex = 0; modIndex < Mods.ModSettings.Count; modIndex++ )
|
||||||
{
|
{
|
||||||
var settings = Mods.ModSettings[ modIndex ];
|
var settings = Mods.ModSettings[ modIndex ];
|
||||||
var modName = settings.Mod.Meta.Name;
|
var modName = settings.Mod.Meta.Name;
|
||||||
if (_modFilter.Length > 0 && !_modNamesLower[modIndex].Contains(_modFilter) )
|
if( _modFilter.Length > 0 && !_modNamesLower[ modIndex ].Contains( _modFilter ) )
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var changedColour = false;
|
var changedColour = false;
|
||||||
if( !settings.Enabled )
|
if( !settings.Enabled )
|
||||||
|
|
@ -226,10 +241,14 @@ namespace Penumbra.UI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if( changedColour )
|
if( changedColour )
|
||||||
|
{
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
if( selected )
|
if( selected )
|
||||||
SetSelection(modIndex, settings);
|
{
|
||||||
|
SetSelection( modIndex, settings );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
|
|
@ -242,26 +261,36 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
public ModInfo Mod() => _mod;
|
public ModInfo Mod() => _mod;
|
||||||
|
|
||||||
private void SetSelection(int idx, ModInfo info)
|
private void SetSelection( int idx, ModInfo info )
|
||||||
{
|
{
|
||||||
_mod = info;
|
_mod = info;
|
||||||
if (idx != _index)
|
if( idx != _index )
|
||||||
_base._menu._installedTab._modPanel._details.ResetState();
|
{
|
||||||
|
_base._menu.InstalledTab.ModPanel.Details.ResetState();
|
||||||
|
}
|
||||||
|
|
||||||
_index = idx;
|
_index = idx;
|
||||||
_deleteIndex = null;
|
_deleteIndex = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSelection(int idx)
|
private void SetSelection( int idx )
|
||||||
{
|
{
|
||||||
if (idx >= (Mods?.ModSettings?.Count ?? 0))
|
if( idx >= ( Mods?.ModSettings?.Count ?? 0 ) )
|
||||||
|
{
|
||||||
idx = -1;
|
idx = -1;
|
||||||
if (idx < 0)
|
}
|
||||||
SetSelection(0, null);
|
|
||||||
|
if( idx < 0 )
|
||||||
|
{
|
||||||
|
SetSelection( 0, null );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
SetSelection(idx, Mods.ModSettings[idx]);
|
{
|
||||||
|
SetSelection( idx, Mods.ModSettings[ idx ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearSelection() => SetSelection(-1);
|
public void ClearSelection() => SetSelection( -1 );
|
||||||
|
|
||||||
public void SelectModByName( string name )
|
public void SelectModByName( string name )
|
||||||
{
|
{
|
||||||
|
|
@ -270,40 +299,42 @@ namespace Penumbra.UI
|
||||||
var mod = Mods.ModSettings[ modIndex ];
|
var mod = Mods.ModSettings[ modIndex ];
|
||||||
|
|
||||||
if( mod.Mod.Meta.Name != name )
|
if( mod.Mod.Meta.Name != name )
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
SetSelection(modIndex, mod);
|
SetSelection( modIndex, mod );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCurrentModMetaFile()
|
private string GetCurrentModMetaFile()
|
||||||
{
|
=> _mod == null ? "" : Path.Combine( _mod.Mod.ModBasePath.FullName, "meta.json" );
|
||||||
if( _mod == null )
|
|
||||||
return "";
|
|
||||||
return Path.Combine( _mod.Mod.ModBasePath.FullName, "meta.json" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReloadCurrentMod()
|
public void ReloadCurrentMod()
|
||||||
{
|
{
|
||||||
var metaPath = GetCurrentModMetaFile();
|
var metaPath = GetCurrentModMetaFile();
|
||||||
if (metaPath.Length > 0 && File.Exists(metaPath))
|
if( metaPath.Length > 0 && File.Exists( metaPath ) )
|
||||||
{
|
{
|
||||||
_mod.Mod.Meta = ModMeta.LoadFromFile(metaPath) ?? _mod.Mod.Meta;
|
_mod.Mod.Meta = ModMeta.LoadFromFile( metaPath ) ?? _mod.Mod.Meta;
|
||||||
_base._menu._installedTab._modPanel._details.ResetState();
|
_base._menu.InstalledTab.ModPanel.Details.ResetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
_mod.Mod.RefreshModFiles();
|
_mod.Mod.RefreshModFiles();
|
||||||
_base._plugin.ModManager.CalculateEffectiveFileList();
|
_base._plugin.ModManager.CalculateEffectiveFileList();
|
||||||
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
|
_base._menu.EffectiveTab.RebuildFileList( _base._plugin.Configuration.ShowAdvanced );
|
||||||
ResetModNamesLower();
|
ResetModNamesLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SaveCurrentMod()
|
public string SaveCurrentMod()
|
||||||
{
|
{
|
||||||
var metaPath = GetCurrentModMetaFile();
|
var metaPath = GetCurrentModMetaFile();
|
||||||
if (metaPath.Length > 0)
|
if( metaPath.Length > 0 )
|
||||||
|
{
|
||||||
File.WriteAllText( metaPath, JsonConvert.SerializeObject( _mod.Mod.Meta, Formatting.Indented ) );
|
File.WriteAllText( metaPath, JsonConvert.SerializeObject( _mod.Mod.Meta, Formatting.Indented ) );
|
||||||
_base._menu._installedTab._modPanel._details.ResetState();
|
}
|
||||||
|
|
||||||
|
_base._menu.InstalledTab.ModPanel.Details.ResetState();
|
||||||
return metaPath;
|
return metaPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
private readonly SettingsInterface _base;
|
private readonly SettingsInterface _base;
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private bool _configChanged;
|
private bool _configChanged;
|
||||||
|
|
||||||
public TabSettings(SettingsInterface ui)
|
public TabSettings( SettingsInterface ui )
|
||||||
{
|
{
|
||||||
_base = ui;
|
_base = ui;
|
||||||
_config = _base._plugin.Configuration;
|
_config = _base._plugin.Configuration;
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Penumbra.UI
|
||||||
if( ImGui.InputText( LabelRootFolder, ref basePath, 255 ) && _config.CurrentCollection != basePath )
|
if( ImGui.InputText( LabelRootFolder, ref basePath, 255 ) && _config.CurrentCollection != basePath )
|
||||||
{
|
{
|
||||||
_config.CurrentCollection = basePath;
|
_config.CurrentCollection = basePath;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Penumbra.UI
|
||||||
if( ImGui.Button( LabelRediscoverButton ) )
|
if( ImGui.Button( LabelRediscoverButton ) )
|
||||||
{
|
{
|
||||||
_base.ReloadMods();
|
_base.ReloadMods();
|
||||||
_base._menu._installedTab._selector.ClearSelection();
|
_base._menu.InstalledTab.Selector.ClearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,8 +63,8 @@ namespace Penumbra.UI
|
||||||
if( ImGui.Checkbox( LabelEnabled, ref enabled ) )
|
if( ImGui.Checkbox( LabelEnabled, ref enabled ) )
|
||||||
{
|
{
|
||||||
_config.IsEnabled = enabled;
|
_config.IsEnabled = enabled;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
RefreshActors.RedrawAll(_base._plugin.PluginInterface.ClientState.Actors);
|
Game.RefreshActors.RedrawAll( _base._plugin.PluginInterface.ClientState.Actors );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,15 +85,17 @@ namespace Penumbra.UI
|
||||||
if( ImGui.Checkbox( LabelShowAdvanced, ref showAdvanced ) )
|
if( ImGui.Checkbox( LabelShowAdvanced, ref showAdvanced ) )
|
||||||
{
|
{
|
||||||
_config.ShowAdvanced = showAdvanced;
|
_config.ShowAdvanced = showAdvanced;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
_base._menu._effectiveTab.RebuildFileList(showAdvanced);
|
_base._menu.EffectiveTab.RebuildFileList( showAdvanced );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawLogLoadedFilesBox()
|
private void DrawLogLoadedFilesBox()
|
||||||
{
|
{
|
||||||
if( _base._plugin.ResourceLoader != null )
|
if( _base._plugin.ResourceLoader != null )
|
||||||
|
{
|
||||||
ImGui.Checkbox( LabelLogLoadedFiles, ref _base._plugin.ResourceLoader.LogAllFiles );
|
ImGui.Checkbox( LabelLogLoadedFiles, ref _base._plugin.ResourceLoader.LogAllFiles );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawDisableNotificationsBox()
|
private void DrawDisableNotificationsBox()
|
||||||
|
|
@ -102,7 +104,7 @@ namespace Penumbra.UI
|
||||||
if( ImGui.Checkbox( LabelDisableNotifications, ref fswatch ) )
|
if( ImGui.Checkbox( LabelDisableNotifications, ref fswatch ) )
|
||||||
{
|
{
|
||||||
_config.DisableFileSystemNotifications = fswatch;
|
_config.DisableFileSystemNotifications = fswatch;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,12 +114,16 @@ namespace Penumbra.UI
|
||||||
if( ImGui.Checkbox( LabelEnableHttpApi, ref http ) )
|
if( ImGui.Checkbox( LabelEnableHttpApi, ref http ) )
|
||||||
{
|
{
|
||||||
if( http )
|
if( http )
|
||||||
|
{
|
||||||
_base._plugin.CreateWebServer();
|
_base._plugin.CreateWebServer();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_base._plugin.ShutdownWebServer();
|
_base._plugin.ShutdownWebServer();
|
||||||
|
}
|
||||||
|
|
||||||
_config.EnableHttpApi = http;
|
_config.EnableHttpApi = http;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +147,9 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
var ret = ImGui.BeginTabItem( LabelTab );
|
var ret = ImGui.BeginTabItem( LabelTab );
|
||||||
if( !ret )
|
if( !ret )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DrawRootFolder();
|
DrawRootFolder();
|
||||||
|
|
||||||
|
|
@ -149,17 +157,19 @@ namespace Penumbra.UI
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawOpenModsButton();
|
DrawOpenModsButton();
|
||||||
|
|
||||||
ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
|
ImGuiCustom.VerticalDistance( DefaultVerticalSpace );
|
||||||
DrawEnabledBox();
|
DrawEnabledBox();
|
||||||
|
|
||||||
ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
|
ImGuiCustom.VerticalDistance( DefaultVerticalSpace );
|
||||||
DrawInvertModOrderBox();
|
DrawInvertModOrderBox();
|
||||||
|
|
||||||
ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
|
ImGuiCustom.VerticalDistance( DefaultVerticalSpace );
|
||||||
DrawShowAdvancedBox();
|
DrawShowAdvancedBox();
|
||||||
|
|
||||||
if( _config.ShowAdvanced )
|
if( _config.ShowAdvanced )
|
||||||
|
{
|
||||||
DrawAdvancedSettings();
|
DrawAdvancedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
if( _configChanged )
|
if( _configChanged )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,34 @@ namespace Penumbra
|
||||||
{
|
{
|
||||||
public static class ArrayExtensions
|
public static class ArrayExtensions
|
||||||
{
|
{
|
||||||
public static void Swap<T>( this T[] array, int idx1, int idx2 )
|
public static void Swap< T >( this T[] array, int idx1, int idx2 )
|
||||||
{
|
{
|
||||||
var tmp = array[idx1];
|
var tmp = array[ idx1 ];
|
||||||
array[idx1] = array[idx2];
|
array[ idx1 ] = array[ idx2 ];
|
||||||
array[idx2] = tmp;
|
array[ idx2 ] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Swap<T>( this List<T> array, int idx1, int idx2 )
|
public static void Swap< T >( this List< T > array, int idx1, int idx2 )
|
||||||
{
|
{
|
||||||
var tmp = array[idx1];
|
var tmp = array[ idx1 ];
|
||||||
array[idx1] = array[idx2];
|
array[ idx1 ] = array[ idx2 ];
|
||||||
array[idx2] = tmp;
|
array[ idx2 ] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Swap<T>( this T[] array, T lhs, T rhs )
|
public static int IndexOf< T >( this T[] array, Predicate< T > match )
|
||||||
|
{
|
||||||
|
for( var i = 0; i < array.Length; ++i )
|
||||||
|
{
|
||||||
|
if( match( array[ i ] ) )
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Swap< T >( this T[] array, T lhs, T rhs )
|
||||||
{
|
{
|
||||||
var idx1 = Array.IndexOf( array, lhs );
|
var idx1 = Array.IndexOf( array, lhs );
|
||||||
if( idx1 < 0 )
|
if( idx1 < 0 )
|
||||||
|
|
@ -36,7 +49,7 @@ namespace Penumbra
|
||||||
array.Swap( idx1, idx2 );
|
array.Swap( idx1, idx2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Swap<T>( this List<T> array, T lhs, T rhs )
|
public static void Swap< T >( this List< T > array, T lhs, T rhs )
|
||||||
{
|
{
|
||||||
var idx1 = array.IndexOf( lhs );
|
var idx1 = array.IndexOf( lhs );
|
||||||
if( idx1 < 0 )
|
if( idx1 < 0 )
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ namespace Penumbra.Util
|
||||||
{
|
{
|
||||||
var k = ( uint )i;
|
var k = ( uint )i;
|
||||||
for( var j = 0; j < 8; j++ )
|
for( var j = 0; j < 8; j++ )
|
||||||
|
{
|
||||||
k = ( k & 1 ) != 0 ? ( k >> 1 ) ^ Poly : k >> 1;
|
k = ( k & 1 ) != 0 ? ( k >> 1 ) ^ Poly : k >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
} ).ToArray();
|
} ).ToArray();
|
||||||
|
|
@ -40,14 +42,16 @@ namespace Penumbra.Util
|
||||||
public void Update( byte[] data )
|
public void Update( byte[] data )
|
||||||
{
|
{
|
||||||
foreach( var b in data )
|
foreach( var b in data )
|
||||||
|
{
|
||||||
Update( b );
|
Update( b );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl( MethodImplOptions.AggressiveInlining )]
|
[MethodImpl( MethodImplOptions.AggressiveInlining )]
|
||||||
public void Update( byte b )
|
public void Update( byte b )
|
||||||
{
|
{
|
||||||
_crc32 = CrcArray[ ( _crc32 ^ b ) & 0xFF ] ^
|
_crc32 = CrcArray[ ( _crc32 ^ b ) & 0xFF ] ^
|
||||||
( ( _crc32 >> 8 ) & 0x00FFFFFF );
|
( ( _crc32 >> 8 ) & 0x00FFFFFF );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,21 +3,16 @@ using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
public class SingleOrArrayConverter<T> : JsonConverter
|
public class SingleOrArrayConverter< T > : JsonConverter
|
||||||
{
|
{
|
||||||
public override bool CanConvert( Type objectType )
|
public override bool CanConvert( Type objectType ) => objectType == typeof( HashSet< T > );
|
||||||
{
|
|
||||||
return (objectType == typeof(HashSet<T>));
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Type == JTokenType.Array
|
||||||
{
|
? token.ToObject< HashSet< T > >()
|
||||||
return token.ToObject<HashSet<T>>();
|
: new HashSet< T > { token.ToObject< T >() };
|
||||||
}
|
|
||||||
return new HashSet<T>{ token.ToObject<T>() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
|
|
@ -28,22 +23,20 @@ public class SingleOrArrayConverter<T> : JsonConverter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DictSingleOrArrayConverter<T,U> : JsonConverter
|
public class DictSingleOrArrayConverter< T, U > : JsonConverter
|
||||||
{
|
{
|
||||||
public override bool CanConvert( Type objectType )
|
public override bool CanConvert( Type objectType ) => objectType == typeof( Dictionary< T, HashSet< U > > );
|
||||||
{
|
|
||||||
return (objectType == typeof(Dictionary<T, HashSet<U>>));
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
if( token.Type == JTokenType.Array )
|
||||||
{
|
{
|
||||||
return token.ToObject<HashSet<T>>();
|
return token.ToObject< HashSet< T > >();
|
||||||
}
|
}
|
||||||
return new HashSet<T>{ token.ToObject<T>() };
|
|
||||||
|
return new HashSet< T > { token.ToObject< T >() };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,11 @@ namespace Penumbra
|
||||||
public static class StringPathExtensions
|
public static class StringPathExtensions
|
||||||
{
|
{
|
||||||
private static readonly char[] _invalid = Path.GetInvalidFileNameChars();
|
private static readonly char[] _invalid = Path.GetInvalidFileNameChars();
|
||||||
|
|
||||||
public static string ReplaceInvalidPathSymbols( this string s, string replacement = "_" )
|
public static string ReplaceInvalidPathSymbols( this string s, string replacement = "_" )
|
||||||
{
|
=> string.Join( replacement, s.Split( _invalid ) );
|
||||||
return string.Join( replacement, s.Split( _invalid ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string RemoveInvalidPathSymbols( this string s )
|
public static string RemoveInvalidPathSymbols( this string s )
|
||||||
{
|
=> string.Concat( s.Split( _invalid ) );
|
||||||
return string.Concat( s.Split( _invalid ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue