From e14fedf59e22813917451fb4117a78bb1ea28e30 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 19 May 2023 23:00:44 +0200 Subject: [PATCH] Add some Metadata validation. --- Penumbra/Api/PenumbraApi.cs | 22 +-- Penumbra/Collections/Cache/ImcCache.cs | 4 +- .../Communication/CreatedCharacterBase.cs | 3 +- .../Communication/CreatingCharacterBase.cs | 3 +- Penumbra/Communication/EnabledChanged.cs | 3 +- Penumbra/Communication/ModDirectoryChanged.cs | 3 +- Penumbra/Communication/ModPathChanged.cs | 3 +- Penumbra/Communication/ModSettingChanged.cs | 3 +- .../Import/TexToolsMeta.Deserialization.cs | 2 +- .../Meta/Manipulations/EqdpManipulation.cs | 87 +++++----- .../Meta/Manipulations/EqpManipulation.cs | 13 ++ .../Meta/Manipulations/EstManipulation.cs | 10 ++ .../Meta/Manipulations/GmpManipulation.cs | 6 + .../Meta/Manipulations/ImcManipulation.cs | 45 ++++-- .../Meta/Manipulations/MetaManipulation.cs | 151 ++++++++++-------- .../Meta/Manipulations/RspManipulation.cs | 52 +++--- Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs | 2 +- 17 files changed, 254 insertions(+), 158 deletions(-) diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index d88d069e..369374c6 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -69,7 +69,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi return; CheckInitialized(); - _communicator.CreatingCharacterBase.Subscribe(new Action(value), Communication.CreatingCharacterBase.Priority.Api); + _communicator.CreatingCharacterBase.Subscribe(new Action(value), + Communication.CreatingCharacterBase.Priority.Api); } remove { @@ -89,7 +90,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi return; CheckInitialized(); - _communicator.CreatedCharacterBase.Subscribe(new Action(value), Communication.CreatedCharacterBase.Priority.Api); + _communicator.CreatedCharacterBase.Subscribe(new Action(value), + Communication.CreatedCharacterBase.Priority.Api); } remove { @@ -181,7 +183,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi public event ChangedItemClick? ChangedItemClicked { - add => _communicator.ChangedItemClick.Subscribe(new Action(value!), Communication.ChangedItemClick.Priority.Default); + add => _communicator.ChangedItemClick.Subscribe(new Action(value!), + Communication.ChangedItemClick.Priority.Default); remove => _communicator.ChangedItemClick.Unsubscribe(new Action(value!)); } @@ -1120,13 +1123,14 @@ public class PenumbraApi : IDisposable, IPenumbraApi } manips = new HashSet(manipArray!.Length); - foreach (var manip in manipArray.Where(m => m.ManipulationType != MetaManipulation.Type.Unknown)) + foreach (var manip in manipArray.Where(m => m.Validate())) { - if (!manips.Add(manip)) - { - manips = null; - return false; - } + if (manips.Add(manip)) + continue; + + Penumbra.Log.Warning($"Manipulation {manip} {manip.EntryToString()} is invalid and was skipped."); + manips = null; + return false; } return true; diff --git a/Penumbra/Collections/Cache/ImcCache.cs b/Penumbra/Collections/Cache/ImcCache.cs index 7689e5b2..97f2aa8a 100644 --- a/Penumbra/Collections/Cache/ImcCache.cs +++ b/Penumbra/Collections/Cache/ImcCache.cs @@ -36,7 +36,7 @@ public readonly struct ImcCache : IDisposable public bool ApplyMod( MetaFileManager manager, ModCollection collection, ImcManipulation manip ) { - if( !manip.Valid ) + if( !manip.Validate() ) { return false; } @@ -76,7 +76,7 @@ public readonly struct ImcCache : IDisposable public bool RevertMod( MetaFileManager manager, ModCollection collection, ImcManipulation m ) { - if( !m.Valid || !_imcManipulations.Remove( m ) ) + if( !m.Validate() || !_imcManipulations.Remove( m ) ) { return false; } diff --git a/Penumbra/Communication/CreatedCharacterBase.cs b/Penumbra/Communication/CreatedCharacterBase.cs index dacd51dd..adb92bfe 100644 --- a/Penumbra/Communication/CreatedCharacterBase.cs +++ b/Penumbra/Communication/CreatedCharacterBase.cs @@ -1,4 +1,5 @@ using System; +using Penumbra.Api; using Penumbra.Util; namespace Penumbra.Communication; @@ -12,7 +13,7 @@ public sealed class CreatedCharacterBase : EventWrapper + /// Api = 0, } diff --git a/Penumbra/Communication/CreatingCharacterBase.cs b/Penumbra/Communication/CreatingCharacterBase.cs index 357bc066..8dc1c634 100644 --- a/Penumbra/Communication/CreatingCharacterBase.cs +++ b/Penumbra/Communication/CreatingCharacterBase.cs @@ -1,4 +1,5 @@ using System; +using Penumbra.Api; using Penumbra.Util; namespace Penumbra.Communication; @@ -16,7 +17,7 @@ public sealed class CreatingCharacterBase : EventWrapper + /// Api = 0, } diff --git a/Penumbra/Communication/EnabledChanged.cs b/Penumbra/Communication/EnabledChanged.cs index ec63337f..793663b9 100644 --- a/Penumbra/Communication/EnabledChanged.cs +++ b/Penumbra/Communication/EnabledChanged.cs @@ -1,4 +1,5 @@ using System; +using Penumbra.Api; using Penumbra.Util; namespace Penumbra.Communication; @@ -13,7 +14,7 @@ public sealed class EnabledChanged : EventWrapper, EnabledChanged.P { public enum Priority { - /// + /// Api = int.MinValue, } diff --git a/Penumbra/Communication/ModDirectoryChanged.cs b/Penumbra/Communication/ModDirectoryChanged.cs index 102ddec4..0f30b71b 100644 --- a/Penumbra/Communication/ModDirectoryChanged.cs +++ b/Penumbra/Communication/ModDirectoryChanged.cs @@ -1,4 +1,5 @@ using System; +using Penumbra.Api; using Penumbra.Util; namespace Penumbra.Communication; @@ -14,7 +15,7 @@ public sealed class ModDirectoryChanged : EventWrapper, Mod { public enum Priority { - /// + /// Api = 0, /// diff --git a/Penumbra/Communication/ModPathChanged.cs b/Penumbra/Communication/ModPathChanged.cs index e5b1e20c..e35a0ff4 100644 --- a/Penumbra/Communication/ModPathChanged.cs +++ b/Penumbra/Communication/ModPathChanged.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Penumbra.Api; using Penumbra.Mods; using Penumbra.Mods.Manager; using Penumbra.Util; @@ -22,7 +23,7 @@ public sealed class ModPathChanged : EventWrapper CollectionCacheManagerAddition = -100, - /// + /// Api = 0, /// diff --git a/Penumbra/Communication/ModSettingChanged.cs b/Penumbra/Communication/ModSettingChanged.cs index e32a84c2..6445b956 100644 --- a/Penumbra/Communication/ModSettingChanged.cs +++ b/Penumbra/Communication/ModSettingChanged.cs @@ -1,4 +1,5 @@ using System; +using Penumbra.Api; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Mods; @@ -21,7 +22,7 @@ public sealed class ModSettingChanged : EventWrapper + /// Api = int.MinValue, /// diff --git a/Penumbra/Import/TexToolsMeta.Deserialization.cs b/Penumbra/Import/TexToolsMeta.Deserialization.cs index dc2047b2..92d2aa5a 100644 --- a/Penumbra/Import/TexToolsMeta.Deserialization.cs +++ b/Penumbra/Import/TexToolsMeta.Deserialization.cs @@ -122,7 +122,7 @@ public partial class TexToolsMeta { var imc = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, i, manip.EquipSlot, value); - if (imc.Valid) + if (imc.Validate()) MetaManipulations.Add(imc); } diff --git a/Penumbra/Meta/Manipulations/EqdpManipulation.cs b/Penumbra/Meta/Manipulations/EqdpManipulation.cs index 3e927407..526abdd4 100644 --- a/Penumbra/Meta/Manipulations/EqdpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqdpManipulation.cs @@ -9,91 +9,102 @@ using Penumbra.Meta.Files; namespace Penumbra.Meta.Manipulations; -[StructLayout( LayoutKind.Sequential, Pack = 1 )] -public readonly struct EqdpManipulation : IMetaManipulation< EqdpManipulation > +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public readonly struct EqdpManipulation : IMetaManipulation { public EqdpEntry Entry { get; private init; } - [JsonConverter( typeof( StringEnumConverter ) )] + [JsonConverter(typeof(StringEnumConverter))] public Gender Gender { get; private init; } - [JsonConverter( typeof( StringEnumConverter ) )] + [JsonConverter(typeof(StringEnumConverter))] public ModelRace Race { get; private init; } public ushort SetId { get; private init; } - [JsonConverter( typeof( StringEnumConverter ) )] + [JsonConverter(typeof(StringEnumConverter))] public EquipSlot Slot { get; private init; } [JsonConstructor] - public EqdpManipulation( EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, ushort setId ) + public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, ushort setId) { Gender = gender; Race = race; SetId = setId; Slot = slot; - Entry = Eqdp.Mask( Slot ) & entry; + Entry = Eqdp.Mask(Slot) & entry; } - public EqdpManipulation Copy( EqdpManipulation entry ) + public EqdpManipulation Copy(EqdpManipulation entry) { - if( entry.Slot != Slot ) + if (entry.Slot != Slot) { - var (bit1, bit2) = entry.Entry.ToBits( entry.Slot ); - return new EqdpManipulation(Eqdp.FromSlotAndBits( Slot, bit1, bit2 ), Slot, Gender, Race, SetId); + var (bit1, bit2) = entry.Entry.ToBits(entry.Slot); + return new EqdpManipulation(Eqdp.FromSlotAndBits(Slot, bit1, bit2), Slot, Gender, Race, SetId); } + return new EqdpManipulation(entry.Entry, Slot, Gender, Race, SetId); } - public EqdpManipulation Copy( EqdpEntry entry ) + public EqdpManipulation Copy(EqdpEntry entry) => new(entry, Slot, Gender, Race, SetId); public override string ToString() => $"Eqdp - {SetId} - {Slot} - {Race.ToName()} - {Gender.ToName()}"; - public bool Equals( EqdpManipulation other ) + public bool Equals(EqdpManipulation other) => Gender == other.Gender - && Race == other.Race + && Race == other.Race && SetId == other.SetId - && Slot == other.Slot; + && Slot == other.Slot; - public override bool Equals( object? obj ) - => obj is EqdpManipulation other && Equals( other ); + public override bool Equals(object? obj) + => obj is EqdpManipulation other && Equals(other); public override int GetHashCode() - => HashCode.Combine( ( int )Gender, ( int )Race, SetId, ( int )Slot ); + => HashCode.Combine((int)Gender, (int)Race, SetId, (int)Slot); - public int CompareTo( EqdpManipulation other ) + public int CompareTo(EqdpManipulation other) { - var r = Race.CompareTo( other.Race ); - if( r != 0 ) - { + var r = Race.CompareTo(other.Race); + if (r != 0) return r; - } - var g = Gender.CompareTo( other.Gender ); - if( g != 0 ) - { + var g = Gender.CompareTo(other.Gender); + if (g != 0) return g; - } - var set = SetId.CompareTo( other.SetId ); - return set != 0 ? set : Slot.CompareTo( other.Slot ); + var set = SetId.CompareTo(other.SetId); + return set != 0 ? set : Slot.CompareTo(other.Slot); } public MetaIndex FileIndex() - => CharacterUtilityData.EqdpIdx( Names.CombinedRace( Gender, Race ), Slot.IsAccessory() ); + => CharacterUtilityData.EqdpIdx(Names.CombinedRace(Gender, Race), Slot.IsAccessory()); - public bool Apply( ExpandedEqdpFile file ) + public bool Apply(ExpandedEqdpFile file) { - var entry = file[ SetId ]; - var mask = Eqdp.Mask( Slot ); - if( ( entry & mask ) == Entry ) - { + var entry = file[SetId]; + var mask = Eqdp.Mask(Slot); + if ((entry & mask) == Entry) return false; - } - file[ SetId ] = ( entry & ~mask ) | Entry; + file[SetId] = (entry & ~mask) | Entry; return true; } -} \ No newline at end of file + + public bool Validate() + { + var mask = Eqdp.Mask(Slot); + if (mask == 0) + return false; + + if ((mask & Entry) != Entry) + return false; + + if (FileIndex() == (MetaIndex)(-1)) + return false; + + // No check for set id. + return true; + } +} diff --git a/Penumbra/Meta/Manipulations/EqpManipulation.cs b/Penumbra/Meta/Manipulations/EqpManipulation.cs index 9d9010d2..b7a17c19 100644 --- a/Penumbra/Meta/Manipulations/EqpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqpManipulation.cs @@ -67,4 +67,17 @@ public readonly struct EqpManipulation : IMetaManipulation< EqpManipulation > file[ SetId ] = ( entry & ~mask ) | Entry; return true; } + + public bool Validate() + { + var mask = Eqp.Mask(Slot); + if (mask == 0) + return false; + if ((Entry & mask) != Entry) + return false; + + // No check for set id. + + return true; + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manipulations/EstManipulation.cs b/Penumbra/Meta/Manipulations/EstManipulation.cs index 24c22024..497c9219 100644 --- a/Penumbra/Meta/Manipulations/EstManipulation.cs +++ b/Penumbra/Meta/Manipulations/EstManipulation.cs @@ -103,4 +103,14 @@ public readonly struct EstManipulation : IMetaManipulation< EstManipulation > _ => throw new ArgumentOutOfRangeException(), }; } + + public bool Validate() + { + if (!Enum.IsDefined(Slot)) + return false; + if (Names.CombinedRace(Gender, Race) == GenderRace.Unknown) + return false; + // No known check for set id or entry. + return true; + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manipulations/GmpManipulation.cs b/Penumbra/Meta/Manipulations/GmpManipulation.cs index b8464abf..38669d12 100644 --- a/Penumbra/Meta/Manipulations/GmpManipulation.cs +++ b/Penumbra/Meta/Manipulations/GmpManipulation.cs @@ -51,4 +51,10 @@ public readonly struct GmpManipulation : IMetaManipulation< GmpManipulation > file[ SetId ] = Entry; return true; } + + public bool Validate() + { + // No known conditions. + return true; + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manipulations/ImcManipulation.cs b/Penumbra/Meta/Manipulations/ImcManipulation.cs index 2db291bd..9046f85e 100644 --- a/Penumbra/Meta/Manipulations/ImcManipulation.cs +++ b/Penumbra/Meta/Manipulations/ImcManipulation.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection.Metadata; using System.Runtime.InteropServices; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -72,15 +73,6 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation > } } - public bool Valid - => ObjectType switch - { - ObjectType.Accessory => BodySlot == BodySlot.Unknown, - ObjectType.Equipment => BodySlot == BodySlot.Unknown, - ObjectType.DemiHuman => BodySlot == BodySlot.Unknown, - _ => EquipSlot == EquipSlot.Unknown, - }; - public ImcManipulation Copy( ImcEntry entry ) => new(ObjectType, BodySlot, PrimaryId, SecondaryId, Variant, EquipSlot, entry); @@ -160,4 +152,39 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation > public bool Apply( ImcFile file ) => file.SetEntry( ImcFile.PartIndex( EquipSlot ), Variant, Entry ); + + public bool Validate() + { + switch (ObjectType) + { + case ObjectType.Accessory: + case ObjectType.Equipment: + if (BodySlot is not BodySlot.Unknown) + return false; + if (!EquipSlot.IsEquipmentPiece()) + return false; + if (SecondaryId != 0) + return false; + break; + case ObjectType.DemiHuman: + if (BodySlot is not BodySlot.Unknown) + return false; + if (!EquipSlot.IsEquipmentPiece()) + return false; + break; + default: + if (!Enum.IsDefined(BodySlot)) + return false; + if (EquipSlot is not EquipSlot.Unknown) + return false; + if (!Enum.IsDefined(ObjectType)) + return false; + break; + } + + if (Entry.MaterialId == 0) + return false; + + return true; + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manipulations/MetaManipulation.cs b/Penumbra/Meta/Manipulations/MetaManipulation.cs index 98d138dd..1e0760f1 100644 --- a/Penumbra/Meta/Manipulations/MetaManipulation.cs +++ b/Penumbra/Meta/Manipulations/MetaManipulation.cs @@ -12,12 +12,12 @@ public interface IMetaManipulation public MetaIndex FileIndex(); } -public interface IMetaManipulation< T > - : IMetaManipulation, IComparable< T >, IEquatable< T > where T : struct +public interface IMetaManipulation + : IMetaManipulation, IComparable, IEquatable where T : struct { } -[StructLayout( LayoutKind.Explicit, Pack = 1, Size = 16 )] -public readonly struct MetaManipulation : IEquatable< MetaManipulation >, IComparable< MetaManipulation > +[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 16)] +public readonly struct MetaManipulation : IEquatable, IComparable { public const int CurrentVersion = 0; @@ -32,33 +32,33 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa Rsp = 6, } - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly EqpManipulation Eqp = default; - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly GmpManipulation Gmp = default; - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly EqdpManipulation Eqdp = default; - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly EstManipulation Est = default; - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly RspManipulation Rsp = default; - [FieldOffset( 0 )] + [FieldOffset(0)] [JsonIgnore] public readonly ImcManipulation Imc = default; - [FieldOffset( 15 )] - [JsonConverter( typeof( StringEnumConverter ) )] - [JsonProperty( "Type" )] + [FieldOffset(15)] + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("Type")] public readonly Type ManipulationType; public object? Manipulation @@ -76,149 +76,157 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa }; init { - switch( value ) + switch (value) { case EqpManipulation m: Eqp = m; - ManipulationType = Type.Eqp; + ManipulationType = m.Validate() ? Type.Eqp : Type.Unknown; return; case EqdpManipulation m: Eqdp = m; - ManipulationType = Type.Eqdp; + ManipulationType = m.Validate() ? Type.Eqdp : Type.Unknown; return; case GmpManipulation m: Gmp = m; - ManipulationType = Type.Gmp; + ManipulationType = m.Validate() ? Type.Gmp : Type.Unknown; return; case EstManipulation m: Est = m; - ManipulationType = Type.Est; + ManipulationType = m.Validate() ? Type.Est : Type.Unknown; return; case RspManipulation m: Rsp = m; - ManipulationType = Type.Rsp; + ManipulationType = m.Validate() ? Type.Rsp : Type.Unknown; return; case ImcManipulation m: Imc = m; - ManipulationType = m.Valid ? Type.Imc : Type.Unknown; + ManipulationType = m.Validate() ? Type.Imc : Type.Unknown; return; } } } - public MetaManipulation( EqpManipulation eqp ) + public bool Validate() + { + return ManipulationType switch + { + Type.Imc => Imc.Validate(), + Type.Eqdp => Eqdp.Validate(), + Type.Eqp => Eqp.Validate(), + Type.Est => Est.Validate(), + Type.Gmp => Gmp.Validate(), + Type.Rsp => Rsp.Validate(), + _ => false, + }; + } + + public MetaManipulation(EqpManipulation eqp) { Eqp = eqp; ManipulationType = Type.Eqp; } - public MetaManipulation( GmpManipulation gmp ) + public MetaManipulation(GmpManipulation gmp) { Gmp = gmp; ManipulationType = Type.Gmp; } - public MetaManipulation( EqdpManipulation eqdp ) + public MetaManipulation(EqdpManipulation eqdp) { Eqdp = eqdp; ManipulationType = Type.Eqdp; } - public MetaManipulation( EstManipulation est ) + public MetaManipulation(EstManipulation est) { Est = est; ManipulationType = Type.Est; } - public MetaManipulation( RspManipulation rsp ) + public MetaManipulation(RspManipulation rsp) { Rsp = rsp; ManipulationType = Type.Rsp; } - public MetaManipulation( ImcManipulation imc ) + public MetaManipulation(ImcManipulation imc) { Imc = imc; ManipulationType = Type.Imc; } - public static implicit operator MetaManipulation( EqpManipulation eqp ) + public static implicit operator MetaManipulation(EqpManipulation eqp) => new(eqp); - public static implicit operator MetaManipulation( GmpManipulation gmp ) + public static implicit operator MetaManipulation(GmpManipulation gmp) => new(gmp); - public static implicit operator MetaManipulation( EqdpManipulation eqdp ) + public static implicit operator MetaManipulation(EqdpManipulation eqdp) => new(eqdp); - public static implicit operator MetaManipulation( EstManipulation est ) + public static implicit operator MetaManipulation(EstManipulation est) => new(est); - public static implicit operator MetaManipulation( RspManipulation rsp ) + public static implicit operator MetaManipulation(RspManipulation rsp) => new(rsp); - public static implicit operator MetaManipulation( ImcManipulation imc ) + public static implicit operator MetaManipulation(ImcManipulation imc) => new(imc); - public bool EntryEquals( MetaManipulation other ) + public bool EntryEquals(MetaManipulation other) { - if( ManipulationType != other.ManipulationType ) - { + if (ManipulationType != other.ManipulationType) return false; - } return ManipulationType switch { - Type.Eqp => Eqp.Entry.Equals( other.Eqp.Entry ), - Type.Gmp => Gmp.Entry.Equals( other.Gmp.Entry ), - Type.Eqdp => Eqdp.Entry.Equals( other.Eqdp.Entry ), - Type.Est => Est.Entry.Equals( other.Est.Entry ), - Type.Rsp => Rsp.Entry.Equals( other.Rsp.Entry ), - Type.Imc => Imc.Entry.Equals( other.Imc.Entry ), + Type.Eqp => Eqp.Entry.Equals(other.Eqp.Entry), + Type.Gmp => Gmp.Entry.Equals(other.Gmp.Entry), + Type.Eqdp => Eqdp.Entry.Equals(other.Eqdp.Entry), + Type.Est => Est.Entry.Equals(other.Est.Entry), + Type.Rsp => Rsp.Entry.Equals(other.Rsp.Entry), + Type.Imc => Imc.Entry.Equals(other.Imc.Entry), _ => throw new ArgumentOutOfRangeException(), }; } - public bool Equals( MetaManipulation other ) + public bool Equals(MetaManipulation other) { - if( ManipulationType != other.ManipulationType ) - { + if (ManipulationType != other.ManipulationType) return false; - } return ManipulationType switch { - Type.Eqp => Eqp.Equals( other.Eqp ), - Type.Gmp => Gmp.Equals( other.Gmp ), - Type.Eqdp => Eqdp.Equals( other.Eqdp ), - Type.Est => Est.Equals( other.Est ), - Type.Rsp => Rsp.Equals( other.Rsp ), - Type.Imc => Imc.Equals( other.Imc ), + Type.Eqp => Eqp.Equals(other.Eqp), + Type.Gmp => Gmp.Equals(other.Gmp), + Type.Eqdp => Eqdp.Equals(other.Eqdp), + Type.Est => Est.Equals(other.Est), + Type.Rsp => Rsp.Equals(other.Rsp), + Type.Imc => Imc.Equals(other.Imc), _ => false, }; } - public MetaManipulation WithEntryOf( MetaManipulation other ) + public MetaManipulation WithEntryOf(MetaManipulation other) { - if( ManipulationType != other.ManipulationType ) - { + if (ManipulationType != other.ManipulationType) return this; - } return ManipulationType switch { - Type.Eqp => Eqp.Copy( other.Eqp.Entry ), - Type.Gmp => Gmp.Copy( other.Gmp.Entry ), - Type.Eqdp => Eqdp.Copy( other.Eqdp ), - Type.Est => Est.Copy( other.Est.Entry ), - Type.Rsp => Rsp.Copy( other.Rsp.Entry ), - Type.Imc => Imc.Copy( other.Imc.Entry ), + Type.Eqp => Eqp.Copy(other.Eqp.Entry), + Type.Gmp => Gmp.Copy(other.Gmp.Entry), + Type.Eqdp => Eqdp.Copy(other.Eqdp), + Type.Est => Est.Copy(other.Est.Entry), + Type.Rsp => Rsp.Copy(other.Rsp.Entry), + Type.Imc => Imc.Copy(other.Imc.Entry), _ => throw new ArgumentOutOfRangeException(), }; } - public override bool Equals( object? obj ) - => obj is MetaManipulation other && Equals( other ); + public override bool Equals(object? obj) + => obj is MetaManipulation other && Equals(other); public override int GetHashCode() => ManipulationType switch @@ -232,11 +240,11 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa _ => 0, }; - public unsafe int CompareTo( MetaManipulation other ) + public unsafe int CompareTo(MetaManipulation other) { - fixed( MetaManipulation* lhs = &this ) + fixed (MetaManipulation* lhs = &this) { - return MemoryUtility.MemCmpUnchecked( lhs, &other, sizeof( MetaManipulation ) ); + return MemoryUtility.MemCmpUnchecked(lhs, &other, sizeof(MetaManipulation)); } } @@ -255,12 +263,13 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa public string EntryToString() => ManipulationType switch { - Type.Imc => $"{Imc.Entry.DecalId}-{Imc.Entry.MaterialId}-{Imc.Entry.VfxId}-{Imc.Entry.SoundId}-{Imc.Entry.MaterialAnimationId}-{Imc.Entry.AttributeMask}", - Type.Eqdp => $"{( ushort )Eqdp.Entry:X}", - Type.Eqp => $"{( ulong )Eqp.Entry:X}", + Type.Imc => + $"{Imc.Entry.DecalId}-{Imc.Entry.MaterialId}-{Imc.Entry.VfxId}-{Imc.Entry.SoundId}-{Imc.Entry.MaterialAnimationId}-{Imc.Entry.AttributeMask}", + Type.Eqdp => $"{(ushort)Eqdp.Entry:X}", + Type.Eqp => $"{(ulong)Eqp.Entry:X}", Type.Est => $"{Est.Entry}", Type.Gmp => $"{Gmp.Entry.Value}", Type.Rsp => $"{Rsp.Entry}", _ => string.Empty, }; -} \ No newline at end of file +} diff --git a/Penumbra/Meta/Manipulations/RspManipulation.cs b/Penumbra/Meta/Manipulations/RspManipulation.cs index 397f80dc..734488ae 100644 --- a/Penumbra/Meta/Manipulations/RspManipulation.cs +++ b/Penumbra/Meta/Manipulations/RspManipulation.cs @@ -8,59 +8,69 @@ using Penumbra.Meta.Files; namespace Penumbra.Meta.Manipulations; -[StructLayout( LayoutKind.Sequential, Pack = 1 )] -public readonly struct RspManipulation : IMetaManipulation< RspManipulation > +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public readonly struct RspManipulation : IMetaManipulation { public float Entry { get; private init; } - [JsonConverter( typeof( StringEnumConverter ) )] + [JsonConverter(typeof(StringEnumConverter))] public SubRace SubRace { get; private init; } - [JsonConverter( typeof( StringEnumConverter ) )] + [JsonConverter(typeof(StringEnumConverter))] public RspAttribute Attribute { get; private init; } [JsonConstructor] - public RspManipulation( SubRace subRace, RspAttribute attribute, float entry ) + public RspManipulation(SubRace subRace, RspAttribute attribute, float entry) { Entry = entry; SubRace = subRace; Attribute = attribute; } - public RspManipulation Copy( float entry ) + public RspManipulation Copy(float entry) => new(SubRace, Attribute, entry); public override string ToString() => $"Rsp - {SubRace.ToName()} - {Attribute.ToFullString()}"; - public bool Equals( RspManipulation other ) - => SubRace == other.SubRace + public bool Equals(RspManipulation other) + => SubRace == other.SubRace && Attribute == other.Attribute; - public override bool Equals( object? obj ) - => obj is RspManipulation other && Equals( other ); + public override bool Equals(object? obj) + => obj is RspManipulation other && Equals(other); public override int GetHashCode() - => HashCode.Combine( ( int )SubRace, ( int )Attribute ); + => HashCode.Combine((int)SubRace, (int)Attribute); - public int CompareTo( RspManipulation other ) + public int CompareTo(RspManipulation other) { - var s = SubRace.CompareTo( other.SubRace ); - return s != 0 ? s : Attribute.CompareTo( other.Attribute ); + var s = SubRace.CompareTo(other.SubRace); + return s != 0 ? s : Attribute.CompareTo(other.Attribute); } public MetaIndex FileIndex() => MetaIndex.HumanCmp; - public bool Apply( CmpFile file ) + public bool Apply(CmpFile file) { - var value = file[ SubRace, Attribute ]; - if( value == Entry ) - { + var value = file[SubRace, Attribute]; + if (value == Entry) return false; - } - file[ SubRace, Attribute ] = Entry; + file[SubRace, Attribute] = Entry; return true; } -} \ No newline at end of file + + public bool Validate() + { + if (SubRace is SubRace.Unknown || !Enum.IsDefined(SubRace)) + return false; + if (!Enum.IsDefined(Attribute)) + return false; + if (Entry is <= 1e-2f or > 8f) + return false; + + return true; + } +} diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs index 89b3d5bf..24e9aafe 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SubMod.cs @@ -89,7 +89,7 @@ public sealed class SubMod : ISubMod var manips = json[nameof(Manipulations)]; if (manips != null) foreach (var s in manips.Children().Select(c => c.ToObject()) - .Where(m => m.ManipulationType != MetaManipulation.Type.Unknown)) + .Where(m => m.Validate())) ManipulationData.Add(s); }