mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Move mod creation functions to own subclass.
This commit is contained in:
parent
c2ac745d72
commit
1f942491ac
13 changed files with 237 additions and 233 deletions
|
|
@ -829,7 +829,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public PenumbraApiEc CreateNamedTemporaryCollection( string name )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( name.Length == 0 || Mod.ReplaceBadXivSymbols( name ) != name )
|
||||
if( name.Length == 0 || Mod.Creator.ReplaceBadXivSymbols( name ) != name )
|
||||
{
|
||||
return PenumbraApiEc.InvalidArgument;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public partial class TexToolsImporter
|
|||
};
|
||||
Penumbra.Log.Information( $" -> Importing {archive.Type} Archive." );
|
||||
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, Path.GetRandomFileName() );
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, Path.GetRandomFileName() );
|
||||
var options = new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
|
|
@ -99,13 +99,13 @@ public partial class TexToolsImporter
|
|||
// Use either the top-level directory as the mods base name, or the (fixed for path) name in the json.
|
||||
if( leadDir )
|
||||
{
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, baseName, false );
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, baseName, false );
|
||||
Directory.Move( Path.Combine( oldName, baseName ), _currentModDirectory.FullName );
|
||||
Directory.Delete( oldName );
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, name, false );
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, name, false );
|
||||
Directory.Move( oldName, _currentModDirectory.FullName );
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +114,7 @@ public partial class TexToolsImporter
|
|||
return _currentModDirectory;
|
||||
}
|
||||
|
||||
|
||||
// Search the archive for the meta.json file which needs to exist.
|
||||
private static string FindArchiveModMeta( IArchive archive, out bool leadDir )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ public partial class TexToolsImporter
|
|||
|
||||
var modList = modListRaw.Select( m => JsonConvert.DeserializeObject< SimpleMod >( m, JsonSettings )! ).ToList();
|
||||
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) );
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) );
|
||||
// Create a new ModMeta from the TTMP mod list info
|
||||
Mod.CreateMeta( _currentModDirectory, _currentModName, DefaultTexToolsData.Author, DefaultTexToolsData.Description, null, null );
|
||||
Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, DefaultTexToolsData.Author, DefaultTexToolsData.Description, null, null );
|
||||
|
||||
// Open the mod data file from the mod pack as a SqPackStream
|
||||
_streamDisposer = GetSqPackStreamStream( extractedModPack, "TTMPD.mpd" );
|
||||
ExtractSimpleModList( _currentModDirectory, modList );
|
||||
Mod.CreateDefaultFiles( _currentModDirectory );
|
||||
Mod.Creator.CreateDefaultFiles( _currentModDirectory );
|
||||
ResetStreamDisposer();
|
||||
return _currentModDirectory;
|
||||
}
|
||||
|
|
@ -90,15 +90,15 @@ public partial class TexToolsImporter
|
|||
_currentOptionName = DefaultTexToolsData.DefaultOption;
|
||||
Penumbra.Log.Information( " -> Importing Simple V2 ModPack" );
|
||||
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, _currentModName );
|
||||
Mod.CreateMeta( _currentModDirectory, _currentModName, modList.Author, string.IsNullOrEmpty( modList.Description )
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, _currentModName );
|
||||
Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, modList.Author, string.IsNullOrEmpty( modList.Description )
|
||||
? "Mod imported from TexTools mod pack"
|
||||
: modList.Description, modList.Version, modList.Url );
|
||||
|
||||
// Open the mod data file from the mod pack as a SqPackStream
|
||||
_streamDisposer = GetSqPackStreamStream( extractedModPack, "TTMPD.mpd" );
|
||||
ExtractSimpleModList( _currentModDirectory, modList.SimpleModsList );
|
||||
Mod.CreateDefaultFiles( _currentModDirectory );
|
||||
Mod.Creator.CreateDefaultFiles( _currentModDirectory );
|
||||
ResetStreamDisposer();
|
||||
return _currentModDirectory;
|
||||
}
|
||||
|
|
@ -135,8 +135,8 @@ public partial class TexToolsImporter
|
|||
_currentNumOptions = GetOptionCount( modList );
|
||||
_currentModName = modList.Name;
|
||||
|
||||
_currentModDirectory = Mod.CreateModFolder( _baseDirectory, _currentModName );
|
||||
Mod.CreateMeta( _currentModDirectory, _currentModName, modList.Author, modList.Description, modList.Version, modList.Url );
|
||||
_currentModDirectory = Mod.Creator.CreateModFolder( _baseDirectory, _currentModName );
|
||||
Mod.Creator.CreateMeta( _currentModDirectory, _currentModName, modList.Author, modList.Description, modList.Version, modList.Url );
|
||||
|
||||
if( _currentNumOptions == 0 )
|
||||
{
|
||||
|
|
@ -173,7 +173,7 @@ public partial class TexToolsImporter
|
|||
{
|
||||
var name = numGroups == 1 ? _currentGroupName : $"{_currentGroupName}, Part {groupId + 1}";
|
||||
options.Clear();
|
||||
var groupFolder = Mod.NewSubFolderName( _currentModDirectory, name )
|
||||
var groupFolder = Mod.Creator.NewSubFolderName( _currentModDirectory, name )
|
||||
?? new DirectoryInfo( Path.Combine( _currentModDirectory.FullName,
|
||||
numGroups == 1 ? $"Group {groupPriority + 1}" : $"Group {groupPriority + 1}, Part {groupId + 1}" ) );
|
||||
|
||||
|
|
@ -183,10 +183,10 @@ public partial class TexToolsImporter
|
|||
var option = allOptions[ i + optionIdx ];
|
||||
_token.ThrowIfCancellationRequested();
|
||||
_currentOptionName = option.Name;
|
||||
var optionFolder = Mod.NewSubFolderName( groupFolder, option.Name )
|
||||
var optionFolder = Mod.Creator.NewSubFolderName( groupFolder, option.Name )
|
||||
?? new DirectoryInfo( Path.Combine( groupFolder.FullName, $"Option {i + optionIdx + 1}" ) );
|
||||
ExtractSimpleModList( optionFolder, option.ModsJsons );
|
||||
options.Add( Mod.CreateSubMod( _currentModDirectory, optionFolder, option ) );
|
||||
options.Add( Mod.Creator.CreateSubMod( _currentModDirectory, optionFolder, option ) );
|
||||
if( option.IsChecked )
|
||||
{
|
||||
defaultSettings = group.SelectionType == GroupType.Multi
|
||||
|
|
@ -207,12 +207,12 @@ public partial class TexToolsImporter
|
|||
if( empty != null )
|
||||
{
|
||||
_currentOptionName = empty.Name;
|
||||
options.Insert( 0, Mod.CreateEmptySubMod( empty.Name ) );
|
||||
options.Insert( 0, Mod.Creator.CreateEmptySubMod( empty.Name ) );
|
||||
defaultSettings = defaultSettings == null ? 0 : defaultSettings.Value + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Mod.CreateOptionGroup( _currentModDirectory, group.SelectionType, name, groupPriority, groupPriority,
|
||||
Mod.Creator.CreateOptionGroup( _currentModDirectory, group.SelectionType, name, groupPriority, groupPriority,
|
||||
defaultSettings ?? 0, group.Description, options );
|
||||
++groupPriority;
|
||||
}
|
||||
|
|
@ -220,7 +220,7 @@ public partial class TexToolsImporter
|
|||
}
|
||||
|
||||
ResetStreamDisposer();
|
||||
Mod.CreateDefaultFiles( _currentModDirectory );
|
||||
Mod.Creator.CreateDefaultFiles( _currentModDirectory );
|
||||
return _currentModDirectory;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -150,11 +150,11 @@ public partial class Mod
|
|||
foreach( var (group, groupIdx) in _mod.Groups.WithIndex() )
|
||||
{
|
||||
_redirections[ groupIdx + 1 ] = new Dictionary< Utf8GamePath, FullPath >[group.Count];
|
||||
var groupDir = CreateModFolder( directory, group.Name );
|
||||
var groupDir = Creator.CreateModFolder( directory, group.Name );
|
||||
|
||||
foreach( var option in group.OfType< SubMod >() )
|
||||
{
|
||||
var optionDir = CreateModFolder( groupDir, option.Name );
|
||||
var optionDir = Creator.CreateModFolder( groupDir, option.Name );
|
||||
newDict = new Dictionary< Utf8GamePath, FullPath >( option.FileData.Count );
|
||||
_redirections[ groupIdx + 1 ][ option.OptionIdx ] = newDict;
|
||||
foreach( var (gamePath, fullPath) in option.FileData )
|
||||
|
|
|
|||
|
|
@ -64,12 +64,6 @@ public static class EquipmentSwap
|
|||
var imcFileTo = new ImcFile( imcManip);
|
||||
|
||||
var isAccessory = slot.IsAccessory();
|
||||
var estType = slot switch
|
||||
{
|
||||
EquipSlot.Head => EstManipulation.EstType.Head,
|
||||
EquipSlot.Body => EstManipulation.EstType.Body,
|
||||
_ => ( EstManipulation.EstType )0,
|
||||
};
|
||||
|
||||
var skipFemale = false;
|
||||
var skipMale = false;
|
||||
|
|
@ -89,12 +83,6 @@ public static class EquipmentSwap
|
|||
continue;
|
||||
}
|
||||
|
||||
var est = ItemSwap.CreateEst( redirections, manips, estType, gr, idFrom, idTo );
|
||||
if( est != null )
|
||||
{
|
||||
swaps.Add( est );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var eqdp = CreateEqdp( redirections, manips, slot, gr, idFrom, idTo, mtrlVariantTo );
|
||||
|
|
@ -140,6 +128,19 @@ public static class EquipmentSwap
|
|||
{
|
||||
var mdl = CreateMdl( redirections, slot, gr, idFrom, idTo, mtrlTo );
|
||||
meta.ChildSwaps.Add( mdl );
|
||||
|
||||
var estType = slot switch
|
||||
{
|
||||
EquipSlot.Head => EstManipulation.EstType.Head,
|
||||
EquipSlot.Body => EstManipulation.EstType.Body,
|
||||
_ => ( EstManipulation.EstType )0,
|
||||
};
|
||||
|
||||
var est = ItemSwap.CreateEst( redirections, manips, estType, gr, idFrom, idTo );
|
||||
if( est != null )
|
||||
{
|
||||
meta.ChildSwaps.Add( est );
|
||||
}
|
||||
}
|
||||
else if( !ownMtrl && meta.SwapAppliedIsDefault )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ public partial class Mod
|
|||
return NewDirectoryState.Identical;
|
||||
}
|
||||
|
||||
var fixedNewName = ReplaceBadXivSymbols( newName );
|
||||
var fixedNewName = Creator.ReplaceBadXivSymbols( newName );
|
||||
if( fixedNewName != newName )
|
||||
{
|
||||
return NewDirectoryState.ContainsInvalidSymbols;
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Dalamud.Utility;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Import;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class Mod
|
||||
{
|
||||
/// <summary>
|
||||
/// Create and return a new directory based on the given directory and name, that is <br/>
|
||||
/// - Not Empty.<br/>
|
||||
/// - Unique, by appending (digit) for duplicates.<br/>
|
||||
/// - Containing no symbols invalid for FFXIV or windows paths.<br/>
|
||||
/// </summary>
|
||||
/// <param name="outDirectory"></param>
|
||||
/// <param name="modListName"></param>
|
||||
/// <param name="create"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="IOException"></exception>
|
||||
internal static DirectoryInfo CreateModFolder( DirectoryInfo outDirectory, string modListName, bool create = true )
|
||||
{
|
||||
var name = modListName;
|
||||
if( name.Length == 0 )
|
||||
{
|
||||
name = "_";
|
||||
}
|
||||
|
||||
var newModFolderBase = NewOptionDirectory( outDirectory, name );
|
||||
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
|
||||
if( newModFolder.Length == 0 )
|
||||
{
|
||||
throw new IOException( "Could not create mod folder: too many folders of the same name exist." );
|
||||
}
|
||||
|
||||
if( create )
|
||||
{
|
||||
Directory.CreateDirectory( newModFolder );
|
||||
}
|
||||
|
||||
return new DirectoryInfo( newModFolder );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the name for a group or option subfolder based on its parent folder and given name.
|
||||
/// subFolderName should never be empty, and the result is unique and contains no invalid symbols.
|
||||
/// </summary>
|
||||
internal static DirectoryInfo? NewSubFolderName( DirectoryInfo parentFolder, string subFolderName )
|
||||
{
|
||||
var newModFolderBase = NewOptionDirectory( parentFolder, subFolderName );
|
||||
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
|
||||
return newModFolder.Length == 0 ? null : new DirectoryInfo( newModFolder );
|
||||
}
|
||||
|
||||
// Create the file containing the meta information about a mod from scratch.
|
||||
internal static void CreateMeta( DirectoryInfo directory, string? name, string? author, string? description, string? version,
|
||||
string? website )
|
||||
{
|
||||
var mod = new Mod( directory );
|
||||
mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString( name! );
|
||||
mod.Author = author != null ? new LowerString( author ) : mod.Author;
|
||||
mod.Description = description ?? mod.Description;
|
||||
mod.Version = version ?? mod.Version;
|
||||
mod.Website = website ?? mod.Website;
|
||||
mod.SaveMetaFile(); // Not delayed.
|
||||
}
|
||||
|
||||
// Create a file for an option group from given data.
|
||||
internal static void CreateOptionGroup( DirectoryInfo baseFolder, GroupType type, string name,
|
||||
int priority, int index, uint defaultSettings, string desc, IEnumerable< ISubMod > subMods )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case GroupType.Multi:
|
||||
{
|
||||
var group = new MultiModGroup()
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Priority = priority,
|
||||
DefaultSettings = defaultSettings,
|
||||
};
|
||||
group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) );
|
||||
IModGroup.Save( group, baseFolder, index );
|
||||
break;
|
||||
}
|
||||
case GroupType.Single:
|
||||
{
|
||||
var group = new SingleModGroup()
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Priority = priority,
|
||||
DefaultSettings = defaultSettings,
|
||||
};
|
||||
group.OptionData.AddRange( subMods.OfType< SubMod >() );
|
||||
IModGroup.Save( group, baseFolder, index );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the data for a given sub mod from its data and the folder it is based on.
|
||||
internal static ISubMod CreateSubMod( DirectoryInfo baseFolder, DirectoryInfo optionFolder, OptionList option )
|
||||
{
|
||||
var list = optionFolder.EnumerateFiles( "*.*", SearchOption.AllDirectories )
|
||||
.Select( f => ( Utf8GamePath.FromFile( f, optionFolder, out var gamePath, true ), gamePath, new FullPath( f ) ) )
|
||||
.Where( t => t.Item1 );
|
||||
|
||||
var mod = new SubMod( null! ) // Mod is irrelevant here, only used for saving.
|
||||
{
|
||||
Name = option.Name,
|
||||
Description = option.Description,
|
||||
};
|
||||
foreach( var (_, gamePath, file) in list )
|
||||
{
|
||||
mod.FileData.TryAdd( gamePath, file );
|
||||
}
|
||||
|
||||
mod.IncorporateMetaChanges( baseFolder, true );
|
||||
return mod;
|
||||
}
|
||||
|
||||
// Create an empty sub mod for single groups with None options.
|
||||
internal static ISubMod CreateEmptySubMod( string name )
|
||||
=> new SubMod( null! ) // Mod is irrelevant here, only used for saving.
|
||||
{
|
||||
Name = name,
|
||||
};
|
||||
|
||||
// Create the default data file from all unused files that were not handled before
|
||||
// and are used in sub mods.
|
||||
internal static void CreateDefaultFiles( DirectoryInfo directory )
|
||||
{
|
||||
var mod = new Mod( directory );
|
||||
mod.Reload( false, out _ );
|
||||
foreach( var file in mod.FindUnusedFiles() )
|
||||
{
|
||||
if( Utf8GamePath.FromFile( new FileInfo( file.FullName ), directory, out var gamePath, true ) )
|
||||
{
|
||||
mod._default.FileData.TryAdd( gamePath, file );
|
||||
}
|
||||
}
|
||||
|
||||
mod._default.IncorporateMetaChanges( directory, true );
|
||||
mod.SaveDefaultMod();
|
||||
}
|
||||
|
||||
// Return the name of a new valid directory based on the base directory and the given name.
|
||||
private static DirectoryInfo NewOptionDirectory( DirectoryInfo baseDir, string optionName )
|
||||
=> new(Path.Combine( baseDir.FullName, ReplaceBadXivSymbols( optionName ) ));
|
||||
|
||||
// Normalize for nicer names, and remove invalid symbols or invalid paths.
|
||||
public static string ReplaceBadXivSymbols( string s, string replacement = "_" )
|
||||
{
|
||||
if( s == "." )
|
||||
{
|
||||
return replacement;
|
||||
}
|
||||
|
||||
if( s == ".." )
|
||||
{
|
||||
return replacement + replacement;
|
||||
}
|
||||
|
||||
StringBuilder sb = new(s.Length);
|
||||
foreach( var c in s.Normalize( NormalizationForm.FormKC ) )
|
||||
{
|
||||
if( c.IsInvalidInPath() )
|
||||
{
|
||||
sb.Append( replacement );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append( c );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
188
Penumbra/Mods/Mod.Creator.cs
Normal file
188
Penumbra/Mods/Mod.Creator.cs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Dalamud.Utility;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Import;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class Mod
|
||||
{
|
||||
internal static class Creator
|
||||
{
|
||||
/// <summary>
|
||||
/// Create and return a new directory based on the given directory and name, that is <br/>
|
||||
/// - Not Empty.<br/>
|
||||
/// - Unique, by appending (digit) for duplicates.<br/>
|
||||
/// - Containing no symbols invalid for FFXIV or windows paths.<br/>
|
||||
/// </summary>
|
||||
/// <param name="outDirectory"></param>
|
||||
/// <param name="modListName"></param>
|
||||
/// <param name="create"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="IOException"></exception>
|
||||
public static DirectoryInfo CreateModFolder( DirectoryInfo outDirectory, string modListName, bool create = true )
|
||||
{
|
||||
var name = modListName;
|
||||
if( name.Length == 0 )
|
||||
{
|
||||
name = "_";
|
||||
}
|
||||
|
||||
var newModFolderBase = NewOptionDirectory( outDirectory, name );
|
||||
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
|
||||
if( newModFolder.Length == 0 )
|
||||
{
|
||||
throw new IOException( "Could not create mod folder: too many folders of the same name exist." );
|
||||
}
|
||||
|
||||
if( create )
|
||||
{
|
||||
Directory.CreateDirectory( newModFolder );
|
||||
}
|
||||
|
||||
return new DirectoryInfo( newModFolder );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the name for a group or option subfolder based on its parent folder and given name.
|
||||
/// subFolderName should never be empty, and the result is unique and contains no invalid symbols.
|
||||
/// </summary>
|
||||
public static DirectoryInfo? NewSubFolderName( DirectoryInfo parentFolder, string subFolderName )
|
||||
{
|
||||
var newModFolderBase = NewOptionDirectory( parentFolder, subFolderName );
|
||||
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
|
||||
return newModFolder.Length == 0 ? null : new DirectoryInfo( newModFolder );
|
||||
}
|
||||
|
||||
/// <summary> Create the file containing the meta information about a mod from scratch. </summary>
|
||||
public static void CreateMeta( DirectoryInfo directory, string? name, string? author, string? description, string? version,
|
||||
string? website )
|
||||
{
|
||||
var mod = new Mod( directory );
|
||||
mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString( name! );
|
||||
mod.Author = author != null ? new LowerString( author ) : mod.Author;
|
||||
mod.Description = description ?? mod.Description;
|
||||
mod.Version = version ?? mod.Version;
|
||||
mod.Website = website ?? mod.Website;
|
||||
mod.SaveMetaFile(); // Not delayed.
|
||||
}
|
||||
|
||||
/// <summary> Create a file for an option group from given data. </summary>
|
||||
public static void CreateOptionGroup( DirectoryInfo baseFolder, GroupType type, string name,
|
||||
int priority, int index, uint defaultSettings, string desc, IEnumerable< ISubMod > subMods )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case GroupType.Multi:
|
||||
{
|
||||
var group = new MultiModGroup()
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Priority = priority,
|
||||
DefaultSettings = defaultSettings,
|
||||
};
|
||||
group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) );
|
||||
IModGroup.Save( group, baseFolder, index );
|
||||
break;
|
||||
}
|
||||
case GroupType.Single:
|
||||
{
|
||||
var group = new SingleModGroup()
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Priority = priority,
|
||||
DefaultSettings = defaultSettings,
|
||||
};
|
||||
group.OptionData.AddRange( subMods.OfType< SubMod >() );
|
||||
IModGroup.Save( group, baseFolder, index );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Create the data for a given sub mod from its data and the folder it is based on. </summary>
|
||||
public static ISubMod CreateSubMod( DirectoryInfo baseFolder, DirectoryInfo optionFolder, OptionList option )
|
||||
{
|
||||
var list = optionFolder.EnumerateFiles( "*.*", SearchOption.AllDirectories )
|
||||
.Select( f => ( Utf8GamePath.FromFile( f, optionFolder, out var gamePath, true ), gamePath, new FullPath( f ) ) )
|
||||
.Where( t => t.Item1 );
|
||||
|
||||
var mod = new SubMod( null! ) // Mod is irrelevant here, only used for saving.
|
||||
{
|
||||
Name = option.Name,
|
||||
Description = option.Description,
|
||||
};
|
||||
foreach( var (_, gamePath, file) in list )
|
||||
{
|
||||
mod.FileData.TryAdd( gamePath, file );
|
||||
}
|
||||
|
||||
mod.IncorporateMetaChanges( baseFolder, true );
|
||||
return mod;
|
||||
}
|
||||
|
||||
/// <summary> Create an empty sub mod for single groups with None options. </summary>
|
||||
internal static ISubMod CreateEmptySubMod( string name )
|
||||
=> new SubMod( null! ) // Mod is irrelevant here, only used for saving.
|
||||
{
|
||||
Name = name,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create the default data file from all unused files that were not handled before
|
||||
/// and are used in sub mods.
|
||||
/// </summary>
|
||||
internal static void CreateDefaultFiles( DirectoryInfo directory )
|
||||
{
|
||||
var mod = new Mod( directory );
|
||||
mod.Reload( false, out _ );
|
||||
foreach( var file in mod.FindUnusedFiles() )
|
||||
{
|
||||
if( Utf8GamePath.FromFile( new FileInfo( file.FullName ), directory, out var gamePath, true ) )
|
||||
{
|
||||
mod._default.FileData.TryAdd( gamePath, file );
|
||||
}
|
||||
}
|
||||
|
||||
mod._default.IncorporateMetaChanges( directory, true );
|
||||
mod.SaveDefaultMod();
|
||||
}
|
||||
|
||||
/// <summary> Return the name of a new valid directory based on the base directory and the given name. </summary>
|
||||
public static DirectoryInfo NewOptionDirectory( DirectoryInfo baseDir, string optionName )
|
||||
=> new(Path.Combine( baseDir.FullName, ReplaceBadXivSymbols( optionName ) ));
|
||||
|
||||
/// <summary> Normalize for nicer names, and remove invalid symbols or invalid paths. </summary>
|
||||
public static string ReplaceBadXivSymbols( string s, string replacement = "_" )
|
||||
{
|
||||
switch( s )
|
||||
{
|
||||
case ".": return replacement;
|
||||
case "..": return replacement + replacement;
|
||||
}
|
||||
|
||||
StringBuilder sb = new(s.Length);
|
||||
foreach( var c in s.Normalize( NormalizationForm.FormKC ) )
|
||||
{
|
||||
if( c.IsInvalidInPath() )
|
||||
{
|
||||
sb.Append( replacement );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append( c );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,9 +51,9 @@ public sealed partial class Mod
|
|||
DirectoryInfo? dir = null;
|
||||
try
|
||||
{
|
||||
dir = CreateModFolder( Penumbra.ModManager.BasePath, collection.Name );
|
||||
dir = Creator.CreateModFolder( Penumbra.ModManager.BasePath, collection.Name );
|
||||
var fileDir = Directory.CreateDirectory( Path.Combine( dir.FullName, "files" ) );
|
||||
CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor,
|
||||
Creator.CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor,
|
||||
$"Mod generated from temporary collection {collection.Name} for {character ?? "Unknown Character"}.", null, null );
|
||||
var mod = new Mod( dir );
|
||||
var defaultMod = mod._default;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
@ -63,8 +65,7 @@ public partial class Mod
|
|||
{
|
||||
if( ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions )
|
||||
{
|
||||
Penumbra.Log.Warning(
|
||||
$"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options." );
|
||||
ChatUtil.NotificationMessage( $"Multi Group {ret.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", "Warning", NotificationType.Warning );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public partial class Mod
|
|||
_default.WriteTexToolsMeta( ModPath );
|
||||
foreach( var group in Groups )
|
||||
{
|
||||
var dir = NewOptionDirectory( ModPath, group.Name );
|
||||
var dir = Creator.NewOptionDirectory( ModPath, group.Name );
|
||||
if( !dir.Exists )
|
||||
{
|
||||
dir.Create();
|
||||
|
|
@ -75,7 +75,7 @@ public partial class Mod
|
|||
|
||||
foreach( var option in group.OfType< SubMod >() )
|
||||
{
|
||||
var optionDir = NewOptionDirectory( dir, option.Name );
|
||||
var optionDir = Creator.NewOptionDirectory( dir, option.Name );
|
||||
if( !optionDir.Exists )
|
||||
{
|
||||
optionDir.Create();
|
||||
|
|
|
|||
|
|
@ -216,9 +216,9 @@ public class ItemSwapWindow : IDisposable
|
|||
|
||||
private void CreateMod()
|
||||
{
|
||||
var newDir = Mod.CreateModFolder( Penumbra.ModManager.BasePath, _newModName );
|
||||
Mod.CreateMeta( newDir, _newModName, Penumbra.Config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty );
|
||||
Mod.CreateDefaultFiles( newDir );
|
||||
var newDir = Mod.Creator.CreateModFolder( Penumbra.ModManager.BasePath, _newModName );
|
||||
Mod.Creator.CreateMeta( newDir, _newModName, Penumbra.Config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty );
|
||||
Mod.Creator.CreateDefaultFiles( newDir );
|
||||
Penumbra.ModManager.AddMod( newDir );
|
||||
if( !_swapData.WriteMod( Penumbra.ModManager.Last(), _useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps ) )
|
||||
{
|
||||
|
|
@ -239,7 +239,7 @@ public class ItemSwapWindow : IDisposable
|
|||
DirectoryInfo? optionFolderName = null;
|
||||
try
|
||||
{
|
||||
optionFolderName = Mod.NewSubFolderName( new DirectoryInfo( Path.Combine( _mod.ModPath.FullName, _selectedGroup?.Name ?? _newGroupName ) ), _newOptionName );
|
||||
optionFolderName = Mod.Creator.NewSubFolderName( new DirectoryInfo( Path.Combine( _mod.ModPath.FullName, _selectedGroup?.Name ?? _newGroupName ) ), _newOptionName );
|
||||
if( optionFolderName?.Exists == true )
|
||||
{
|
||||
throw new Exception( $"The folder {optionFolderName.FullName} for the option already exists." );
|
||||
|
|
|
|||
|
|
@ -92,9 +92,9 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
{
|
||||
try
|
||||
{
|
||||
var newDir = Mod.CreateModFolder( Penumbra.ModManager.BasePath, _newModName );
|
||||
Mod.CreateMeta( newDir, _newModName, Penumbra.Config.DefaultModAuthor, string.Empty, "1.0", string.Empty );
|
||||
Mod.CreateDefaultFiles( newDir );
|
||||
var newDir = Mod.Creator.CreateModFolder( Penumbra.ModManager.BasePath, _newModName );
|
||||
Mod.Creator.CreateMeta( newDir, _newModName, Penumbra.Config.DefaultModAuthor, string.Empty, "1.0", string.Empty );
|
||||
Mod.Creator.CreateDefaultFiles( newDir );
|
||||
Penumbra.ModManager.AddMod( newDir );
|
||||
_newModName = string.Empty;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue