diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index 3bdc99a9..49e8ae66 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -34,7 +34,7 @@ public static class EquipmentSwap swaps.Add( eqp ); } - var gmp = CreateGmp( manips, slotFrom, idFrom, idTo); + var gmp = CreateGmp( manips, slotFrom, idFrom, idTo ); if( gmp != null ) { swaps.Add( gmp ); @@ -52,9 +52,19 @@ public static class EquipmentSwap _ => ( EstManipulation.EstType )0, }; + var skipFemale = false; + var skipMale = false; var mtrlVariantTo = imcFileTo.GetEntry( ImcFile.PartIndex( slotFrom ), variantTo ).MaterialId; foreach( var gr in Enum.GetValues< GenderRace >() ) { + switch( gr.Split().Item1 ) + { + case Gender.Male when skipMale: continue; + case Gender.Female when skipFemale: continue; + case Gender.MaleNpc when skipMale: continue; + case Gender.FemaleNpc when skipFemale: continue; + } + if( CharacterUtility.EqdpIdx( gr, isAccessory ) < 0 ) { continue; @@ -66,10 +76,26 @@ public static class EquipmentSwap swaps.Add( est ); } - var eqdp = CreateEqdp( redirections, manips, slotFrom, gr, idFrom, idTo, mtrlVariantTo ); - if( eqdp != null ) + try { - swaps.Add( eqdp ); + var eqdp = CreateEqdp( redirections, manips, slotFrom, gr, idFrom, idTo, mtrlVariantTo ); + if( eqdp != null ) + { + swaps.Add( eqdp ); + } + } + catch( ItemSwap.MissingFileException e ) + { + switch( gr ) + { + case GenderRace.MidlanderMale when e.Type == ResourceType.Mdl: + skipMale = true; + continue; + case GenderRace.MidlanderFemale when e.Type == ResourceType.Mdl: + skipFemale = true; + continue; + default: throw; + } } } @@ -79,7 +105,6 @@ public static class EquipmentSwap swaps.Add( imc ); } - return affectedItems; } @@ -106,8 +131,9 @@ public static class EquipmentSwap public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slot, GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo ) { - var mdlPathFrom = GamePaths.Equipment.Mdl.Path( idFrom, gr, slot ); - var mdlPathTo = GamePaths.Equipment.Mdl.Path( idTo, gr, slot ); + var accessory = slot.IsAccessory(); + var mdlPathFrom = accessory ? GamePaths.Accessory.Mdl.Path( idFrom, gr, slot ) : GamePaths.Equipment.Mdl.Path( idFrom, gr, slot ); + var mdlPathTo = accessory ? GamePaths.Accessory.Mdl.Path( idTo, gr, slot ) : GamePaths.Equipment.Mdl.Path( idTo, gr, slot ); var mdl = FileSwap.CreateSwap( ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo ); foreach( ref var fileName in mdl.AsMdl()!.Materials.AsSpan() ) @@ -292,7 +318,7 @@ public static class EquipmentSwap } var newPath = ItemSwap.ReplaceAnyId( path, prefix, idFrom ); - newPath = ItemSwap.AddSuffix( newPath, ".tex", $"_{Path.GetFileName( texture.Path ).GetStableHashCode():x8}", true ); + newPath = ItemSwap.AddSuffix( newPath, ".tex", $"_{Path.GetFileName( texture.Path ).GetStableHashCode():x8}" ); if( newPath != path ) { texture.Path = addedDashes ? newPath.Replace( "--", string.Empty ) : newPath; @@ -311,7 +337,7 @@ public static class EquipmentSwap public static FileSwap CreateAtex( Func< Utf8GamePath, FullPath > redirections, ref string filePath, ref bool dataWasChanged ) { var oldPath = filePath; - filePath = ItemSwap.AddSuffix( filePath, ".atex", $"_{Path.GetFileName( filePath ).GetStableHashCode():x8}", true ); + filePath = ItemSwap.AddSuffix( filePath, ".atex", $"_{Path.GetFileName( filePath ).GetStableHashCode():x8}" ); dataWasChanged = true; return FileSwap.CreateSwap( ResourceType.Atex, redirections, filePath, oldPath, oldPath ); diff --git a/Penumbra/Mods/ItemSwap/ItemSwap.cs b/Penumbra/Mods/ItemSwap/ItemSwap.cs index d8e8809a..68812674 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwap.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwap.cs @@ -23,6 +23,15 @@ public static class ItemSwap public class IdUnavailableException : Exception { } + public class MissingFileException : Exception + { + public readonly ResourceType Type; + + public MissingFileException( ResourceType type, object path ) + : base($"Could not load {type} File Data for \"{path}\".") + => Type = type; + } + private static bool LoadFile( FullPath path, out byte[] data ) { if( path.FullName.Length > 0 ) diff --git a/Penumbra/Mods/ItemSwap/Swaps.cs b/Penumbra/Mods/ItemSwap/Swaps.cs index 5e40f9ac..332aa6ac 100644 --- a/Penumbra/Mods/ItemSwap/Swaps.cs +++ b/Penumbra/Mods/ItemSwap/Swaps.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using Penumbra.GameData.Enums; +using static Penumbra.Mods.ItemSwap.ItemSwap; namespace Penumbra.Mods.ItemSwap; @@ -63,7 +64,7 @@ public sealed class FileSwap : Swap public ResourceType Type; /// The binary or parsed data of the file at SwapToModded. - public IWritable FileData = ItemSwap.GenericFile.Invalid; + public IWritable FileData = GenericFile.Invalid; /// The path that would be requested without manipulated parent files. public string SwapFromPreChangePath = string.Empty; @@ -114,12 +115,13 @@ public sealed class FileSwap : Swap /// The unmodded path to the file the game is supposed to load instead. /// A full swap container with the actual file in memory. /// True if everything could be read correctly, false otherwise. - public static FileSwap CreateSwap( ResourceType type, Func< Utf8GamePath, FullPath > redirections, string swapFromRequest, string swapToRequest, string? swapFromPreChange = null ) + public static FileSwap CreateSwap( ResourceType type, Func< Utf8GamePath, FullPath > redirections, string swapFromRequest, string swapToRequest, + string? swapFromPreChange = null ) { var swap = new FileSwap { Type = type, - FileData = ItemSwap.GenericFile.Invalid, + FileData = GenericFile.Invalid, DataWasChanged = false, SwapFromPreChangePath = swapFromPreChange ?? swapFromRequest, SwapFromChanged = swapFromPreChange != swapFromRequest, @@ -142,10 +144,10 @@ public sealed class FileSwap : Swap swap.FileData = type switch { - ResourceType.Mdl => ItemSwap.LoadMdl( swap.SwapToModded, out var f ) ? f : throw new Exception( $"Could not load file data for {swap.SwapToModded}." ), - ResourceType.Mtrl => ItemSwap.LoadMtrl( swap.SwapToModded, out var f ) ? f : throw new Exception( $"Could not load file data for {swap.SwapToModded}." ), - ResourceType.Avfx => ItemSwap.LoadAvfx( swap.SwapToModded, out var f ) ? f : throw new Exception( $"Could not load file data for {swap.SwapToModded}." ), - _ => ItemSwap.LoadFile( swap.SwapToModded, out var f ) ? f : throw new Exception( $"Could not load file data for {swap.SwapToModded}." ), + ResourceType.Mdl => LoadMdl( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ), + ResourceType.Mtrl => LoadMtrl( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ), + ResourceType.Avfx => LoadAvfx( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ), + _ => LoadFile( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ), }; return swap; @@ -166,7 +168,7 @@ public sealed class FileSwap : Swap var name = $"{( oldFilename.StartsWith( "--" ) ? "--" : string.Empty )}{string.Join( null, hash.Select( c => c.ToString( "x2" ) ) )}.{swap.Type.ToString().ToLowerInvariant()}"; var newPath = path.Replace( oldFilename, name ); - var newSwap = CreateSwap( swap.Type, redirections, newPath, swap.SwapToRequestPath.ToString()); + var newSwap = CreateSwap( swap.Type, redirections, newPath, swap.SwapToRequestPath.ToString() ); path = newPath; dataWasChanged = true;