Add option to clear non-ascii symbols from paths again.

This commit is contained in:
Ottermandias 2023-12-10 15:41:26 +01:00
parent b14cd26e4e
commit 59ea1f2dd6
13 changed files with 56 additions and 62 deletions

View file

@ -877,7 +877,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public PenumbraApiEc CreateNamedTemporaryCollection(string name)
{
CheckInitialized();
if (name.Length == 0 || ModCreator.ReplaceBadXivSymbols(name) != name || name.Contains('|'))
if (name.Length == 0 || ModCreator.ReplaceBadXivSymbols(name, _config.ReplaceNonAsciiOnImport) != name || name.Contains('|'))
return PenumbraApiEc.InvalidArgument;
return _tempCollections.CreateTemporaryCollection(name).Length > 0

View file

@ -46,6 +46,7 @@ public class Configuration : IPluginConfiguration, ISavable
public bool UseOwnerNameForCharacterCollection { get; set; } = true;
public bool UseNoModsInInspect { get; set; } = false;
public bool HideChangedItemFilters { get; set; } = false;
public bool ReplaceNonAsciiOnImport { get; set; } = false;
public bool HidePrioritiesInSelector { get; set; } = false;
public bool HideRedrawBar { get; set; } = false;

View file

@ -44,7 +44,7 @@ public partial class TexToolsImporter
};
Penumbra.Log.Information($" -> Importing {archive.Type} Archive.");
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, Path.GetRandomFileName());
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, Path.GetRandomFileName(), _config.ReplaceNonAsciiOnImport, true);
var options = new ExtractionOptions()
{
ExtractFullPath = true,
@ -97,13 +97,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 = ModCreator.CreateModFolder(_baseDirectory, baseName, false);
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, baseName, _config.ReplaceNonAsciiOnImport, false);
Directory.Move(Path.Combine(oldName, baseName), _currentModDirectory.FullName);
Directory.Delete(oldName);
}
else
{
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, name, false);
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, name, _config.ReplaceNonAsciiOnImport, false);
Directory.Move(oldName, _currentModDirectory.FullName);
}

View file

@ -35,7 +35,7 @@ public partial class TexToolsImporter
var modList = modListRaw.Select(m => JsonConvert.DeserializeObject<SimpleMod>(m, JsonSettings)!).ToList();
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, Path.GetFileNameWithoutExtension(modPackFile.Name));
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, Path.GetFileNameWithoutExtension(modPackFile.Name), _config.ReplaceNonAsciiOnImport, true);
// Create a new ModMeta from the TTMP mod list info
_modManager.DataEditor.CreateMeta(_currentModDirectory, _currentModName, DefaultTexToolsData.Author, DefaultTexToolsData.Description,
null, null);
@ -88,7 +88,7 @@ public partial class TexToolsImporter
_currentOptionName = DefaultTexToolsData.DefaultOption;
Penumbra.Log.Information(" -> Importing Simple V2 ModPack");
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, _currentModName);
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, _currentModName, _config.ReplaceNonAsciiOnImport, true);
_modManager.DataEditor.CreateMeta(_currentModDirectory, _currentModName, modList.Author, string.IsNullOrEmpty(modList.Description)
? "Mod imported from TexTools mod pack"
: modList.Description, modList.Version, modList.Url);
@ -131,7 +131,7 @@ public partial class TexToolsImporter
_currentNumOptions = GetOptionCount(modList);
_currentModName = modList.Name;
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, _currentModName);
_currentModDirectory = ModCreator.CreateModFolder(_baseDirectory, _currentModName, _config.ReplaceNonAsciiOnImport, true);
_modManager.DataEditor.CreateMeta(_currentModDirectory, _currentModName, modList.Author, modList.Description, modList.Version,
modList.Url);
@ -168,7 +168,7 @@ public partial class TexToolsImporter
{
var name = numGroups == 1 ? _currentGroupName : $"{_currentGroupName}, Part {groupId + 1}";
options.Clear();
var groupFolder = ModCreator.NewSubFolderName(_currentModDirectory, name)
var groupFolder = ModCreator.NewSubFolderName(_currentModDirectory, name, _config.ReplaceNonAsciiOnImport)
?? new DirectoryInfo(Path.Combine(_currentModDirectory.FullName,
numGroups == 1 ? $"Group {groupPriority + 1}" : $"Group {groupPriority + 1}, Part {groupId + 1}"));
@ -178,7 +178,7 @@ public partial class TexToolsImporter
var option = allOptions[i + optionIdx];
_token.ThrowIfCancellationRequested();
_currentOptionName = option.Name;
var optionFolder = ModCreator.NewSubFolderName(groupFolder, option.Name)
var optionFolder = ModCreator.NewSubFolderName(groupFolder, option.Name, _config.ReplaceNonAsciiOnImport)
?? new DirectoryInfo(Path.Combine(groupFolder.FullName, $"Option {i + optionIdx + 1}"));
ExtractSimpleModList(optionFolder, option.ModsJsons);
options.Add(_modManager.Creator.CreateSubMod(_currentModDirectory, optionFolder, option));

View file

@ -49,13 +49,13 @@ public unsafe class MetaFileManager
TexToolsMeta.WriteTexToolsMeta(this, mod.Default.Manipulations, mod.ModPath);
foreach (var group in mod.Groups)
{
var dir = ModCreator.NewOptionDirectory(mod.ModPath, group.Name);
var dir = ModCreator.NewOptionDirectory(mod.ModPath, group.Name, Config.ReplaceNonAsciiOnImport);
if (!dir.Exists)
dir.Create();
foreach (var option in group.OfType<SubMod>())
{
var optionDir = ModCreator.NewOptionDirectory(dir, option.Name);
var optionDir = ModCreator.NewOptionDirectory(dir, option.Name, Config.ReplaceNonAsciiOnImport);
if (!optionDir.Exists)
optionDir.Create();

View file

@ -4,7 +4,6 @@ using OtterGui;
using OtterGui.Classes;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
using Penumbra.Services;
@ -15,6 +14,7 @@ namespace Penumbra.Mods.Editor;
public class ModMerger : IDisposable
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
private readonly ModOptionEditor _editor;
private readonly ModFileSystemSelector _selector;
@ -40,13 +40,14 @@ public class ModMerger : IDisposable
public Exception? Error { get; private set; }
public ModMerger(ModManager mods, ModOptionEditor editor, ModFileSystemSelector selector, DuplicateManager duplicates,
CommunicatorService communicator, ModCreator creator)
CommunicatorService communicator, ModCreator creator, Configuration config)
{
_editor = editor;
_selector = selector;
_duplicates = duplicates;
_communicator = communicator;
_creator = creator;
_config = config;
_mods = mods;
_selector.SelectionChanged += OnSelectionChange;
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModMerger);
@ -82,7 +83,8 @@ public class ModMerger : IDisposable
catch (Exception ex)
{
Error = ex;
Penumbra.Messager.NotificationMessage(ex, $"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}, cleaning up changes.", NotificationType.Error, false);
Penumbra.Messager.NotificationMessage(ex, $"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}, cleaning up changes.",
NotificationType.Error, false);
FailureCleanup();
DataCleanup();
}
@ -138,10 +140,10 @@ public class ModMerger : IDisposable
var (option, optionCreated) = _editor.FindOrAddOption(MergeToMod!, groupIdx, optionName);
if (optionCreated)
_createdOptions.Add(option);
var dir = ModCreator.NewOptionDirectory(MergeToMod!.ModPath, groupName);
var dir = ModCreator.NewOptionDirectory(MergeToMod!.ModPath, groupName, _config.ReplaceNonAsciiOnImport);
if (!dir.Exists)
_createdDirectories.Add(dir.FullName);
dir = ModCreator.NewOptionDirectory(dir, optionName);
dir = ModCreator.NewOptionDirectory(dir, optionName, _config.ReplaceNonAsciiOnImport);
if (!dir.Exists)
_createdDirectories.Add(dir.FullName);
CopyFiles(dir);

View file

@ -6,11 +6,10 @@ using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
using Penumbra.String.Classes;
namespace Penumbra.Mods;
namespace Penumbra.Mods.Editor;
public class ModNormalizer
public class ModNormalizer(ModManager _modManager, Configuration _config)
{
private readonly ModManager _modManager;
private readonly List<List<Dictionary<Utf8GamePath, FullPath>>> _redirections = new();
public Mod Mod { get; private set; } = null!;
@ -25,9 +24,6 @@ public class ModNormalizer
public bool Running
=> !Worker.IsCompleted;
public ModNormalizer(ModManager modManager)
=> _modManager = modManager;
public void Normalize(Mod mod)
{
if (Step < TotalSteps)
@ -175,10 +171,10 @@ public class ModNormalizer
for (var i = _redirections[groupIdx + 1].Count; i < group.Count; ++i)
_redirections[groupIdx + 1].Add(new Dictionary<Utf8GamePath, FullPath>());
var groupDir = ModCreator.CreateModFolder(directory, group.Name);
var groupDir = ModCreator.CreateModFolder(directory, group.Name, _config.ReplaceNonAsciiOnImport, true);
foreach (var option in group.OfType<SubMod>())
{
var optionDir = ModCreator.CreateModFolder(groupDir, option.Name);
var optionDir = ModCreator.CreateModFolder(groupDir, option.Name, _config.ReplaceNonAsciiOnImport, true);
newDict = _redirections[groupIdx + 1][option.OptionIdx];
newDict.Clear();
@ -228,7 +224,8 @@ public class ModNormalizer
}
catch (Exception e)
{
Penumbra.Messager.NotificationMessage(e, $"Could not move old files out of the way while normalizing mod {Mod.Name}.", NotificationType.Error, false);
Penumbra.Messager.NotificationMessage(e, $"Could not move old files out of the way while normalizing mod {Mod.Name}.",
NotificationType.Error, false);
}
return false;
@ -251,7 +248,8 @@ public class ModNormalizer
}
catch (Exception e)
{
Penumbra.Messager.NotificationMessage(e, $"Could not move new files into the mod while normalizing mod {Mod.Name}.", NotificationType.Error, false);
Penumbra.Messager.NotificationMessage(e, $"Could not move new files into the mod while normalizing mod {Mod.Name}.",
NotificationType.Error, false);
foreach (var dir in Mod.ModPath.EnumerateDirectories())
{
if (dir.FullName.Equals(_oldDirName, StringComparison.OrdinalIgnoreCase)

View file

@ -217,7 +217,7 @@ public sealed class ModManager : ModStorage, IDisposable
if (oldName == newName)
return NewDirectoryState.Identical;
var fixedNewName = ModCreator.ReplaceBadXivSymbols(newName);
var fixedNewName = ModCreator.ReplaceBadXivSymbols(newName, _config.ReplaceNonAsciiOnImport);
if (fixedNewName != newName)
return NewDirectoryState.ContainsInvalidSymbols;

View file

@ -16,30 +16,15 @@ using Penumbra.String.Classes;
namespace Penumbra.Mods;
public partial class ModCreator
public partial class ModCreator(SaveService _saveService, Configuration _config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager,
IGamePathParser _gamePathParser)
{
private readonly Configuration _config;
private readonly SaveService _saveService;
private readonly ModDataEditor _dataEditor;
private readonly MetaFileManager _metaFileManager;
private readonly IGamePathParser _gamePathParser;
public ModCreator(SaveService saveService, Configuration config, ModDataEditor dataEditor, MetaFileManager metaFileManager,
IGamePathParser gamePathParser)
{
_saveService = saveService;
_config = config;
_dataEditor = dataEditor;
_metaFileManager = metaFileManager;
_gamePathParser = gamePathParser;
}
/// <summary> Creates directory and files necessary for a new mod without adding it to the manager. </summary>
public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "")
{
try
{
var newDir = CreateModFolder(basePath, newName);
var newDir = CreateModFolder(basePath, newName, _config.ReplaceNonAsciiOnImport, true);
_dataEditor.CreateMeta(newDir, newName, _config.DefaultModAuthor, description, "1.0", string.Empty);
CreateDefaultFiles(newDir);
return newDir;
@ -138,13 +123,13 @@ public partial class ModCreator
/// - Unique, by appending (digit) for duplicates.<br/>
/// - Containing no symbols invalid for FFXIV or windows paths.<br/>
/// </summary>
public static DirectoryInfo CreateModFolder(DirectoryInfo outDirectory, string modListName, bool create = true)
public static DirectoryInfo CreateModFolder(DirectoryInfo outDirectory, string modListName, bool onlyAscii, bool create)
{
var name = modListName;
if (name.Length == 0)
name = "_";
var newModFolderBase = NewOptionDirectory(outDirectory, name);
var newModFolderBase = NewOptionDirectory(outDirectory, name, onlyAscii);
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.");
@ -238,9 +223,9 @@ public partial class ModCreator
/// 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)
public static DirectoryInfo? NewSubFolderName(DirectoryInfo parentFolder, string subFolderName, bool onlyAscii)
{
var newModFolderBase = NewOptionDirectory(parentFolder, subFolderName);
var newModFolderBase = NewOptionDirectory(parentFolder, subFolderName, onlyAscii);
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
return newModFolder.Length == 0 ? null : new DirectoryInfo(newModFolder);
}
@ -325,14 +310,14 @@ public partial class ModCreator
}
/// <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)
public static DirectoryInfo NewOptionDirectory(DirectoryInfo baseDir, string optionName, bool onlyAscii)
{
var option = ReplaceBadXivSymbols(optionName);
var option = ReplaceBadXivSymbols(optionName, onlyAscii);
return new DirectoryInfo(Path.Combine(baseDir.FullName, option.Length > 0 ? option : "_"));
}
/// <summary> Normalize for nicer names, and remove invalid symbols or invalid paths. </summary>
public static string ReplaceBadXivSymbols(string s, string replacement = "_")
public static string ReplaceBadXivSymbols(string s, bool onlyAscii, string replacement = "_")
{
switch (s)
{
@ -345,6 +330,8 @@ public partial class ModCreator
{
if (c.IsInvalidInPath())
sb.Append(replacement);
else if (onlyAscii && c.IsInvalidAscii())
sb.Append(replacement);
else
sb.Append(c);
}

View file

@ -52,7 +52,7 @@ public class TemporaryMod : IMod
DirectoryInfo? dir = null;
try
{
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name);
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true);
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Name} for {character ?? "Unknown Character"}.", null, null);

View file

@ -25,6 +25,7 @@ namespace Penumbra.UI.AdvancedWindow;
public class ItemSwapTab : IDisposable, ITab
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
private readonly ItemService _itemService;
private readonly CollectionManager _collectionManager;
@ -32,13 +33,14 @@ public class ItemSwapTab : IDisposable, ITab
private readonly MetaFileManager _metaFileManager;
public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager,
ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager)
ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager, Configuration config)
{
_communicator = communicator;
_itemService = itemService;
_collectionManager = collectionManager;
_modManager = modManager;
_metaFileManager = metaFileManager;
_config = config;
_swapData = new ItemSwapContainer(metaFileManager, identifier);
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
@ -296,7 +298,7 @@ public class ItemSwapTab : IDisposable, ITab
{
optionFolderName =
ModCreator.NewSubFolderName(new DirectoryInfo(Path.Combine(_mod.ModPath.FullName, _selectedGroup?.Name ?? _newGroupName)),
_newOptionName);
_newOptionName, _config.ReplaceNonAsciiOnImport);
if (optionFolderName?.Exists == true)
throw new Exception($"The folder {optionFolderName.FullName} for the option already exists.");

View file

@ -22,12 +22,13 @@ public partial class ModEditWindow
private HashSet<string> GetPlayerResourcesOfType(ResourceType type)
{
var resources = ResourceTreeApiHelper.GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type)
var resources = ResourceTreeApiHelper
.GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type)
.Values
.SelectMany(resources => resources.Values)
.Select(resource => resource.Item1);
return new(resources, StringComparer.OrdinalIgnoreCase);
return new HashSet<string>(resources, StringComparer.OrdinalIgnoreCase);
}
private IReadOnlyList<FileRegistry> PopulateIsOnPlayer(IReadOnlyList<FileRegistry> files, ResourceType type)
@ -198,7 +199,7 @@ public partial class ModEditWindow
if (mod == null)
return new QuickImportAction(editor, optionName, gamePath);
var (preferredPath, subDirs) = GetPreferredPath(mod, subMod);
var (preferredPath, subDirs) = GetPreferredPath(mod, subMod, owner._config.ReplaceNonAsciiOnImport);
var targetPath = new FullPath(Path.Combine(preferredPath.FullName, gamePath.ToString())).FullName;
if (File.Exists(targetPath))
return new QuickImportAction(editor, optionName, gamePath);
@ -226,7 +227,7 @@ public partial class ModEditWindow
return fileRegistry;
}
private static (DirectoryInfo, int) GetPreferredPath(Mod mod, ISubMod subMod)
private static (DirectoryInfo, int) GetPreferredPath(Mod mod, ISubMod subMod, bool replaceNonAscii)
{
var path = mod.ModPath;
var subDirs = 0;
@ -237,13 +238,13 @@ public partial class ModEditWindow
var fullName = subMod.FullName;
if (fullName.EndsWith(": " + name))
{
path = ModCreator.NewOptionDirectory(path, fullName[..^(name.Length + 2)]);
path = ModCreator.NewOptionDirectory(path, name);
path = ModCreator.NewOptionDirectory(path, fullName[..^(name.Length + 2)], replaceNonAscii);
path = ModCreator.NewOptionDirectory(path, name, replaceNonAscii);
subDirs = 2;
}
else
{
path = ModCreator.NewOptionDirectory(path, fullName);
path = ModCreator.NewOptionDirectory(path, fullName, replaceNonAscii);
subDirs = 1;
}

View file

@ -535,6 +535,9 @@ public class SettingsTab : ITab
/// <summary> Draw all settings pertaining to import and export of mods. </summary>
private void DrawModHandlingSettings()
{
Checkbox("Replace Non-Standard Symbols On Import",
"Replace all non-ASCII symbols in mod and option names with underscores when importing mods.", _config.ReplaceNonAsciiOnImport,
v => _config.ReplaceNonAsciiOnImport = v);
Checkbox("Always Open Import at Default Directory",
"Open the import window at the location specified here every time, forgetting your previous path.",
_config.AlwaysOpenDefaultImport, v => _config.AlwaysOpenDefaultImport = v);