Move mod creation functions to own subclass.

This commit is contained in:
Ottermandias 2023-03-03 13:43:00 +01:00
parent c2ac745d72
commit 1f942491ac
13 changed files with 237 additions and 233 deletions

View file

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

View file

@ -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 )
{

View file

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

View file

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

View file

@ -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 )
{

View file

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

View file

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

View 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();
}
}
}

View file

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

View file

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

View file

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

View file

@ -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." );

View file

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