mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-31 21:03:48 +01:00
Make line endings explicit in editorconfig and share in sub projects, also apply editorconfig everywhere and move some namespaces.
This commit is contained in:
parent
53adb6fa54
commit
2b4a01df06
155 changed files with 1620 additions and 1614 deletions
|
|
@ -59,7 +59,7 @@ public class DuplicateManager
|
|||
public void Clear()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
Worker = Task.CompletedTask;
|
||||
Worker = Task.CompletedTask;
|
||||
_duplicates.Clear();
|
||||
SavedSpace = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ public interface IMod
|
|||
{
|
||||
LowerString Name { get; }
|
||||
|
||||
public int Index { get; }
|
||||
public int Index { get; }
|
||||
public int Priority { get; }
|
||||
|
||||
public ISubMod Default { get; }
|
||||
public IReadOnlyList< IModGroup > Groups { get; }
|
||||
public ISubMod Default { get; }
|
||||
public IReadOnlyList<IModGroup> Groups { get; }
|
||||
|
||||
public IEnumerable< SubMod > AllSubMods { get; }
|
||||
|
||||
// Cache
|
||||
public IEnumerable<SubMod> AllSubMods { get; }
|
||||
|
||||
// Cache
|
||||
public int TotalManipulations { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
using System.IO.Compression;
|
||||
using OtterGui.Tasks;
|
||||
using OtterGui.Tasks;
|
||||
using Penumbra.Mods.Manager;
|
||||
|
||||
namespace Penumbra.Mods.Editor;
|
||||
|
||||
/// <summary> Utility to create and apply a zipped backup of a mod. </summary>
|
||||
public class ModBackup
|
||||
{
|
||||
{
|
||||
/// <summary> Set when reading Config and migrating from v4 to v5. </summary>
|
||||
public static bool MigrateModBackups = false;
|
||||
|
||||
public static bool CreatingBackup { get; private set; }
|
||||
|
||||
private readonly Mod _mod;
|
||||
public readonly string Name;
|
||||
public readonly bool Exists;
|
||||
private readonly Mod _mod;
|
||||
public readonly string Name;
|
||||
public readonly bool Exists;
|
||||
|
||||
public ModBackup(ModExportManager modExportManager, Mod mod)
|
||||
{
|
||||
_mod = mod;
|
||||
Name = Path.Combine(modExportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp";
|
||||
Exists = File.Exists(Name);
|
||||
{
|
||||
_mod = mod;
|
||||
Name = Path.Combine(modExportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp";
|
||||
Exists = File.Exists(Name);
|
||||
}
|
||||
|
||||
/// <summary> Migrate file extensions. </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using OtterGui;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods.Editor;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
|
@ -7,7 +8,7 @@ namespace Penumbra.Mods;
|
|||
public class ModFileEditor
|
||||
{
|
||||
private readonly ModFileCollection _files;
|
||||
private readonly ModManager _modManager;
|
||||
private readonly ModManager _modManager;
|
||||
|
||||
public bool Changes { get; private set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Penumbra.Api.Enums;
|
|||
using Penumbra.Communication;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.UI.ModsTab;
|
||||
|
|
@ -174,7 +175,7 @@ public class ModMerger : IDisposable
|
|||
ret = new FullPath(MergeToMod!.ModPath, relPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
foreach (var originalOption in mergeOptions)
|
||||
{
|
||||
foreach (var manip in originalOption.Manipulations)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
@ -146,6 +147,7 @@ public class ModMetaEditor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Split(currentOption.Manipulations);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Dalamud.Interface.Internal.Notifications;
|
|||
using OtterGui;
|
||||
using OtterGui.Tasks;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
public class ModSwapEditor
|
||||
{
|
||||
private readonly ModManager _modManager;
|
||||
private readonly ModManager _modManager;
|
||||
private readonly Dictionary<Utf8GamePath, FullPath> _swaps = new();
|
||||
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> Swaps
|
||||
|
|
|
|||
|
|
@ -10,27 +10,29 @@ namespace Penumbra.Mods.ItemSwap;
|
|||
public static class CustomizationSwap
|
||||
{
|
||||
/// The .mdl file for customizations is unique per racecode, slot and id, thus the .mdl redirection itself is independent of the mode.
|
||||
public static FileSwap CreateMdl( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo )
|
||||
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
|
||||
SetId idFrom, SetId idTo)
|
||||
{
|
||||
if( idFrom.Id > byte.MaxValue )
|
||||
{
|
||||
throw new Exception( $"The Customization ID {idFrom} is too large for {slot}." );
|
||||
}
|
||||
if (idFrom.Id > byte.MaxValue)
|
||||
throw new Exception($"The Customization ID {idFrom} is too large for {slot}.");
|
||||
|
||||
var mdlPathFrom = GamePaths.Character.Mdl.Path( race, slot, idFrom, slot.ToCustomizationType() );
|
||||
var mdlPathTo = GamePaths.Character.Mdl.Path( race, slot, idTo, slot.ToCustomizationType() );
|
||||
var mdlPathFrom = GamePaths.Character.Mdl.Path(race, slot, idFrom, slot.ToCustomizationType());
|
||||
var mdlPathTo = GamePaths.Character.Mdl.Path(race, slot, idTo, slot.ToCustomizationType());
|
||||
|
||||
var mdl = FileSwap.CreateSwap( manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo );
|
||||
var range = slot == BodySlot.Tail && race is GenderRace.HrothgarMale or GenderRace.HrothgarFemale or GenderRace.HrothgarMaleNpc or GenderRace.HrothgarMaleNpc ? 5 : 1;
|
||||
var mdl = FileSwap.CreateSwap(manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo);
|
||||
var range = slot == BodySlot.Tail
|
||||
&& race is GenderRace.HrothgarMale or GenderRace.HrothgarFemale or GenderRace.HrothgarMaleNpc or GenderRace.HrothgarMaleNpc
|
||||
? 5
|
||||
: 1;
|
||||
|
||||
foreach( ref var materialFileName in mdl.AsMdl()!.Materials.AsSpan() )
|
||||
foreach (ref var materialFileName in mdl.AsMdl()!.Materials.AsSpan())
|
||||
{
|
||||
var name = materialFileName;
|
||||
foreach( var variant in Enumerable.Range( 1, range ) )
|
||||
foreach (var variant in Enumerable.Range(1, range))
|
||||
{
|
||||
name = materialFileName;
|
||||
var mtrl = CreateMtrl( manager, redirections, slot, race, idFrom, idTo, ( byte )variant, ref name, ref mdl.DataWasChanged );
|
||||
mdl.ChildSwaps.Add( mtrl );
|
||||
var mtrl = CreateMtrl(manager, redirections, slot, race, idFrom, idTo, (byte)variant, ref name, ref mdl.DataWasChanged);
|
||||
mdl.ChildSwaps.Add(mtrl);
|
||||
}
|
||||
|
||||
materialFileName = name;
|
||||
|
|
@ -39,71 +41,75 @@ public static class CustomizationSwap
|
|||
return mdl;
|
||||
}
|
||||
|
||||
public static FileSwap CreateMtrl( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo, byte variant,
|
||||
ref string fileName, ref bool dataWasChanged )
|
||||
public static FileSwap CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
|
||||
SetId idFrom, SetId idTo, byte variant,
|
||||
ref string fileName, ref bool dataWasChanged)
|
||||
{
|
||||
variant = slot is BodySlot.Face or BodySlot.Zear ? byte.MaxValue : variant;
|
||||
var mtrlFromPath = GamePaths.Character.Mtrl.Path( race, slot, idFrom, fileName, out var gameRaceFrom, out var gameSetIdFrom, variant );
|
||||
var mtrlToPath = GamePaths.Character.Mtrl.Path( race, slot, idTo, fileName, out var gameRaceTo, out var gameSetIdTo, variant );
|
||||
var mtrlFromPath = GamePaths.Character.Mtrl.Path(race, slot, idFrom, fileName, out var gameRaceFrom, out var gameSetIdFrom, variant);
|
||||
var mtrlToPath = GamePaths.Character.Mtrl.Path(race, slot, idTo, fileName, out var gameRaceTo, out var gameSetIdTo, variant);
|
||||
|
||||
var newFileName = fileName;
|
||||
newFileName = ItemSwap.ReplaceRace( newFileName, gameRaceTo, race, gameRaceTo != race );
|
||||
newFileName = ItemSwap.ReplaceBody( newFileName, slot, idTo, idFrom, idFrom != idTo );
|
||||
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_c{race.ToRaceCode()}", gameRaceFrom != race || MaterialHandling.IsSpecialCase( race, idFrom ) );
|
||||
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_{slot.ToAbbreviation()}{idFrom.Id:D4}", gameSetIdFrom != idFrom );
|
||||
newFileName = ItemSwap.ReplaceRace(newFileName, gameRaceTo, race, gameRaceTo != race);
|
||||
newFileName = ItemSwap.ReplaceBody(newFileName, slot, idTo, idFrom, idFrom != idTo);
|
||||
newFileName = ItemSwap.AddSuffix(newFileName, ".mtrl", $"_c{race.ToRaceCode()}",
|
||||
gameRaceFrom != race || MaterialHandling.IsSpecialCase(race, idFrom));
|
||||
newFileName = ItemSwap.AddSuffix(newFileName, ".mtrl", $"_{slot.ToAbbreviation()}{idFrom.Id:D4}", gameSetIdFrom != idFrom);
|
||||
|
||||
var actualMtrlFromPath = mtrlFromPath;
|
||||
if( newFileName != fileName )
|
||||
if (newFileName != fileName)
|
||||
{
|
||||
actualMtrlFromPath = GamePaths.Character.Mtrl.Path( race, slot, idFrom, newFileName, out _, out _, variant );
|
||||
actualMtrlFromPath = GamePaths.Character.Mtrl.Path(race, slot, idFrom, newFileName, out _, out _, variant);
|
||||
fileName = newFileName;
|
||||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
var mtrl = FileSwap.CreateSwap( manager, ResourceType.Mtrl, redirections, actualMtrlFromPath, mtrlToPath, actualMtrlFromPath );
|
||||
var shpk = CreateShader( manager, redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( shpk );
|
||||
var mtrl = FileSwap.CreateSwap(manager, ResourceType.Mtrl, redirections, actualMtrlFromPath, mtrlToPath, actualMtrlFromPath);
|
||||
var shpk = CreateShader(manager, redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged);
|
||||
mtrl.ChildSwaps.Add(shpk);
|
||||
|
||||
foreach( ref var texture in mtrl.AsMtrl()!.Textures.AsSpan() )
|
||||
foreach (ref var texture in mtrl.AsMtrl()!.Textures.AsSpan())
|
||||
{
|
||||
var tex = CreateTex( manager, redirections, slot, race, idFrom, ref texture, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( tex );
|
||||
var tex = CreateTex(manager, redirections, slot, race, idFrom, ref texture, ref mtrl.DataWasChanged);
|
||||
mtrl.ChildSwaps.Add(tex);
|
||||
}
|
||||
|
||||
return mtrl;
|
||||
}
|
||||
|
||||
public static FileSwap CreateTex( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, ref MtrlFile.Texture texture,
|
||||
ref bool dataWasChanged )
|
||||
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
|
||||
SetId idFrom, ref MtrlFile.Texture texture,
|
||||
ref bool dataWasChanged)
|
||||
{
|
||||
var path = texture.Path;
|
||||
var addedDashes = false;
|
||||
if( texture.DX11 )
|
||||
if (texture.DX11)
|
||||
{
|
||||
var fileName = Path.GetFileName( path );
|
||||
if( !fileName.StartsWith( "--" ) )
|
||||
var fileName = Path.GetFileName(path);
|
||||
if (!fileName.StartsWith("--"))
|
||||
{
|
||||
path = path.Replace( fileName, $"--{fileName}" );
|
||||
path = path.Replace(fileName, $"--{fileName}");
|
||||
addedDashes = true;
|
||||
}
|
||||
}
|
||||
|
||||
var newPath = ItemSwap.ReplaceAnyRace( path, race );
|
||||
newPath = ItemSwap.ReplaceAnyBody( newPath, slot, idFrom );
|
||||
newPath = ItemSwap.AddSuffix( newPath, ".tex", $"_{Path.GetFileName( texture.Path ).GetStableHashCode():x8}", true );
|
||||
if( newPath != path )
|
||||
var newPath = ItemSwap.ReplaceAnyRace(path, race);
|
||||
newPath = ItemSwap.ReplaceAnyBody(newPath, slot, idFrom);
|
||||
newPath = ItemSwap.AddSuffix(newPath, ".tex", $"_{Path.GetFileName(texture.Path).GetStableHashCode():x8}", true);
|
||||
if (newPath != path)
|
||||
{
|
||||
texture.Path = addedDashes ? newPath.Replace( "--", string.Empty ) : newPath;
|
||||
texture.Path = addedDashes ? newPath.Replace("--", string.Empty) : newPath;
|
||||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Tex, redirections, newPath, path, path );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, newPath, path, path);
|
||||
}
|
||||
|
||||
|
||||
public static FileSwap CreateShader( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, ref string shaderName, ref bool dataWasChanged )
|
||||
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName,
|
||||
ref bool dataWasChanged)
|
||||
{
|
||||
var path = $"shader/sm5/shpk/{shaderName}";
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Shpk, redirections, path, path );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Shpk, redirections, path, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,10 +249,10 @@ public static class EquipmentSwap
|
|||
private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom,
|
||||
SetId idFrom, SetId idTo, Variant variantFrom)
|
||||
{
|
||||
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
|
||||
var imc = new ImcFile(manager, entry);
|
||||
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
|
||||
var imc = new ImcFile(manager, entry);
|
||||
EquipItem[] items;
|
||||
Variant[] variants;
|
||||
Variant[] variants;
|
||||
if (idFrom == idTo)
|
||||
{
|
||||
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray();
|
||||
|
|
@ -264,8 +264,9 @@ public static class EquipmentSwap
|
|||
else
|
||||
{
|
||||
items = identifier.Identify(slotFrom.IsEquipment()
|
||||
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
||||
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)).Select(kvp => kvp.Value).OfType<EquipItem>().ToArray();
|
||||
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
||||
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)).Select(kvp => kvp.Value).OfType<EquipItem>()
|
||||
.ToArray();
|
||||
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (Variant)i).ToArray();
|
||||
}
|
||||
|
||||
|
|
@ -283,11 +284,13 @@ public static class EquipmentSwap
|
|||
return new MetaSwap(manips, manipFrom, manipTo);
|
||||
}
|
||||
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
||||
SetId idFrom, SetId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||
=> CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo);
|
||||
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips,
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||
Func<MetaManipulation, MetaManipulation> manips,
|
||||
EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo,
|
||||
Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||
{
|
||||
|
|
@ -401,7 +404,8 @@ public static class EquipmentSwap
|
|||
ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||
=> CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged);
|
||||
|
||||
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom,
|
||||
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, EquipSlot slotFrom,
|
||||
EquipSlot slotTo, SetId idFrom,
|
||||
SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||
{
|
||||
var path = texture.Path;
|
||||
|
|
@ -428,13 +432,15 @@ public static class EquipmentSwap
|
|||
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, newPath, path, path);
|
||||
}
|
||||
|
||||
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName, ref bool dataWasChanged)
|
||||
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName,
|
||||
ref bool dataWasChanged)
|
||||
{
|
||||
var path = $"shader/sm5/shpk/{shaderName}";
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Shpk, redirections, path, path);
|
||||
}
|
||||
|
||||
public static FileSwap CreateAtex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string filePath, ref bool dataWasChanged)
|
||||
public static FileSwap CreateAtex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string filePath,
|
||||
ref bool dataWasChanged)
|
||||
{
|
||||
var oldPath = filePath;
|
||||
filePath = ItemSwap.AddSuffix(filePath, ".atex", $"_{Path.GetFileName(filePath).GetStableHashCode():x8}");
|
||||
|
|
|
|||
|
|
@ -20,47 +20,45 @@ public static class ItemSwap
|
|||
{
|
||||
public readonly ResourceType Type;
|
||||
|
||||
public MissingFileException( ResourceType type, object path )
|
||||
public MissingFileException(ResourceType type, object path)
|
||||
: base($"Could not load {type} File Data for \"{path}\".")
|
||||
=> Type = type;
|
||||
}
|
||||
|
||||
private static bool LoadFile( MetaFileManager manager, FullPath path, out byte[] data )
|
||||
private static bool LoadFile(MetaFileManager manager, FullPath path, out byte[] data)
|
||||
{
|
||||
if( path.FullName.Length > 0 )
|
||||
{
|
||||
if (path.FullName.Length > 0)
|
||||
try
|
||||
{
|
||||
if( path.IsRooted )
|
||||
if (path.IsRooted)
|
||||
{
|
||||
data = File.ReadAllBytes( path.FullName );
|
||||
data = File.ReadAllBytes(path.FullName);
|
||||
return true;
|
||||
}
|
||||
|
||||
var file = manager.GameData.GetFile( path.InternalName.ToString() );
|
||||
if( file != null )
|
||||
var file = manager.GameData.GetFile(path.InternalName.ToString());
|
||||
if (file != null)
|
||||
{
|
||||
data = file.Data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Debug( $"Could not load file {path}:\n{e}" );
|
||||
Penumbra.Log.Debug($"Could not load file {path}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
data = Array.Empty< byte >();
|
||||
data = Array.Empty<byte>();
|
||||
return false;
|
||||
}
|
||||
|
||||
public class GenericFile : IWritable
|
||||
{
|
||||
public readonly byte[] Data;
|
||||
public bool Valid { get; }
|
||||
public bool Valid { get; }
|
||||
|
||||
public GenericFile( MetaFileManager manager, FullPath path )
|
||||
=> Valid = LoadFile( manager, path, out Data );
|
||||
public GenericFile(MetaFileManager manager, FullPath path)
|
||||
=> Valid = LoadFile(manager, path, out Data);
|
||||
|
||||
public byte[] Write()
|
||||
=> Data;
|
||||
|
|
@ -68,69 +66,67 @@ public static class ItemSwap
|
|||
public static readonly GenericFile Invalid = new(null!, FullPath.Empty);
|
||||
}
|
||||
|
||||
public static bool LoadFile( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out GenericFile? file )
|
||||
public static bool LoadFile(MetaFileManager manager, FullPath path, [NotNullWhen(true)] out GenericFile? file)
|
||||
{
|
||||
file = new GenericFile( manager, path );
|
||||
if( file.Valid )
|
||||
{
|
||||
file = new GenericFile(manager, path);
|
||||
if (file.Valid)
|
||||
return true;
|
||||
}
|
||||
|
||||
file = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadMdl( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out MdlFile? file )
|
||||
public static bool LoadMdl(MetaFileManager manager, FullPath path, [NotNullWhen(true)] out MdlFile? file)
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
if (LoadFile(manager, path, out byte[] data))
|
||||
{
|
||||
file = new MdlFile( data );
|
||||
file = new MdlFile(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Debug( $"Could not parse file {path} to Mdl:\n{e}" );
|
||||
Penumbra.Log.Debug($"Could not parse file {path} to Mdl:\n{e}");
|
||||
}
|
||||
|
||||
file = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadMtrl(MetaFileManager manager, FullPath path, [NotNullWhen( true )] out MtrlFile? file )
|
||||
public static bool LoadMtrl(MetaFileManager manager, FullPath path, [NotNullWhen(true)] out MtrlFile? file)
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
if (LoadFile(manager, path, out byte[] data))
|
||||
{
|
||||
file = new MtrlFile( data );
|
||||
file = new MtrlFile(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Debug( $"Could not parse file {path} to Mtrl:\n{e}" );
|
||||
Penumbra.Log.Debug($"Could not parse file {path} to Mtrl:\n{e}");
|
||||
}
|
||||
|
||||
file = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadAvfx( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out AvfxFile? file )
|
||||
public static bool LoadAvfx(MetaFileManager manager, FullPath path, [NotNullWhen(true)] out AvfxFile? file)
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
if (LoadFile(manager, path, out byte[] data))
|
||||
{
|
||||
file = new AvfxFile( data );
|
||||
file = new AvfxFile(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Debug( $"Could not parse file {path} to Avfx:\n{e}" );
|
||||
Penumbra.Log.Debug($"Could not parse file {path} to Avfx:\n{e}");
|
||||
}
|
||||
|
||||
file = null;
|
||||
|
|
@ -138,40 +134,41 @@ public static class ItemSwap
|
|||
}
|
||||
|
||||
|
||||
public static FileSwap CreatePhyb(MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
public static FileSwap CreatePhyb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstManipulation.EstType type,
|
||||
GenderRace race, ushort estEntry)
|
||||
{
|
||||
var phybPath = GamePaths.Skeleton.Phyb.Path( race, EstManipulation.ToName( type ), estEntry );
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Phyb, redirections, phybPath, phybPath );
|
||||
var phybPath = GamePaths.Skeleton.Phyb.Path(race, EstManipulation.ToName(type), estEntry);
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Phyb, redirections, phybPath, phybPath);
|
||||
}
|
||||
|
||||
public static FileSwap CreateSklb(MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
public static FileSwap CreateSklb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstManipulation.EstType type,
|
||||
GenderRace race, ushort estEntry)
|
||||
{
|
||||
var sklbPath = GamePaths.Skeleton.Sklb.Path( race, EstManipulation.ToName( type ), estEntry );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath );
|
||||
var sklbPath = GamePaths.Skeleton.Sklb.Path(race, EstManipulation.ToName(type), estEntry);
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath);
|
||||
}
|
||||
|
||||
/// <remarks> metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. </remarks>
|
||||
public static MetaSwap? CreateEst( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EstManipulation.EstType type,
|
||||
GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl )
|
||||
public static MetaSwap? CreateEst(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||
Func<MetaManipulation, MetaManipulation> manips, EstManipulation.EstType type,
|
||||
GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl)
|
||||
{
|
||||
if( type == 0 )
|
||||
{
|
||||
if (type == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
var (gender, race) = genderRace.Split();
|
||||
var fromDefault = new EstManipulation( gender, race, type, idFrom, EstFile.GetDefault( manager, type, genderRace, idFrom ) );
|
||||
var toDefault = new EstManipulation( gender, race, type, idTo, EstFile.GetDefault( manager, type, genderRace, idTo ) );
|
||||
var est = new MetaSwap( manips, fromDefault, toDefault );
|
||||
var fromDefault = new EstManipulation(gender, race, type, idFrom, EstFile.GetDefault(manager, type, genderRace, idFrom));
|
||||
var toDefault = new EstManipulation(gender, race, type, idTo, EstFile.GetDefault(manager, type, genderRace, idTo));
|
||||
var est = new MetaSwap(manips, fromDefault, toDefault);
|
||||
|
||||
if( ownMdl && est.SwapApplied.Est.Entry >= 2 )
|
||||
if (ownMdl && est.SwapApplied.Est.Entry >= 2)
|
||||
{
|
||||
var phyb = CreatePhyb( manager, redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
est.ChildSwaps.Add( phyb );
|
||||
var sklb = CreateSklb( manager, redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
est.ChildSwaps.Add( sklb );
|
||||
var phyb = CreatePhyb(manager, redirections, type, genderRace, est.SwapApplied.Est.Entry);
|
||||
est.ChildSwaps.Add(phyb);
|
||||
var sklb = CreateSklb(manager, redirections, type, genderRace, est.SwapApplied.Est.Entry);
|
||||
est.ChildSwaps.Add(sklb);
|
||||
}
|
||||
else if( est.SwapAppliedIsDefault )
|
||||
else if (est.SwapAppliedIsDefault)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -179,57 +176,55 @@ public static class ItemSwap
|
|||
return est;
|
||||
}
|
||||
|
||||
public static int GetStableHashCode( this string str )
|
||||
public static int GetStableHashCode(this string str)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hash1 = 5381;
|
||||
var hash2 = hash1;
|
||||
|
||||
for( var i = 0; i < str.Length && str[ i ] != '\0'; i += 2 )
|
||||
for (var i = 0; i < str.Length && str[i] != '\0'; i += 2)
|
||||
{
|
||||
hash1 = ( ( hash1 << 5 ) + hash1 ) ^ str[ i ];
|
||||
if( i == str.Length - 1 || str[ i + 1 ] == '\0' )
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ str[i];
|
||||
if (i == str.Length - 1 || str[i + 1] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
hash2 = ( ( hash2 << 5 ) + hash2 ) ^ str[ i + 1 ];
|
||||
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
|
||||
}
|
||||
|
||||
return hash1 + hash2 * 1566083941;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReplaceAnyId( string path, char idType, SetId id, bool condition = true )
|
||||
public static string ReplaceAnyId(string path, char idType, SetId id, bool condition = true)
|
||||
=> condition
|
||||
? Regex.Replace( path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}" )
|
||||
? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}")
|
||||
: path;
|
||||
|
||||
public static string ReplaceAnyRace( string path, GenderRace to, bool condition = true )
|
||||
=> ReplaceAnyId( path, 'c', ( ushort )to, condition );
|
||||
public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true)
|
||||
=> ReplaceAnyId(path, 'c', (ushort)to, condition);
|
||||
|
||||
public static string ReplaceAnyBody( string path, BodySlot slot, SetId to, bool condition = true )
|
||||
=> ReplaceAnyId( path, slot.ToAbbreviation(), to, condition );
|
||||
public static string ReplaceAnyBody(string path, BodySlot slot, SetId to, bool condition = true)
|
||||
=> ReplaceAnyId(path, slot.ToAbbreviation(), to, condition);
|
||||
|
||||
public static string ReplaceId( string path, char type, SetId idFrom, SetId idTo, bool condition = true )
|
||||
public static string ReplaceId(string path, char type, SetId idFrom, SetId idTo, bool condition = true)
|
||||
=> condition
|
||||
? path.Replace( $"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}" )
|
||||
? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}")
|
||||
: path;
|
||||
|
||||
public static string ReplaceSlot( string path, EquipSlot from, EquipSlot to, bool condition = true )
|
||||
public static string ReplaceSlot(string path, EquipSlot from, EquipSlot to, bool condition = true)
|
||||
=> condition
|
||||
? path.Replace( $"_{from.ToSuffix()}_", $"_{to.ToSuffix()}_" )
|
||||
? path.Replace($"_{from.ToSuffix()}_", $"_{to.ToSuffix()}_")
|
||||
: path;
|
||||
|
||||
public static string ReplaceRace( string path, GenderRace from, GenderRace to, bool condition = true )
|
||||
=> ReplaceId( path, 'c', ( ushort )from, ( ushort )to, condition );
|
||||
public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true)
|
||||
=> ReplaceId(path, 'c', (ushort)from, (ushort)to, condition);
|
||||
|
||||
public static string ReplaceBody( string path, BodySlot slot, SetId idFrom, SetId idTo, bool condition = true )
|
||||
=> ReplaceId( path, slot.ToAbbreviation(), idFrom, idTo, condition );
|
||||
public static string ReplaceBody(string path, BodySlot slot, SetId idFrom, SetId idTo, bool condition = true)
|
||||
=> ReplaceId(path, slot.ToAbbreviation(), idFrom, idTo, condition);
|
||||
|
||||
public static string AddSuffix( string path, string ext, string suffix, bool condition = true )
|
||||
public static string AddSuffix(string path, string ext, string suffix, bool condition = true)
|
||||
=> condition
|
||||
? path.Replace( ext, suffix + ext )
|
||||
? path.Replace(ext, suffix + ext)
|
||||
: path;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Penumbra.Meta.Manipulations;
|
|||
using Penumbra.String.Classes;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Mods.ItemSwap;
|
||||
|
|
@ -13,18 +14,18 @@ namespace Penumbra.Mods.ItemSwap;
|
|||
public class ItemSwapContainer
|
||||
{
|
||||
private readonly MetaFileManager _manager;
|
||||
private readonly IdentifierService _identifier;
|
||||
private readonly IdentifierService _identifier;
|
||||
|
||||
private Dictionary< Utf8GamePath, FullPath > _modRedirections = new();
|
||||
private HashSet< MetaManipulation > _modManipulations = new();
|
||||
private Dictionary<Utf8GamePath, FullPath> _modRedirections = new();
|
||||
private HashSet<MetaManipulation> _modManipulations = new();
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > ModRedirections
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections
|
||||
=> _modRedirections;
|
||||
|
||||
public IReadOnlySet< MetaManipulation > ModManipulations
|
||||
public IReadOnlySet<MetaManipulation> ModManipulations
|
||||
=> _modManipulations;
|
||||
|
||||
public readonly List< Swap > Swaps = new();
|
||||
public readonly List<Swap> Swaps = new();
|
||||
|
||||
public bool Loaded { get; private set; }
|
||||
|
||||
|
|
@ -40,72 +41,69 @@ public class ItemSwapContainer
|
|||
NoSwaps,
|
||||
}
|
||||
|
||||
public bool WriteMod( ModManager manager, Mod mod, WriteType writeType = WriteType.NoSwaps, DirectoryInfo? directory = null, int groupIndex = -1, int optionIndex = 0 )
|
||||
public bool WriteMod(ModManager manager, Mod mod, WriteType writeType = WriteType.NoSwaps, DirectoryInfo? directory = null,
|
||||
int groupIndex = -1, int optionIndex = 0)
|
||||
{
|
||||
var convertedManips = new HashSet< MetaManipulation >( Swaps.Count );
|
||||
var convertedFiles = new Dictionary< Utf8GamePath, FullPath >( Swaps.Count );
|
||||
var convertedSwaps = new Dictionary< Utf8GamePath, FullPath >( Swaps.Count );
|
||||
var convertedManips = new HashSet<MetaManipulation>(Swaps.Count);
|
||||
var convertedFiles = new Dictionary<Utf8GamePath, FullPath>(Swaps.Count);
|
||||
var convertedSwaps = new Dictionary<Utf8GamePath, FullPath>(Swaps.Count);
|
||||
directory ??= mod.ModPath;
|
||||
try
|
||||
{
|
||||
foreach( var swap in Swaps.SelectMany( s => s.WithChildren() ) )
|
||||
foreach (var swap in Swaps.SelectMany(s => s.WithChildren()))
|
||||
{
|
||||
switch( swap )
|
||||
switch (swap)
|
||||
{
|
||||
case FileSwap file:
|
||||
// Skip, nothing to do
|
||||
if( file.SwapToModdedEqualsOriginal )
|
||||
{
|
||||
if (file.SwapToModdedEqualsOriginal)
|
||||
continue;
|
||||
}
|
||||
|
||||
if( writeType == WriteType.UseSwaps && file.SwapToModdedExistsInGame && !file.DataWasChanged )
|
||||
if (writeType == WriteType.UseSwaps && file.SwapToModdedExistsInGame && !file.DataWasChanged)
|
||||
{
|
||||
convertedSwaps.TryAdd( file.SwapFromRequestPath, file.SwapToModded );
|
||||
convertedSwaps.TryAdd(file.SwapFromRequestPath, file.SwapToModded);
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = file.GetNewPath( directory.FullName );
|
||||
var path = file.GetNewPath(directory.FullName);
|
||||
var bytes = file.FileData.Write();
|
||||
Directory.CreateDirectory( Path.GetDirectoryName( path )! );
|
||||
_manager.Compactor.WriteAllBytes( path, bytes );
|
||||
convertedFiles.TryAdd( file.SwapFromRequestPath, new FullPath( path ) );
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
_manager.Compactor.WriteAllBytes(path, bytes);
|
||||
convertedFiles.TryAdd(file.SwapFromRequestPath, new FullPath(path));
|
||||
}
|
||||
|
||||
break;
|
||||
case MetaSwap meta:
|
||||
if( !meta.SwapAppliedIsDefault )
|
||||
{
|
||||
convertedManips.Add( meta.SwapApplied );
|
||||
}
|
||||
if (!meta.SwapAppliedIsDefault)
|
||||
convertedManips.Add(meta.SwapApplied);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
manager.OptionEditor.OptionSetFiles( mod, groupIndex, optionIndex, convertedFiles );
|
||||
manager.OptionEditor.OptionSetFileSwaps( mod, groupIndex, optionIndex, convertedSwaps );
|
||||
manager.OptionEditor.OptionSetManipulations( mod, groupIndex, optionIndex, convertedManips );
|
||||
manager.OptionEditor.OptionSetFiles(mod, groupIndex, optionIndex, convertedFiles);
|
||||
manager.OptionEditor.OptionSetFileSwaps(mod, groupIndex, optionIndex, convertedSwaps);
|
||||
manager.OptionEditor.OptionSetManipulations(mod, groupIndex, optionIndex, convertedManips);
|
||||
return true;
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not write FileSwapContainer to {mod.ModPath}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not write FileSwapContainer to {mod.ModPath}:\n{e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadMod( Mod? mod, ModSettings? settings )
|
||||
public void LoadMod(Mod? mod, ModSettings? settings)
|
||||
{
|
||||
Clear();
|
||||
if( mod == null )
|
||||
if (mod == null)
|
||||
{
|
||||
_modRedirections = new Dictionary< Utf8GamePath, FullPath >();
|
||||
_modManipulations = new HashSet< MetaManipulation >();
|
||||
_modRedirections = new Dictionary<Utf8GamePath, FullPath>();
|
||||
_modManipulations = new HashSet<MetaManipulation>();
|
||||
}
|
||||
else
|
||||
{
|
||||
( _modRedirections, _modManipulations ) = ModSettings.GetResolveData( mod, settings );
|
||||
(_modRedirections, _modManipulations) = ModSettings.GetResolveData(mod, settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,59 +111,61 @@ public class ItemSwapContainer
|
|||
{
|
||||
_manager = manager;
|
||||
_identifier = identifier;
|
||||
LoadMod( null, null );
|
||||
LoadMod(null, null);
|
||||
}
|
||||
|
||||
private Func< Utf8GamePath, FullPath > PathResolver( ModCollection? collection )
|
||||
private Func<Utf8GamePath, FullPath> PathResolver(ModCollection? collection)
|
||||
=> collection != null
|
||||
? p => collection.ResolvePath( p ) ?? new FullPath( p )
|
||||
: p => ModRedirections.TryGetValue( p, out var path ) ? path : new FullPath( p );
|
||||
? p => collection.ResolvePath(p) ?? new FullPath(p)
|
||||
: p => ModRedirections.TryGetValue(p, out var path) ? path : new FullPath(p);
|
||||
|
||||
private Func< MetaManipulation, MetaManipulation > MetaResolver( ModCollection? collection )
|
||||
private Func<MetaManipulation, MetaManipulation> MetaResolver(ModCollection? collection)
|
||||
{
|
||||
var set = collection?.MetaCache?.Manipulations.ToHashSet() ?? _modManipulations;
|
||||
return m => set.TryGetValue( m, out var a ) ? a : m;
|
||||
return m => set.TryGetValue(m, out var a) ? a : m;
|
||||
}
|
||||
|
||||
public EquipItem[] LoadEquipment( EquipItem from, EquipItem to, ModCollection? collection = null, bool useRightRing = true, bool useLeftRing = true )
|
||||
public EquipItem[] LoadEquipment(EquipItem from, EquipItem to, ModCollection? collection = null, bool useRightRing = true,
|
||||
bool useLeftRing = true)
|
||||
{
|
||||
Swaps.Clear();
|
||||
Loaded = false;
|
||||
var ret = EquipmentSwap.CreateItemSwap( _manager, _identifier.AwaitedService, Swaps, PathResolver( collection ), MetaResolver( collection ), from, to, useRightRing, useLeftRing );
|
||||
var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection),
|
||||
from, to, useRightRing, useLeftRing);
|
||||
Loaded = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public EquipItem[] LoadTypeSwap( EquipSlot slotFrom, EquipItem from, EquipSlot slotTo, EquipItem to, ModCollection? collection = null )
|
||||
public EquipItem[] LoadTypeSwap(EquipSlot slotFrom, EquipItem from, EquipSlot slotTo, EquipItem to, ModCollection? collection = null)
|
||||
{
|
||||
Swaps.Clear();
|
||||
Loaded = false;
|
||||
var ret = EquipmentSwap.CreateTypeSwap( _manager, _identifier.AwaitedService, Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
||||
var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection),
|
||||
slotFrom, from, slotTo, to);
|
||||
Loaded = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool LoadCustomization( MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to, ModCollection? collection = null )
|
||||
public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to,
|
||||
ModCollection? collection = null)
|
||||
{
|
||||
var pathResolver = PathResolver( collection );
|
||||
var mdl = CustomizationSwap.CreateMdl( manager, pathResolver, slot, race, from, to );
|
||||
var pathResolver = PathResolver(collection);
|
||||
var mdl = CustomizationSwap.CreateMdl(manager, pathResolver, slot, race, from, to);
|
||||
var type = slot switch
|
||||
{
|
||||
BodySlot.Hair => EstManipulation.EstType.Hair,
|
||||
BodySlot.Face => EstManipulation.EstType.Face,
|
||||
_ => ( EstManipulation.EstType )0,
|
||||
_ => (EstManipulation.EstType)0,
|
||||
};
|
||||
|
||||
var metaResolver = MetaResolver( collection );
|
||||
var est = ItemSwap.CreateEst( manager, pathResolver, metaResolver, type, race, from, to, true );
|
||||
var metaResolver = MetaResolver(collection);
|
||||
var est = ItemSwap.CreateEst(manager, pathResolver, metaResolver, type, race, from, to, true);
|
||||
|
||||
Swaps.Add( mdl );
|
||||
if( est != null )
|
||||
{
|
||||
Swaps.Add( est );
|
||||
}
|
||||
Swaps.Add(mdl);
|
||||
if (est != null)
|
||||
Swaps.Add(est);
|
||||
|
||||
Loaded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,4 +89,4 @@ public class ModExportManager : IDisposable
|
|||
new ModBackup(this, mod).Move(null, newDirectory.Name);
|
||||
mod.ModPath = newDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Text.RegularExpressions;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
namespace Penumbra.Mods.Manager;
|
||||
|
||||
public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Penumbra.Import;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.Services;
|
||||
|
|
@ -16,7 +15,7 @@ public enum NewDirectoryState
|
|||
Identical,
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Describes the state of a changed mod event. </summary>
|
||||
public enum ModPathChangeType
|
||||
{
|
||||
|
|
@ -25,7 +24,7 @@ public enum ModPathChangeType
|
|||
Moved,
|
||||
Reloaded,
|
||||
StartingReload,
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ModManager : ModStorage, IDisposable
|
||||
{
|
||||
|
|
@ -46,8 +45,8 @@ public sealed class ModManager : ModStorage, IDisposable
|
|||
_communicator = communicator;
|
||||
DataEditor = dataEditor;
|
||||
OptionEditor = optionEditor;
|
||||
Creator = creator;
|
||||
SetBaseDirectory(config.ModDirectory, true);
|
||||
Creator = creator;
|
||||
SetBaseDirectory(config.ModDirectory, true);
|
||||
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModManager);
|
||||
DiscoverMods();
|
||||
}
|
||||
|
|
@ -242,7 +241,7 @@ public sealed class ModManager : ModStorage, IDisposable
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModPathChangeType.Added:
|
||||
case ModPathChangeType.Added:
|
||||
SetNew(mod);
|
||||
break;
|
||||
case ModPathChangeType.Deleted:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using Penumbra.Api.Enums;
|
|||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods.Manager;
|
||||
|
||||
|
|
@ -19,7 +18,9 @@ public static partial class ModMigration
|
|||
private static partial Regex GroupStartRegex();
|
||||
|
||||
public static bool Migrate(ModCreator creator, SaveService saveService, Mod mod, JObject json, ref uint fileVersion)
|
||||
=> MigrateV0ToV1(creator, saveService, mod, json, ref fileVersion) || MigrateV1ToV2(saveService, mod, ref fileVersion) || MigrateV2ToV3(mod, ref fileVersion);
|
||||
=> MigrateV0ToV1(creator, saveService, mod, json, ref fileVersion)
|
||||
|| MigrateV1ToV2(saveService, mod, ref fileVersion)
|
||||
|| MigrateV2ToV3(mod, ref fileVersion);
|
||||
|
||||
private static bool MigrateV2ToV3(Mod _, ref uint fileVersion)
|
||||
{
|
||||
|
|
@ -63,8 +64,8 @@ public static partial class ModMigration
|
|||
|
||||
var swaps = json["FileSwaps"]?.ToObject<Dictionary<Utf8GamePath, FullPath>>()
|
||||
?? new Dictionary<Utf8GamePath, FullPath>();
|
||||
var groups = json["Groups"]?.ToObject<Dictionary<string, OptionGroupV0>>() ?? new Dictionary<string, OptionGroupV0>();
|
||||
var priority = 1;
|
||||
var groups = json["Groups"]?.ToObject<Dictionary<string, OptionGroupV0>>() ?? new Dictionary<string, OptionGroupV0>();
|
||||
var priority = 1;
|
||||
var seenMetaFiles = new HashSet<FullPath>();
|
||||
foreach (var group in groups.Values)
|
||||
ConvertGroup(creator, mod, group, ref priority, seenMetaFiles);
|
||||
|
|
@ -128,8 +129,8 @@ public static partial class ModMigration
|
|||
var optionPriority = 0;
|
||||
var newMultiGroup = new MultiModGroup()
|
||||
{
|
||||
Name = group.GroupName,
|
||||
Priority = priority++,
|
||||
Name = group.GroupName,
|
||||
Priority = priority++,
|
||||
Description = string.Empty,
|
||||
};
|
||||
mod.Groups.Add(newMultiGroup);
|
||||
|
|
@ -146,8 +147,8 @@ public static partial class ModMigration
|
|||
|
||||
var newSingleGroup = new SingleModGroup()
|
||||
{
|
||||
Name = group.GroupName,
|
||||
Priority = priority++,
|
||||
Name = group.GroupName,
|
||||
Priority = priority++,
|
||||
Description = string.Empty,
|
||||
};
|
||||
mod.Groups.Add(newSingleGroup);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using Penumbra.String.Classes;
|
|||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public sealed partial class Mod : IMod
|
||||
public sealed class Mod : IMod
|
||||
{
|
||||
public static readonly TemporaryMod ForcedFiles = new()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -113,9 +113,7 @@ public partial class ModCreator
|
|||
}
|
||||
|
||||
if (changes)
|
||||
{
|
||||
_saveService.SaveAllOptionGroups(mod, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Load the default option for a given mod.</summary>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,58 +1,57 @@
|
|||
using Newtonsoft.Json;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
public interface ISubMod
|
||||
{
|
||||
public string Name { get; }
|
||||
public string FullName { get; }
|
||||
public string Name { get; }
|
||||
public string FullName { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > Files { get; }
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > FileSwaps { get; }
|
||||
public IReadOnlySet< MetaManipulation > Manipulations { get; }
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> Files { get; }
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> FileSwaps { get; }
|
||||
public IReadOnlySet<MetaManipulation> Manipulations { get; }
|
||||
|
||||
public bool IsDefault { get; }
|
||||
|
||||
public static void WriteSubMod( JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, int? priority )
|
||||
public static void WriteSubMod(JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, int? priority)
|
||||
{
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName( nameof( Name ) );
|
||||
j.WriteValue( mod.Name );
|
||||
j.WritePropertyName( nameof(Description) );
|
||||
j.WriteValue( mod.Description );
|
||||
if( priority != null )
|
||||
j.WritePropertyName(nameof(Name));
|
||||
j.WriteValue(mod.Name);
|
||||
j.WritePropertyName(nameof(Description));
|
||||
j.WriteValue(mod.Description);
|
||||
if (priority != null)
|
||||
{
|
||||
j.WritePropertyName( nameof( IModGroup.Priority ) );
|
||||
j.WriteValue( priority.Value );
|
||||
j.WritePropertyName(nameof(IModGroup.Priority));
|
||||
j.WriteValue(priority.Value);
|
||||
}
|
||||
|
||||
j.WritePropertyName( nameof( mod.Files ) );
|
||||
j.WritePropertyName(nameof(mod.Files));
|
||||
j.WriteStartObject();
|
||||
foreach( var (gamePath, file) in mod.Files )
|
||||
foreach (var (gamePath, file) in mod.Files)
|
||||
{
|
||||
if( file.ToRelPath( basePath, out var relPath ) )
|
||||
if (file.ToRelPath(basePath, out var relPath))
|
||||
{
|
||||
j.WritePropertyName( gamePath.ToString() );
|
||||
j.WriteValue( relPath.ToString() );
|
||||
j.WritePropertyName(gamePath.ToString());
|
||||
j.WriteValue(relPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WritePropertyName( nameof( mod.FileSwaps ) );
|
||||
j.WritePropertyName(nameof(mod.FileSwaps));
|
||||
j.WriteStartObject();
|
||||
foreach( var (gamePath, file) in mod.FileSwaps )
|
||||
foreach (var (gamePath, file) in mod.FileSwaps)
|
||||
{
|
||||
j.WritePropertyName( gamePath.ToString() );
|
||||
j.WriteValue( file.ToString() );
|
||||
j.WritePropertyName(gamePath.ToString());
|
||||
j.WriteValue(file.ToString());
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WritePropertyName( nameof( mod.Manipulations ) );
|
||||
serializer.Serialize( j, mod.Manipulations );
|
||||
j.WritePropertyName(nameof(mod.Manipulations));
|
||||
serializer.Serialize(j, mod.Manipulations);
|
||||
j.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ using OtterGui.Filesystem;
|
|||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
/// <summary> Contains the settings for a given mod. </summary>
|
||||
public class ModSettings
|
||||
|
|
@ -267,4 +266,4 @@ public class ModSettings
|
|||
|
||||
return ( Enabled, Priority, dict );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ using Newtonsoft.Json.Linq;
|
|||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
/// <summary> Groups that allow all available options to be selected at once. </summary>
|
||||
public sealed class MultiModGroup : IModGroup
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ using Newtonsoft.Json.Linq;
|
|||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
/// <summary> Groups that allow only one of their available options to be selected. </summary>
|
||||
public sealed class SingleModGroup : IModGroup
|
||||
|
|
@ -18,59 +17,55 @@ public sealed class SingleModGroup : IModGroup
|
|||
public int Priority { get; set; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
|
||||
public readonly List< SubMod > OptionData = new();
|
||||
public readonly List<SubMod> OptionData = new();
|
||||
|
||||
public int OptionPriority( Index _ )
|
||||
public int OptionPriority(Index _)
|
||||
=> Priority;
|
||||
|
||||
public ISubMod this[ Index idx ]
|
||||
=> OptionData[ idx ];
|
||||
public ISubMod this[Index idx]
|
||||
=> OptionData[idx];
|
||||
|
||||
[JsonIgnore]
|
||||
public int Count
|
||||
=> OptionData.Count;
|
||||
|
||||
public IEnumerator< ISubMod > GetEnumerator()
|
||||
public IEnumerator<ISubMod> GetEnumerator()
|
||||
=> OptionData.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public static SingleModGroup? Load( Mod mod, JObject json, int groupIdx )
|
||||
public static SingleModGroup? Load(Mod mod, JObject json, int groupIdx)
|
||||
{
|
||||
var options = json[ "Options" ];
|
||||
var options = json["Options"];
|
||||
var ret = new SingleModGroup
|
||||
{
|
||||
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 >() ?? 0u,
|
||||
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>() ?? 0u,
|
||||
};
|
||||
if( ret.Name.Length == 0 )
|
||||
{
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
if( options != null )
|
||||
{
|
||||
foreach( var child in options.Children() )
|
||||
if (options != null)
|
||||
foreach (var child in options.Children())
|
||||
{
|
||||
var subMod = new SubMod( mod );
|
||||
subMod.SetPosition( groupIdx, ret.OptionData.Count );
|
||||
subMod.Load( mod.ModPath, child, out _ );
|
||||
ret.OptionData.Add( subMod );
|
||||
var subMod = new SubMod(mod);
|
||||
subMod.SetPosition(groupIdx, ret.OptionData.Count);
|
||||
subMod.Load(mod.ModPath, child, out _);
|
||||
ret.OptionData.Add(subMod);
|
||||
}
|
||||
}
|
||||
|
||||
if( ( int )ret.DefaultSettings >= ret.Count )
|
||||
if ((int)ret.DefaultSettings >= ret.Count)
|
||||
ret.DefaultSettings = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IModGroup Convert( GroupType type )
|
||||
public IModGroup Convert(GroupType type)
|
||||
{
|
||||
switch( type )
|
||||
switch (type)
|
||||
{
|
||||
case GroupType.Single: return this;
|
||||
case GroupType.Multi:
|
||||
|
|
@ -79,47 +74,41 @@ public sealed class SingleModGroup : IModGroup
|
|||
Name = Name,
|
||||
Description = Description,
|
||||
Priority = Priority,
|
||||
DefaultSettings = 1u << ( int )DefaultSettings,
|
||||
DefaultSettings = 1u << (int)DefaultSettings,
|
||||
};
|
||||
multi.PrioritizedOptions.AddRange( OptionData.Select( ( o, i ) => ( o, i ) ) );
|
||||
multi.PrioritizedOptions.AddRange(OptionData.Select((o, i) => (o, i)));
|
||||
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( !OptionData.Move( optionIdxFrom, optionIdxTo ) )
|
||||
{
|
||||
if (!OptionData.Move(optionIdxFrom, optionIdxTo))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update default settings with the move.
|
||||
if( DefaultSettings == optionIdxFrom )
|
||||
if (DefaultSettings == optionIdxFrom)
|
||||
{
|
||||
DefaultSettings = ( uint )optionIdxTo;
|
||||
DefaultSettings = (uint)optionIdxTo;
|
||||
}
|
||||
else if( optionIdxFrom < optionIdxTo )
|
||||
else if (optionIdxFrom < optionIdxTo)
|
||||
{
|
||||
if( DefaultSettings > optionIdxFrom && DefaultSettings <= optionIdxTo )
|
||||
{
|
||||
if (DefaultSettings > optionIdxFrom && DefaultSettings <= optionIdxTo)
|
||||
--DefaultSettings;
|
||||
}
|
||||
}
|
||||
else if( DefaultSettings < optionIdxFrom && DefaultSettings >= optionIdxTo )
|
||||
else if (DefaultSettings < optionIdxFrom && DefaultSettings >= optionIdxTo)
|
||||
{
|
||||
++DefaultSettings;
|
||||
}
|
||||
|
||||
UpdatePositions( Math.Min( 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 OptionData.WithIndex().Skip( from ) )
|
||||
{
|
||||
o.SetPosition( o.GroupIdx, i );
|
||||
}
|
||||
foreach (var (o, i) in OptionData.WithIndex().Skip(from))
|
||||
o.SetPosition(o.GroupIdx, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Import;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
/// <summary>
|
||||
/// A sub mod is a collection of
|
||||
|
|
@ -5,7 +5,6 @@ using Penumbra.Mods.Manager;
|
|||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
@ -21,80 +20,84 @@ public class TemporaryMod : IMod
|
|||
public readonly SubMod Default;
|
||||
|
||||
ISubMod IMod.Default
|
||||
=> Default;
|
||||
=> Default;
|
||||
|
||||
public IReadOnlyList< IModGroup > Groups
|
||||
=> Array.Empty< IModGroup >();
|
||||
public IReadOnlyList<IModGroup> Groups
|
||||
=> Array.Empty<IModGroup>();
|
||||
|
||||
public IEnumerable< SubMod > AllSubMods
|
||||
=> new[] { Default };
|
||||
public IEnumerable<SubMod> AllSubMods
|
||||
=> new[]
|
||||
{
|
||||
Default,
|
||||
};
|
||||
|
||||
public TemporaryMod()
|
||||
=> Default = new SubMod( this );
|
||||
=> Default = new SubMod(this);
|
||||
|
||||
public void SetFile( Utf8GamePath gamePath, FullPath fullPath )
|
||||
=> Default.FileData[ gamePath ] = fullPath;
|
||||
public void SetFile(Utf8GamePath gamePath, FullPath fullPath)
|
||||
=> Default.FileData[gamePath] = fullPath;
|
||||
|
||||
public bool SetManipulation( MetaManipulation manip )
|
||||
=> Default.ManipulationData.Remove( manip ) | Default.ManipulationData.Add( manip );
|
||||
public bool SetManipulation(MetaManipulation manip)
|
||||
=> Default.ManipulationData.Remove(manip) | Default.ManipulationData.Add(manip);
|
||||
|
||||
public void SetAll( Dictionary< Utf8GamePath, FullPath > dict, HashSet< MetaManipulation > manips )
|
||||
public void SetAll(Dictionary<Utf8GamePath, FullPath> dict, HashSet<MetaManipulation> manips)
|
||||
{
|
||||
Default.FileData = dict;
|
||||
Default.ManipulationData = manips;
|
||||
}
|
||||
|
||||
public static void SaveTempCollection( Configuration config, SaveService saveService, ModManager modManager, ModCollection collection, string? character = null )
|
||||
public static void SaveTempCollection(Configuration config, SaveService saveService, ModManager modManager, ModCollection collection,
|
||||
string? character = null)
|
||||
{
|
||||
DirectoryInfo? dir = null;
|
||||
try
|
||||
{
|
||||
dir = ModCreator.CreateModFolder( modManager.BasePath, collection.Name );
|
||||
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 );
|
||||
var mod = new Mod( dir );
|
||||
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name);
|
||||
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);
|
||||
var mod = new Mod(dir);
|
||||
var defaultMod = mod.Default;
|
||||
foreach( var (gamePath, fullPath) in collection.ResolvedFiles )
|
||||
foreach (var (gamePath, fullPath) in collection.ResolvedFiles)
|
||||
{
|
||||
if( gamePath.Path.EndsWith( ".imc"u8 ) )
|
||||
if (gamePath.Path.EndsWith(".imc"u8))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetPath = fullPath.Path.FullName;
|
||||
if( fullPath.Path.Name.StartsWith( '|' ) )
|
||||
if (fullPath.Path.Name.StartsWith('|'))
|
||||
{
|
||||
targetPath = targetPath.Split( '|', 3, StringSplitOptions.RemoveEmptyEntries ).Last();
|
||||
targetPath = targetPath.Split('|', 3, StringSplitOptions.RemoveEmptyEntries).Last();
|
||||
}
|
||||
|
||||
if( Path.IsPathRooted(targetPath) )
|
||||
if (Path.IsPathRooted(targetPath))
|
||||
{
|
||||
var target = Path.Combine( fileDir.FullName, Path.GetFileName(targetPath) );
|
||||
File.Copy( targetPath, target, true );
|
||||
defaultMod.FileData[ gamePath ] = new FullPath( target );
|
||||
var target = Path.Combine(fileDir.FullName, Path.GetFileName(targetPath));
|
||||
File.Copy(targetPath, target, true);
|
||||
defaultMod.FileData[gamePath] = new FullPath(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultMod.FileSwapData[ gamePath ] = new FullPath(targetPath);
|
||||
defaultMod.FileSwapData[gamePath] = new FullPath(targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
foreach( var manip in collection.MetaCache?.Manipulations ?? Array.Empty< MetaManipulation >() )
|
||||
defaultMod.ManipulationData.Add( manip );
|
||||
foreach (var manip in collection.MetaCache?.Manipulations ?? Array.Empty<MetaManipulation>())
|
||||
defaultMod.ManipulationData.Add(manip);
|
||||
|
||||
saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod));
|
||||
modManager.AddMod( dir );
|
||||
Penumbra.Log.Information( $"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}." );
|
||||
modManager.AddMod(dir);
|
||||
Penumbra.Log.Information($"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}.");
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not save temporary collection {collection.Name} to permanent Mod:\n{e}" );
|
||||
if( dir != null && Directory.Exists( dir.FullName ) )
|
||||
Penumbra.Log.Error($"Could not save temporary collection {collection.Name} to permanent Mod:\n{e}");
|
||||
if (dir != null && Directory.Exists(dir.FullName))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete( dir.FullName, true );
|
||||
Directory.Delete(dir.FullName, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -103,4 +106,4 @@ public class TemporaryMod : IMod
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue