mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 06:13:45 +01:00
Everything's a service.
This commit is contained in:
parent
2670ba52c1
commit
dd8c910597
45 changed files with 2155 additions and 2212 deletions
|
|
@ -62,7 +62,7 @@ public partial class Mod
|
|||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Could not normalize mod:\n{e}", "Failure", NotificationType.Error );
|
||||
Penumbra.ChatService.NotificationMessage( $"Could not normalize mod:\n{e}", "Failure", NotificationType.Error );
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -75,7 +75,7 @@ public partial class Mod
|
|||
{
|
||||
if( Directory.Exists( _normalizationDirName ) )
|
||||
{
|
||||
ChatUtil.NotificationMessage( "Could not normalize mod:\n"
|
||||
Penumbra.ChatService.NotificationMessage( "Could not normalize mod:\n"
|
||||
+ "The directory TmpNormalization may not already exist when normalizing a mod.", "Failure",
|
||||
NotificationType.Error );
|
||||
return false;
|
||||
|
|
@ -83,7 +83,7 @@ public partial class Mod
|
|||
|
||||
if( Directory.Exists( _oldDirName ) )
|
||||
{
|
||||
ChatUtil.NotificationMessage( "Could not normalize mod:\n"
|
||||
Penumbra.ChatService.NotificationMessage( "Could not normalize mod:\n"
|
||||
+ "The directory TmpNormalizationOld may not already exist when normalizing a mod.", "Failure",
|
||||
NotificationType.Error );
|
||||
return false;
|
||||
|
|
@ -173,7 +173,7 @@ public partial class Mod
|
|||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Could not normalize mod:\n{e}", "Failure", NotificationType.Error );
|
||||
Penumbra.ChatService.NotificationMessage( $"Could not normalize mod:\n{e}", "Failure", NotificationType.Error );
|
||||
_redirections = null;
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ public partial class Mod
|
|||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Could not move old files out of the way while normalizing mod mod:\n{e}", "Failure", NotificationType.Error );
|
||||
Penumbra.ChatService.NotificationMessage( $"Could not move old files out of the way while normalizing mod mod:\n{e}", "Failure", NotificationType.Error );
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -223,7 +223,7 @@ public partial class Mod
|
|||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Could not move new files into the mod while normalizing mod mod:\n{e}", "Failure", NotificationType.Error );
|
||||
Penumbra.ChatService.NotificationMessage( $"Could not move new files into the mod while normalizing mod mod:\n{e}", "Failure", NotificationType.Error );
|
||||
foreach( var dir in _mod.ModPath.EnumerateDirectories() )
|
||||
{
|
||||
if( dir.FullName.Equals( _oldDirName, StringComparison.OrdinalIgnoreCase )
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public partial class Mod
|
|||
}
|
||||
|
||||
// Return the state of the new potential name of a directory.
|
||||
public static NewDirectoryState NewDirectoryValid( string oldName, string newName, out DirectoryInfo? directory )
|
||||
public NewDirectoryState NewDirectoryValid( string oldName, string newName, out DirectoryInfo? directory )
|
||||
{
|
||||
directory = null;
|
||||
if( newName.Length == 0 )
|
||||
|
|
@ -182,7 +182,7 @@ public partial class Mod
|
|||
return NewDirectoryState.ContainsInvalidSymbols;
|
||||
}
|
||||
|
||||
directory = new DirectoryInfo( Path.Combine( Penumbra.ModManager.BasePath.FullName, fixedNewName ) );
|
||||
directory = new DirectoryInfo( Path.Combine( BasePath.FullName, fixedNewName ) );
|
||||
if( File.Exists( directory.FullName ) )
|
||||
{
|
||||
return NewDirectoryState.ExistsAsFile;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ImGuiScene;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
|
|
@ -14,43 +15,37 @@ public sealed partial class Mod
|
|||
{
|
||||
public sealed partial class Manager
|
||||
{
|
||||
public delegate void ModOptionChangeDelegate( ModOptionChangeType type, Mod mod, int groupIdx, int optionIdx, int movedToIdx );
|
||||
public delegate void ModOptionChangeDelegate(ModOptionChangeType type, Mod mod, int groupIdx, int optionIdx, int movedToIdx);
|
||||
public event ModOptionChangeDelegate ModOptionChanged;
|
||||
|
||||
public void ChangeModGroupType( Mod mod, int groupIdx, GroupType type )
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
if( group.Type == type )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mod._groups[ groupIdx ] = group.Convert( type );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupTypeChanged, mod, groupIdx, -1, -1 );
|
||||
}
|
||||
|
||||
public void ChangeModGroupDefaultOption( Mod mod, int groupIdx, uint defaultOption )
|
||||
public void ChangeModGroupType(Mod mod, int groupIdx, GroupType type)
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
if( group.DefaultSettings == defaultOption )
|
||||
{
|
||||
if (group.Type == type)
|
||||
return;
|
||||
}
|
||||
|
||||
group.DefaultSettings = defaultOption;
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.DefaultOptionChanged, mod, groupIdx, -1, -1 );
|
||||
mod._groups[groupIdx] = group.Convert(type);
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void RenameModGroup( Mod mod, int groupIdx, string newName )
|
||||
public void ChangeModGroupDefaultOption(Mod mod, int groupIdx, uint defaultOption)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
var oldName = group.Name;
|
||||
if( oldName == newName || !VerifyFileName( mod, group, newName, true ) )
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
if (group.DefaultSettings == defaultOption)
|
||||
return;
|
||||
}
|
||||
|
||||
group.DeleteFile( mod.ModPath, groupIdx );
|
||||
group.DefaultSettings = defaultOption;
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void RenameModGroup(Mod mod, int groupIdx, string newName)
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
var oldName = group.Name;
|
||||
if (oldName == newName || !VerifyFileName(mod, group, newName, true))
|
||||
return;
|
||||
|
||||
group.DeleteFile(mod.ModPath, groupIdx);
|
||||
|
||||
var _ = group switch
|
||||
{
|
||||
|
|
@ -59,61 +54,63 @@ public sealed partial class Mod
|
|||
_ => newName,
|
||||
};
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupRenamed, mod, groupIdx, -1, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void AddModGroup( Mod mod, GroupType type, string newName )
|
||||
public void AddModGroup(Mod mod, GroupType type, string newName)
|
||||
{
|
||||
if( !VerifyFileName( mod, null, newName, true ) )
|
||||
{
|
||||
if (!VerifyFileName(mod, null, newName, true))
|
||||
return;
|
||||
}
|
||||
|
||||
var maxPriority = mod._groups.Count == 0 ? 0 : mod._groups.Max( o => o.Priority ) + 1;
|
||||
var maxPriority = mod._groups.Count == 0 ? 0 : mod._groups.Max(o => o.Priority) + 1;
|
||||
|
||||
mod._groups.Add( type == GroupType.Multi
|
||||
? new MultiModGroup { Name = newName, Priority = maxPriority }
|
||||
: new SingleModGroup { Name = newName, Priority = maxPriority } );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupAdded, mod, mod._groups.Count - 1, -1, -1 );
|
||||
}
|
||||
|
||||
public void DeleteModGroup( Mod mod, int groupIdx )
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, -1, -1 );
|
||||
mod._groups.RemoveAt( groupIdx );
|
||||
UpdateSubModPositions( mod, groupIdx );
|
||||
group.DeleteFile( mod.ModPath, groupIdx );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupDeleted, mod, groupIdx, -1, -1 );
|
||||
}
|
||||
|
||||
public void MoveModGroup( Mod mod, int groupIdxFrom, int groupIdxTo )
|
||||
{
|
||||
if( mod._groups.Move( groupIdxFrom, groupIdxTo ) )
|
||||
{
|
||||
UpdateSubModPositions( mod, Math.Min( groupIdxFrom, groupIdxTo ) );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupMoved, mod, groupIdxFrom, -1, groupIdxTo );
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSubModPositions( Mod mod, int fromGroup )
|
||||
{
|
||||
foreach( var (group, groupIdx) in mod._groups.WithIndex().Skip( fromGroup ) )
|
||||
{
|
||||
foreach( var (o, optionIdx) in group.OfType< SubMod >().WithIndex() )
|
||||
mod._groups.Add(type == GroupType.Multi
|
||||
? new MultiModGroup
|
||||
{
|
||||
o.SetPosition( groupIdx, optionIdx );
|
||||
Name = newName,
|
||||
Priority = maxPriority,
|
||||
}
|
||||
: new SingleModGroup
|
||||
{
|
||||
Name = newName,
|
||||
Priority = maxPriority,
|
||||
});
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, mod._groups.Count - 1, -1, -1);
|
||||
}
|
||||
|
||||
public void DeleteModGroup(Mod mod, int groupIdx)
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, -1, -1);
|
||||
mod._groups.RemoveAt(groupIdx);
|
||||
UpdateSubModPositions(mod, groupIdx);
|
||||
group.DeleteFile(mod.ModPath, groupIdx);
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void MoveModGroup(Mod mod, int groupIdxFrom, int groupIdxTo)
|
||||
{
|
||||
if (mod._groups.Move(groupIdxFrom, groupIdxTo))
|
||||
{
|
||||
UpdateSubModPositions(mod, Math.Min(groupIdxFrom, groupIdxTo));
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, groupIdxFrom, -1, groupIdxTo);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeGroupDescription( Mod mod, int groupIdx, string newDescription )
|
||||
private static void UpdateSubModPositions(Mod mod, int fromGroup)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
if( group.Description == newDescription )
|
||||
foreach (var (group, groupIdx) in mod._groups.WithIndex().Skip(fromGroup))
|
||||
{
|
||||
return;
|
||||
foreach (var (o, optionIdx) in group.OfType<SubMod>().WithIndex())
|
||||
o.SetPosition(groupIdx, optionIdx);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeGroupDescription(Mod mod, int groupIdx, string newDescription)
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
if (group.Description == newDescription)
|
||||
return;
|
||||
|
||||
var _ = group switch
|
||||
{
|
||||
|
|
@ -121,29 +118,25 @@ public sealed partial class Mod
|
|||
MultiModGroup m => m.Description = newDescription,
|
||||
_ => newDescription,
|
||||
};
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.DisplayChange, mod, groupIdx, -1, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void ChangeOptionDescription( Mod mod, int groupIdx, int optionIdx, string newDescription )
|
||||
public void ChangeOptionDescription(Mod mod, int groupIdx, int optionIdx, string newDescription)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
var option = group[ optionIdx ];
|
||||
if( option.Description == newDescription || option is not SubMod s )
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
var option = group[optionIdx];
|
||||
if (option.Description == newDescription || option is not SubMod s)
|
||||
return;
|
||||
}
|
||||
|
||||
s.Description = newDescription;
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void ChangeGroupPriority( Mod mod, int groupIdx, int newPriority )
|
||||
public void ChangeGroupPriority(Mod mod, int groupIdx, int newPriority)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
if( group.Priority == newPriority )
|
||||
{
|
||||
var group = mod._groups[groupIdx];
|
||||
if (group.Priority == newPriority)
|
||||
return;
|
||||
}
|
||||
|
||||
var _ = group switch
|
||||
{
|
||||
|
|
@ -151,193 +144,174 @@ public sealed partial class Mod
|
|||
MultiModGroup m => m.Priority = newPriority,
|
||||
_ => newPriority,
|
||||
};
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PriorityChanged, mod, groupIdx, -1, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, mod, groupIdx, -1, -1);
|
||||
}
|
||||
|
||||
public void ChangeOptionPriority( Mod mod, int groupIdx, int optionIdx, int newPriority )
|
||||
public void ChangeOptionPriority(Mod mod, int groupIdx, int optionIdx, int newPriority)
|
||||
{
|
||||
switch( mod._groups[ groupIdx ] )
|
||||
switch (mod._groups[groupIdx])
|
||||
{
|
||||
case SingleModGroup:
|
||||
ChangeGroupPriority( mod, groupIdx, newPriority );
|
||||
ChangeGroupPriority(mod, groupIdx, newPriority);
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
if( m.PrioritizedOptions[ optionIdx ].Priority == newPriority )
|
||||
{
|
||||
if (m.PrioritizedOptions[optionIdx].Priority == newPriority)
|
||||
return;
|
||||
}
|
||||
|
||||
m.PrioritizedOptions[ optionIdx ] = ( m.PrioritizedOptions[ optionIdx ].Mod, newPriority );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PriorityChanged, mod, groupIdx, optionIdx, -1 );
|
||||
m.PrioritizedOptions[optionIdx] = (m.PrioritizedOptions[optionIdx].Mod, newPriority);
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, mod, groupIdx, optionIdx, -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameOption( Mod mod, int groupIdx, int optionIdx, string newName )
|
||||
public void RenameOption(Mod mod, int groupIdx, int optionIdx, string newName)
|
||||
{
|
||||
switch( mod._groups[ groupIdx ] )
|
||||
switch (mod._groups[groupIdx])
|
||||
{
|
||||
case SingleModGroup s:
|
||||
if( s.OptionData[ optionIdx ].Name == newName )
|
||||
{
|
||||
if (s.OptionData[optionIdx].Name == newName)
|
||||
return;
|
||||
}
|
||||
|
||||
s.OptionData[ optionIdx ].Name = newName;
|
||||
s.OptionData[optionIdx].Name = newName;
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
var option = m.PrioritizedOptions[ optionIdx ].Mod;
|
||||
if( option.Name == newName )
|
||||
{
|
||||
var option = m.PrioritizedOptions[optionIdx].Mod;
|
||||
if (option.Name == newName)
|
||||
return;
|
||||
}
|
||||
|
||||
option.Name = newName;
|
||||
break;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void AddOption( Mod mod, int groupIdx, string newName )
|
||||
public void AddOption(Mod mod, int groupIdx, string newName)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
var subMod = new SubMod( mod ) { Name = newName };
|
||||
subMod.SetPosition( groupIdx, group.Count );
|
||||
switch( group )
|
||||
var group = mod._groups[groupIdx];
|
||||
var subMod = new SubMod(mod) { Name = newName };
|
||||
subMod.SetPosition(groupIdx, group.Count);
|
||||
switch (group)
|
||||
{
|
||||
case SingleModGroup s:
|
||||
s.OptionData.Add( subMod );
|
||||
s.OptionData.Add(subMod);
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
m.PrioritizedOptions.Add( ( subMod, 0 ) );
|
||||
m.PrioritizedOptions.Add((subMod, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1);
|
||||
}
|
||||
|
||||
public void AddOption( Mod mod, int groupIdx, ISubMod option, int priority = 0 )
|
||||
public void AddOption(Mod mod, int groupIdx, ISubMod option, int priority = 0)
|
||||
{
|
||||
if( option is not SubMod o )
|
||||
{
|
||||
if (option is not SubMod o)
|
||||
return;
|
||||
}
|
||||
|
||||
var group = mod._groups[ groupIdx ];
|
||||
if( group.Count > 63 )
|
||||
var group = mod._groups[groupIdx];
|
||||
if (group.Count > 63)
|
||||
{
|
||||
Penumbra.Log.Error(
|
||||
$"Could not add option {option.Name} to {group.Name} for mod {mod.Name}, "
|
||||
+ "since only up to 64 options are supported in one group." );
|
||||
+ "since only up to 64 options are supported in one group.");
|
||||
return;
|
||||
}
|
||||
|
||||
o.SetPosition( groupIdx, group.Count );
|
||||
o.SetPosition(groupIdx, group.Count);
|
||||
|
||||
switch( group )
|
||||
switch (group)
|
||||
{
|
||||
case SingleModGroup s:
|
||||
s.OptionData.Add( o );
|
||||
s.OptionData.Add(o);
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
m.PrioritizedOptions.Add( ( o, priority ) );
|
||||
m.PrioritizedOptions.Add((o, priority));
|
||||
break;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1);
|
||||
}
|
||||
|
||||
public void DeleteOption( Mod mod, int groupIdx, int optionIdx )
|
||||
public void DeleteOption(Mod mod, int groupIdx, int optionIdx)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1 );
|
||||
switch( group )
|
||||
var group = mod._groups[groupIdx];
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
|
||||
switch (group)
|
||||
{
|
||||
case SingleModGroup s:
|
||||
s.OptionData.RemoveAt( optionIdx );
|
||||
s.OptionData.RemoveAt(optionIdx);
|
||||
|
||||
break;
|
||||
case MultiModGroup m:
|
||||
m.PrioritizedOptions.RemoveAt( optionIdx );
|
||||
m.PrioritizedOptions.RemoveAt(optionIdx);
|
||||
break;
|
||||
}
|
||||
|
||||
group.UpdatePositions( optionIdx );
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionDeleted, mod, groupIdx, optionIdx, -1 );
|
||||
group.UpdatePositions(optionIdx);
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionDeleted, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void MoveOption( Mod mod, int groupIdx, int optionIdxFrom, int optionIdxTo )
|
||||
public void MoveOption(Mod mod, int groupIdx, int optionIdxFrom, int optionIdxTo)
|
||||
{
|
||||
var group = mod._groups[ groupIdx ];
|
||||
if( group.MoveOption( optionIdxFrom, optionIdxTo ) )
|
||||
{
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionMoved, mod, groupIdx, optionIdxFrom, optionIdxTo );
|
||||
}
|
||||
var group = mod._groups[groupIdx];
|
||||
if (group.MoveOption(optionIdxFrom, optionIdxTo))
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionMoved, mod, groupIdx, optionIdxFrom, optionIdxTo);
|
||||
}
|
||||
|
||||
public void OptionSetManipulations( Mod mod, int groupIdx, int optionIdx, HashSet< MetaManipulation > manipulations )
|
||||
public void OptionSetManipulations(Mod mod, int groupIdx, int optionIdx, HashSet<MetaManipulation> manipulations)
|
||||
{
|
||||
var subMod = GetSubMod( mod, groupIdx, optionIdx );
|
||||
if( subMod.Manipulations.Count == manipulations.Count
|
||||
&& subMod.Manipulations.All( m => manipulations.TryGetValue( m, out var old ) && old.EntryEquals( m ) ) )
|
||||
{
|
||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||
if (subMod.Manipulations.Count == manipulations.Count
|
||||
&& subMod.Manipulations.All(m => manipulations.TryGetValue(m, out var old) && old.EntryEquals(m)))
|
||||
return;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
|
||||
subMod.ManipulationData = manipulations;
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionMetaChanged, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void OptionSetFiles( Mod mod, int groupIdx, int optionIdx, Dictionary< Utf8GamePath, FullPath > replacements )
|
||||
public void OptionSetFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> replacements)
|
||||
{
|
||||
var subMod = GetSubMod( mod, groupIdx, optionIdx );
|
||||
if( subMod.FileData.SetEquals( replacements ) )
|
||||
{
|
||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||
if (subMod.FileData.SetEquals(replacements))
|
||||
return;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
|
||||
subMod.FileData = replacements;
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionFilesChanged, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void OptionAddFiles( Mod mod, int groupIdx, int optionIdx, Dictionary< Utf8GamePath, FullPath > additions )
|
||||
public void OptionAddFiles(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> additions)
|
||||
{
|
||||
var subMod = GetSubMod( mod, groupIdx, optionIdx );
|
||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||
var oldCount = subMod.FileData.Count;
|
||||
subMod.FileData.AddFrom( additions );
|
||||
if( oldCount != subMod.FileData.Count )
|
||||
{
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionFilesAdded, mod, groupIdx, optionIdx, -1 );
|
||||
}
|
||||
subMod.FileData.AddFrom(additions);
|
||||
if (oldCount != subMod.FileData.Count)
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public void OptionSetFileSwaps( Mod mod, int groupIdx, int optionIdx, Dictionary< Utf8GamePath, FullPath > swaps )
|
||||
public void OptionSetFileSwaps(Mod mod, int groupIdx, int optionIdx, Dictionary<Utf8GamePath, FullPath> swaps)
|
||||
{
|
||||
var subMod = GetSubMod( mod, groupIdx, optionIdx );
|
||||
if( subMod.FileSwapData.SetEquals( swaps ) )
|
||||
{
|
||||
var subMod = GetSubMod(mod, groupIdx, optionIdx);
|
||||
if (subMod.FileSwapData.SetEquals(swaps))
|
||||
return;
|
||||
}
|
||||
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1);
|
||||
subMod.FileSwapData = swaps;
|
||||
ModOptionChanged.Invoke( ModOptionChangeType.OptionSwapsChanged, mod, groupIdx, optionIdx, -1 );
|
||||
ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, mod, groupIdx, optionIdx, -1);
|
||||
}
|
||||
|
||||
public static bool VerifyFileName( Mod mod, IModGroup? group, string newName, bool message )
|
||||
public bool VerifyFileName(Mod mod, IModGroup? group, string newName, bool message)
|
||||
{
|
||||
var path = newName.RemoveInvalidPathSymbols();
|
||||
if( path.Length == 0
|
||||
|| mod.Groups.Any( o => !ReferenceEquals( o, group )
|
||||
&& string.Equals( o.Name.RemoveInvalidPathSymbols(), path, StringComparison.OrdinalIgnoreCase ) ) )
|
||||
if (path.Length == 0
|
||||
|| mod.Groups.Any(o => !ReferenceEquals(o, group)
|
||||
&& string.Equals(o.Name.RemoveInvalidPathSymbols(), path, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if( message )
|
||||
{
|
||||
Penumbra.Log.Warning( $"Could not name option {newName} because option with same filename {path} already exists." );
|
||||
}
|
||||
if (message)
|
||||
_chat.NotificationMessage($"Could not name option {newName} because option with same filename {path} already exists.",
|
||||
"Warning", NotificationType.Warning);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -345,43 +319,35 @@ public sealed partial class Mod
|
|||
return true;
|
||||
}
|
||||
|
||||
private static SubMod GetSubMod( Mod mod, int groupIdx, int optionIdx )
|
||||
private static SubMod GetSubMod(Mod mod, int groupIdx, int optionIdx)
|
||||
{
|
||||
if( groupIdx == -1 && optionIdx == 0 )
|
||||
{
|
||||
if (groupIdx == -1 && optionIdx == 0)
|
||||
return mod._default;
|
||||
}
|
||||
|
||||
return mod._groups[ groupIdx ] switch
|
||||
return mod._groups[groupIdx] switch
|
||||
{
|
||||
SingleModGroup s => s.OptionData[ optionIdx ],
|
||||
MultiModGroup m => m.PrioritizedOptions[ optionIdx ].Mod,
|
||||
SingleModGroup s => s.OptionData[optionIdx],
|
||||
MultiModGroup m => m.PrioritizedOptions[optionIdx].Mod,
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
}
|
||||
|
||||
private static void OnModOptionChange( ModOptionChangeType type, Mod mod, int groupIdx, int _, int _2 )
|
||||
private static void OnModOptionChange(ModOptionChangeType type, Mod mod, int groupIdx, int _, int _2)
|
||||
{
|
||||
if( type == ModOptionChangeType.PrepareChange )
|
||||
{
|
||||
if (type == ModOptionChangeType.PrepareChange)
|
||||
return;
|
||||
}
|
||||
|
||||
// File deletion is handled in the actual function.
|
||||
if( type is ModOptionChangeType.GroupDeleted or ModOptionChangeType.GroupMoved )
|
||||
if (type is ModOptionChangeType.GroupDeleted or ModOptionChangeType.GroupMoved)
|
||||
{
|
||||
mod.SaveAllGroups();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( groupIdx == -1 )
|
||||
{
|
||||
if (groupIdx == -1)
|
||||
mod.SaveDefaultModDelayed();
|
||||
}
|
||||
else
|
||||
{
|
||||
IModGroup.SaveDelayed( mod._groups[ groupIdx ], mod.ModPath, groupIdx );
|
||||
}
|
||||
IModGroup.SaveDelayed(mod._groups[groupIdx], mod.ModPath, groupIdx);
|
||||
}
|
||||
|
||||
bool ComputeChangedItems()
|
||||
|
|
@ -396,20 +362,20 @@ public sealed partial class Mod
|
|||
ModOptionChangeType.GroupAdded => ComputeChangedItems() & mod.SetCounts(),
|
||||
ModOptionChangeType.GroupDeleted => ComputeChangedItems() & mod.SetCounts(),
|
||||
ModOptionChangeType.GroupMoved => false,
|
||||
ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any( o => o.IsOption ),
|
||||
ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any(o => o.IsOption),
|
||||
ModOptionChangeType.PriorityChanged => false,
|
||||
ModOptionChangeType.OptionAdded => ComputeChangedItems() & mod.SetCounts(),
|
||||
ModOptionChangeType.OptionDeleted => ComputeChangedItems() & mod.SetCounts(),
|
||||
ModOptionChangeType.OptionMoved => false,
|
||||
ModOptionChangeType.OptionFilesChanged => ComputeChangedItems()
|
||||
& ( 0 < ( mod.TotalFileCount = mod.AllSubMods.Sum( s => s.Files.Count ) ) ),
|
||||
& (0 < (mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count))),
|
||||
ModOptionChangeType.OptionSwapsChanged => ComputeChangedItems()
|
||||
& ( 0 < ( mod.TotalSwapCount = mod.AllSubMods.Sum( s => s.FileSwaps.Count ) ) ),
|
||||
& (0 < (mod.TotalSwapCount = mod.AllSubMods.Sum(s => s.FileSwaps.Count))),
|
||||
ModOptionChangeType.OptionMetaChanged => ComputeChangedItems()
|
||||
& ( 0 < ( mod.TotalManipulations = mod.AllSubMods.Sum( s => s.Manipulations.Count ) ) ),
|
||||
& (0 < (mod.TotalManipulations = mod.AllSubMods.Sum(s => s.Manipulations.Count))),
|
||||
ModOptionChangeType.DisplayChange => false,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@ public sealed partial class Mod
|
|||
}
|
||||
|
||||
_exportDirectory = null;
|
||||
Penumbra.Config.ExportDirectory = string.Empty;
|
||||
Penumbra.Config.Save();
|
||||
_config.ExportDirectory = string.Empty;
|
||||
_config.Save();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -166,8 +166,8 @@ public sealed partial class Mod
|
|||
|
||||
if( change )
|
||||
{
|
||||
Penumbra.Config.ExportDirectory = dir.FullName;
|
||||
Penumbra.Config.Save();
|
||||
_config.ExportDirectory = dir.FullName;
|
||||
_config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public sealed partial class Mod
|
||||
{
|
||||
public sealed partial class Manager : IReadOnlyList< Mod >
|
||||
public sealed partial class Manager : IReadOnlyList<Mod>
|
||||
{
|
||||
// Set when reading Config and migrating from v4 to v5.
|
||||
public static bool MigrateModBackups = false;
|
||||
|
|
@ -16,55 +17,60 @@ public sealed partial class Mod
|
|||
// Mods are added when they are created or imported.
|
||||
// Mods are removed when they are deleted or when they are toggled in any collection.
|
||||
// Also gets cleared on mod rediscovery.
|
||||
public readonly HashSet< Mod > NewMods = new();
|
||||
public readonly HashSet<Mod> NewMods = new();
|
||||
|
||||
private readonly List< Mod > _mods = new();
|
||||
private readonly List<Mod> _mods = new();
|
||||
|
||||
public Mod this[ int idx ]
|
||||
=> _mods[ idx ];
|
||||
public Mod this[int idx]
|
||||
=> _mods[idx];
|
||||
|
||||
public Mod this[ Index idx ]
|
||||
=> _mods[ idx ];
|
||||
public Mod this[Index idx]
|
||||
=> _mods[idx];
|
||||
|
||||
public int Count
|
||||
=> _mods.Count;
|
||||
|
||||
public IEnumerator< Mod > GetEnumerator()
|
||||
public IEnumerator<Mod> GetEnumerator()
|
||||
=> _mods.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public Manager( string modDirectory )
|
||||
private readonly Configuration _config;
|
||||
private readonly ChatService _chat;
|
||||
|
||||
public Manager(StartTracker time, Configuration config, ChatService chat)
|
||||
{
|
||||
using var timer = time.Measure(StartTimeType.Mods);
|
||||
_config = config;
|
||||
_chat = chat;
|
||||
ModDirectoryChanged += OnModDirectoryChange;
|
||||
SetBaseDirectory( modDirectory, true );
|
||||
UpdateExportDirectory( Penumbra.Config.ExportDirectory, false );
|
||||
SetBaseDirectory(config.ModDirectory, true);
|
||||
UpdateExportDirectory(_config.ExportDirectory, false);
|
||||
ModOptionChanged += OnModOptionChange;
|
||||
ModPathChanged += OnModPathChange;
|
||||
DiscoverMods();
|
||||
}
|
||||
|
||||
|
||||
// Try to obtain a mod by its directory name (unique identifier, preferred),
|
||||
// or the first mod of the given name if no directory fits.
|
||||
public bool TryGetMod( string modDirectory, string modName, [NotNullWhen( true )] out Mod? mod )
|
||||
public bool TryGetMod(string modDirectory, string modName, [NotNullWhen(true)] out Mod? mod)
|
||||
{
|
||||
mod = null;
|
||||
foreach( var m in _mods )
|
||||
foreach (var m in _mods)
|
||||
{
|
||||
if( string.Equals(m.ModPath.Name, modDirectory, StringComparison.OrdinalIgnoreCase) )
|
||||
if (string.Equals(m.ModPath.Name, modDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mod = m;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( m.Name == modName )
|
||||
{
|
||||
if (m.Name == modName)
|
||||
mod ??= m;
|
||||
}
|
||||
}
|
||||
|
||||
return mod != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public partial class Mod
|
|||
public DirectoryInfo ModPath { get; private set; }
|
||||
public int Index { get; private set; } = -1;
|
||||
|
||||
public bool IsTemporary
|
||||
=> Index < 0;
|
||||
|
||||
// Unused if Index < 0 but used for special temporary mods.
|
||||
public int Priority
|
||||
=> 0;
|
||||
|
|
|
|||
|
|
@ -4,52 +4,37 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Services;
|
||||
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
||||
public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISaveable
|
||||
{
|
||||
public static string ModFileSystemFile(DalamudPluginInterface pi)
|
||||
=> Path.Combine( pi.GetPluginConfigDirectory(), "sort_order.json" );
|
||||
|
||||
// Save the current sort order.
|
||||
// Does not save or copy the backup in the current mod directory,
|
||||
// as this is done on mod directory changes only.
|
||||
// TODO
|
||||
private void SaveFilesystem()
|
||||
{
|
||||
SaveToFile( new FileInfo( ModFileSystemFile(DalamudServices.PluginInterface) ), SaveMod, true );
|
||||
Penumbra.Log.Verbose( "Saved mod filesystem." );
|
||||
}
|
||||
|
||||
private void Save()
|
||||
=> Penumbra.Framework.RegisterDelayed( nameof( SaveFilesystem ), SaveFilesystem );
|
||||
private readonly Mod.Manager _modManager;
|
||||
private readonly FilenameService _files;
|
||||
|
||||
// Create a new ModFileSystem from the currently loaded mods and the current sort order file.
|
||||
public static ModFileSystem Load()
|
||||
public ModFileSystem(Mod.Manager modManager, FilenameService files)
|
||||
{
|
||||
var ret = new ModFileSystem();
|
||||
ret.Reload();
|
||||
|
||||
ret.Changed += ret.OnChange;
|
||||
Penumbra.ModManager.ModDiscoveryFinished += ret.Reload;
|
||||
Penumbra.ModManager.ModDataChanged += ret.OnDataChange;
|
||||
Penumbra.ModManager.ModPathChanged += ret.OnModPathChange;
|
||||
|
||||
return ret;
|
||||
_modManager = modManager;
|
||||
_files = files;
|
||||
Reload();
|
||||
Changed += OnChange;
|
||||
_modManager.ModDiscoveryFinished += Reload;
|
||||
_modManager.ModDataChanged += OnDataChange;
|
||||
_modManager.ModPathChanged += OnModPathChange;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Penumbra.ModManager.ModPathChanged -= OnModPathChange;
|
||||
Penumbra.ModManager.ModDiscoveryFinished -= Reload;
|
||||
Penumbra.ModManager.ModDataChanged -= OnDataChange;
|
||||
_modManager.ModPathChanged -= OnModPathChange;
|
||||
_modManager.ModDiscoveryFinished -= Reload;
|
||||
_modManager.ModDataChanged -= OnDataChange;
|
||||
}
|
||||
|
||||
public struct ImportDate : ISortMode< Mod >
|
||||
public struct ImportDate : ISortMode<Mod>
|
||||
{
|
||||
public string Name
|
||||
=> "Import Date (Older First)";
|
||||
|
|
@ -57,11 +42,11 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
|||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their import date.";
|
||||
|
||||
public IEnumerable< IPath > GetChildren( Folder f )
|
||||
=> f.GetSubFolders().Cast< IPath >().Concat( f.GetLeaves().OrderBy( l => l.Value.ImportDate ) );
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.ImportDate));
|
||||
}
|
||||
|
||||
public struct InverseImportDate : ISortMode< Mod >
|
||||
public struct InverseImportDate : ISortMode<Mod>
|
||||
{
|
||||
public string Name
|
||||
=> "Import Date (Newer First)";
|
||||
|
|
@ -69,71 +54,61 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
|||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse import date.";
|
||||
|
||||
public IEnumerable< IPath > GetChildren( Folder f )
|
||||
=> f.GetSubFolders().Cast< IPath >().Concat( f.GetLeaves().OrderByDescending( l => l.Value.ImportDate ) );
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.ImportDate));
|
||||
}
|
||||
|
||||
// Reload the whole filesystem from currently loaded mods and the current sort order file.
|
||||
// Used on construction and on mod rediscoveries.
|
||||
private void Reload()
|
||||
{
|
||||
{
|
||||
// TODO
|
||||
if( Load( new FileInfo( ModFileSystemFile(DalamudServices.PluginInterface) ), Penumbra.ModManager, ModToIdentifier, ModToName ) )
|
||||
{
|
||||
Save();
|
||||
}
|
||||
if (Load(new FileInfo(_files.FilesystemFile), _modManager, ModToIdentifier, ModToName))
|
||||
Penumbra.SaveService.ImmediateSave(this);
|
||||
|
||||
Penumbra.Log.Debug( "Reloaded mod filesystem." );
|
||||
Penumbra.Log.Debug("Reloaded mod filesystem.");
|
||||
}
|
||||
|
||||
// Save the filesystem on every filesystem change except full reloading.
|
||||
private void OnChange( FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3 )
|
||||
private void OnChange(FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
|
||||
{
|
||||
if( type != FileSystemChangeType.Reload )
|
||||
{
|
||||
Save();
|
||||
}
|
||||
if (type != FileSystemChangeType.Reload)
|
||||
Penumbra.SaveService.QueueSave(this);
|
||||
}
|
||||
|
||||
// Update sort order when defaulted mod names change.
|
||||
private void OnDataChange( ModDataChangeType type, Mod mod, string? oldName )
|
||||
private void OnDataChange(ModDataChangeType type, Mod mod, string? oldName)
|
||||
{
|
||||
if( type.HasFlag( ModDataChangeType.Name ) && oldName != null )
|
||||
if (type.HasFlag(ModDataChangeType.Name) && oldName != null)
|
||||
{
|
||||
var old = oldName.FixName();
|
||||
if( Find( old, out var child ) && child is not Folder )
|
||||
{
|
||||
Rename( child, mod.Name.Text );
|
||||
}
|
||||
if (Find(old, out var child) && child is not Folder)
|
||||
Rename(child, mod.Name.Text);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the filesystem if a mod has been added or removed.
|
||||
// Save it, if the mod directory has been moved, since this will change the save format.
|
||||
private void OnModPathChange( ModPathChangeType type, Mod mod, DirectoryInfo? oldPath, DirectoryInfo? newPath )
|
||||
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldPath, DirectoryInfo? newPath)
|
||||
{
|
||||
switch( type )
|
||||
switch (type)
|
||||
{
|
||||
case ModPathChangeType.Added:
|
||||
var originalName = mod.Name.Text.FixName();
|
||||
var name = originalName;
|
||||
var counter = 1;
|
||||
while( Find( name, out _ ) )
|
||||
{
|
||||
while (Find(name, out _))
|
||||
name = $"{originalName} ({++counter})";
|
||||
}
|
||||
|
||||
CreateLeaf( Root, name, mod );
|
||||
CreateLeaf(Root, name, mod);
|
||||
break;
|
||||
case ModPathChangeType.Deleted:
|
||||
if( FindLeaf( mod, out var leaf ) )
|
||||
{
|
||||
Delete( leaf );
|
||||
}
|
||||
if (FindLeaf(mod, out var leaf))
|
||||
Delete(leaf);
|
||||
|
||||
break;
|
||||
case ModPathChangeType.Moved:
|
||||
Save();
|
||||
Penumbra.SaveService.QueueSave(this);
|
||||
break;
|
||||
case ModPathChangeType.Reloaded:
|
||||
// Nothing
|
||||
|
|
@ -142,32 +117,44 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
|||
}
|
||||
|
||||
// Search the entire filesystem for the leaf corresponding to a mod.
|
||||
public bool FindLeaf( Mod mod, [NotNullWhen( true )] out Leaf? leaf )
|
||||
public bool FindLeaf(Mod mod, [NotNullWhen(true)] out Leaf? leaf)
|
||||
{
|
||||
leaf = Root.GetAllDescendants( ISortMode< Mod >.Lexicographical )
|
||||
.OfType< Leaf >()
|
||||
.FirstOrDefault( l => l.Value == mod );
|
||||
leaf = Root.GetAllDescendants(ISortMode<Mod>.Lexicographical)
|
||||
.OfType<Leaf>()
|
||||
.FirstOrDefault(l => l.Value == mod);
|
||||
return leaf != null;
|
||||
}
|
||||
|
||||
|
||||
// Used for saving and loading.
|
||||
private static string ModToIdentifier( Mod mod )
|
||||
private static string ModToIdentifier(Mod mod)
|
||||
=> mod.ModPath.Name;
|
||||
|
||||
private static string ModToName( Mod mod )
|
||||
private static string ModToName(Mod mod)
|
||||
=> mod.Name.Text.FixName();
|
||||
|
||||
// Return whether a mod has a custom path or is just a numbered default path.
|
||||
public static bool ModHasDefaultPath( Mod mod, string fullPath )
|
||||
public static bool ModHasDefaultPath(Mod mod, string fullPath)
|
||||
{
|
||||
var regex = new Regex( $@"^{Regex.Escape( ModToName( mod ) )}( \(\d+\))?$" );
|
||||
return regex.IsMatch( fullPath );
|
||||
var regex = new Regex($@"^{Regex.Escape(ModToName(mod))}( \(\d+\))?$");
|
||||
return regex.IsMatch(fullPath);
|
||||
}
|
||||
|
||||
private static (string, bool) SaveMod( Mod mod, string fullPath )
|
||||
private static (string, bool) SaveMod(Mod mod, string fullPath)
|
||||
// Only save pairs with non-default paths.
|
||||
=> ModHasDefaultPath( mod, fullPath )
|
||||
? ( string.Empty, false )
|
||||
: ( ModToIdentifier( mod ), true );
|
||||
}
|
||||
=> ModHasDefaultPath(mod, fullPath)
|
||||
? (string.Empty, false)
|
||||
: (ModToIdentifier(mod), true);
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.FilesystemFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
=> SaveToFile(writer, SaveMod, true);
|
||||
|
||||
public string TypeName
|
||||
=> "Mod File System";
|
||||
|
||||
public string LogName(string _)
|
||||
=> "to file";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,69 +21,67 @@ public partial class Mod
|
|||
public GroupType Type
|
||||
=> GroupType.Multi;
|
||||
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
|
||||
public int OptionPriority( Index idx )
|
||||
=> PrioritizedOptions[ idx ].Priority;
|
||||
public int OptionPriority(Index idx)
|
||||
=> PrioritizedOptions[idx].Priority;
|
||||
|
||||
public ISubMod this[ Index idx ]
|
||||
=> PrioritizedOptions[ idx ].Mod;
|
||||
public ISubMod this[Index idx]
|
||||
=> PrioritizedOptions[idx].Mod;
|
||||
|
||||
[JsonIgnore]
|
||||
public int Count
|
||||
=> PrioritizedOptions.Count;
|
||||
|
||||
public readonly List< (SubMod Mod, int Priority) > PrioritizedOptions = new();
|
||||
public readonly List<(SubMod Mod, int Priority)> PrioritizedOptions = new();
|
||||
|
||||
public IEnumerator< ISubMod > GetEnumerator()
|
||||
=> PrioritizedOptions.Select( o => o.Mod ).GetEnumerator();
|
||||
public IEnumerator<ISubMod> GetEnumerator()
|
||||
=> PrioritizedOptions.Select(o => o.Mod).GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public static MultiModGroup? Load( Mod mod, JObject json, int groupIdx )
|
||||
public static MultiModGroup? Load(Mod mod, JObject json, int groupIdx)
|
||||
{
|
||||
var ret = new MultiModGroup()
|
||||
{
|
||||
Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0,
|
||||
DefaultSettings = json[ nameof( DefaultSettings ) ]?.ToObject< uint >() ?? 0,
|
||||
Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty,
|
||||
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
||||
Priority = json[nameof(Priority)]?.ToObject<int>() ?? 0,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<uint>() ?? 0,
|
||||
};
|
||||
if( ret.Name.Length == 0 )
|
||||
{
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = json["Options"];
|
||||
if( options != null )
|
||||
{
|
||||
foreach( var child in options.Children() )
|
||||
if (options != null)
|
||||
foreach (var child in options.Children())
|
||||
{
|
||||
if( ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions )
|
||||
if (ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions)
|
||||
{
|
||||
ChatUtil.NotificationMessage( $"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", "Warning", NotificationType.Warning );
|
||||
Penumbra.ChatService.NotificationMessage(
|
||||
$"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", "Warning",
|
||||
NotificationType.Warning);
|
||||
break;
|
||||
}
|
||||
|
||||
var subMod = new SubMod( mod );
|
||||
subMod.SetPosition( groupIdx, ret.PrioritizedOptions.Count );
|
||||
subMod.Load( mod.ModPath, child, out var priority );
|
||||
ret.PrioritizedOptions.Add( ( subMod, priority ) );
|
||||
var subMod = new SubMod(mod);
|
||||
subMod.SetPosition(groupIdx, ret.PrioritizedOptions.Count);
|
||||
subMod.Load(mod.ModPath, child, out var priority);
|
||||
ret.PrioritizedOptions.Add((subMod, priority));
|
||||
}
|
||||
}
|
||||
|
||||
ret.DefaultSettings = (uint) (ret.DefaultSettings & ( ( 1ul << ret.Count ) - 1 ));
|
||||
ret.DefaultSettings = (uint)(ret.DefaultSettings & ((1ul << ret.Count) - 1));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IModGroup Convert( GroupType type )
|
||||
public IModGroup Convert(GroupType type)
|
||||
{
|
||||
switch( type )
|
||||
switch (type)
|
||||
{
|
||||
case GroupType.Multi: return this;
|
||||
case GroupType.Single:
|
||||
|
|
@ -92,32 +90,28 @@ public partial class Mod
|
|||
Name = Name,
|
||||
Description = Description,
|
||||
Priority = Priority,
|
||||
DefaultSettings = ( uint )Math.Max( Math.Min( Count - 1, BitOperations.TrailingZeroCount( DefaultSettings) ), 0 ),
|
||||
DefaultSettings = (uint)Math.Max(Math.Min(Count - 1, BitOperations.TrailingZeroCount(DefaultSettings)), 0),
|
||||
};
|
||||
multi.OptionData.AddRange( PrioritizedOptions.Select( p => p.Mod ) );
|
||||
multi.OptionData.AddRange(PrioritizedOptions.Select(p => p.Mod));
|
||||
return multi;
|
||||
default: throw new ArgumentOutOfRangeException( nameof( type ), type, null );
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveOption( int optionIdxFrom, int optionIdxTo )
|
||||
public bool MoveOption(int optionIdxFrom, int optionIdxTo)
|
||||
{
|
||||
if( !PrioritizedOptions.Move( optionIdxFrom, optionIdxTo ) )
|
||||
{
|
||||
if (!PrioritizedOptions.Move(optionIdxFrom, optionIdxTo))
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultSettings = Functions.MoveBit( DefaultSettings, optionIdxFrom, optionIdxTo );
|
||||
UpdatePositions( Math.Min( optionIdxFrom, optionIdxTo ) );
|
||||
DefaultSettings = Functions.MoveBit(DefaultSettings, optionIdxFrom, optionIdxTo);
|
||||
UpdatePositions(Math.Min(optionIdxFrom, optionIdxTo));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdatePositions( int from = 0 )
|
||||
public void UpdatePositions(int from = 0)
|
||||
{
|
||||
foreach( var ((o, _), i) in PrioritizedOptions.WithIndex().Skip( from ) )
|
||||
{
|
||||
o.SetPosition( o.GroupIdx, i );
|
||||
}
|
||||
foreach (var ((o, _), i) in PrioritizedOptions.WithIndex().Skip(from))
|
||||
o.SetPosition(o.GroupIdx, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue